Skip to content
This repository was archived by the owner on Dec 11, 2024. It is now read-only.

Application Lifecycle

Marc Wittke edited this page Mar 17, 2019 · 11 revisions

Assumptions

You have a custom application class, that derives either from BackendFxApplication or more specifically from BackendFxDbApplication. The latter implies, that there is a kind of database managed by the application itself. The instance of this class is considered as application wide singleton.

Application Creation

First of all, you have to create the singleton instance of your custom application class. There are some dependencies, that are considered as application wide singletons by design:

  • a ICompositionRoot: The starting point for the application to create an injected object graph. You can read more about it in Mark Seemann's excellent article. In the package Backend.Fx.SimpleInjectorDependencyInjection you will find a useful SimpleInjectorCompositionRoot. However, you are free to bring your own dependency injection framework.

  • a ITenantIdService: Sometimes, the application needs to know the active tenant ids (e.g. to run a job for all tenants). It will use this instance to retrieve the relevant ids.

  • a IDatabaseBootstrapper (applies to BackendFxDbApplication only): as the name says, the responability of this instance is to create and/or migrate the owned database on application start.

Application Boot

You simply boot the application by calling the Boot() method. This will trigger the following actions:

  • The async virtual method OnBoot is being called. Provide an implementation that ensures whatever is externally required by your application
    • In case of a BackendFxDbApplication this method is already implemented:
      • The async virtual method OnDatabaseBoot() is being called. This marks an optional extension point to do something before the database is used.
      • WaitForDatabase() is called. Implement a busy wait for a dependent database system according to your needs. This comes handy, when you are using docker-compose and your database container is not guaranteed to be ready before your application start.
      • DatabaseBootstrapper.EnsureDatabaseExistence() is called. The implementation is expected to ensure the existence of a database containing the schema required by your application to work
      • The async virtual method OnDatabaseBooted() is being called. Provide an implementation that ensures whatever is externally required by your application
  • the composition root is verified. Note that in case of Simple Injector this marks the switch from construction of the container to usage of the container.
  • The async virtual method OnBooted is being called. Your extension point to do something after booting the application.
    • this is a good point to call the RegisterSeedActionForNewlyCreatedTenants() and/or SeedDataForAllActiveTenants() method to enable automatic data seeding on startup and/or tenant creation.
  • an internal ManalResetEvent is set. Other threads that has been queued to wait for this event by calling WaitForBoot(int timeoutMilliSeconds = int.MaxValue) are now allowed to proceed.

Disposing

When the custom application class instance is disposed, all disposable instances are disposed as well:

  • the ICompositionRoot. This will result in disposal of all singleton instances created by the composition root itself.
  • the IDatabaseBootstrapper (in case of BackendFxDbApplication)

You are encouraged to implement Dispose(bool disposing) in your custom application class, if there are more instances that require a clean shutdown.

Some words regarding your application's Composition Root

As general rule, your application logic should never depend on the composition root. By doing so, you'd create classes that are shamelessly lying about their dependencies. You might have heard of the Service Locator Anti Pattern.

Also don't use the composition root in your IDatabaseBootstrapper. Follow the principle "separate use from construction", well imagined as "You don't build and use a hotel at the same time".

The only acceptable components allowed to use the composition root are as follows:

  • OLTP entry points, e.g. a middleware in case of ASP.Net Core or a command handler.
  • Background jobs, please see IJobEngine
  • handling of integration events, technically the same thing like a background job.
Clone this wiki locally