-
Notifications
You must be signed in to change notification settings - Fork 9
Application Lifetime Management
The application manifest is a shared application service providing information about the application being currently executed.
- AppId: provides the application ID. This information could be used to identify the application within a microservices federation.
- AppVersion: provides the application version.
- AppInstanceId: this identifier is provided to uniquely identify an instance of the application across the distributed environment. This can be potentially used in message exchanging.
- Features: provides a list of information data about the features currently available in the application.
By default, there is no application manifest service provided by the framework, because this is a service which needs to be provided by the most specific application layer. However, the AppManifestBase class is provided as a convenience base for inferring the ID and the version from the main application assembly.
- Service contract: IAppManifest
- Instancing: singleton (shared)
- Mode: single
Remarks: the application manifest is also an expando, so it is possible to add dynamic properties to it at runtime.
Make sure to add an
AppManifestclass in your main application assembly. The following example code is necessary and, at the same time, sufficient:
/// <summary>
/// Provides the application manifest.
/// </summary>
/// <remarks>
/// The information is retrieved from the assembly information.
/// </remarks>
public class AppManifest : AppManifestBase
{
}Kephas provides a shared application service considered the application's entry point, called IAppManager. Its main functionalities consist of initializing and finalizing the application.
-
InitializeAppAsync(appContext: IAppContext, [cancellationToken: CancellationToken]): Task: called upon application startup to initialize it. The context parameter is used to pass contextual information required in the initialization process. -
FinalizeAppAsync(appContext: IAppContext, [cancellationToken: CancellationToken]): Task: called upon application termination to finalize it. -
AppManifest: IAppManifest: Property providing the application manifest. This is another way of retrieving the application manifest.
Typically, implementations of the application manager service will aggregate feature manager services, which in turn will start initialization of different application features. A default implementation is already provided by Kephas, so there is not really needed to come up with a custom implementation.
- Service contract: IAppManager
- Instancing: singleton (shared)
- Mode: single
- Default implementation: DefaultAppManager
- Upon initialization, it sets the features list in the application manifest, so that the features being initialized know what features are available. However, if a manager of an optional feature throws an exception during initialization, the associated feature is removed from the available ones (in the application manifest), so that the other components and services know that the feature is not available anymore.
Caution: even if an optional feature fails to initialize and is removed from the list of available features, its manager will still be invoked in the finalization phase. This is to ensure that the application cleans up everything it needs to. Therefore, it is the responsibility of that feature manager to handle the partially initialized resources.
- The features are invoked in the order given by the dependency chain and processing priority, that is a dependency is invoked before the dependent feature.
Caution: If a dependent feature is marked with a processing priority higher than one of its dependencies, a circular virtual dependency occurs and the application initialization is interrupted.
Features are functional characteristics of the application, autonomous but usually interconnected. A feature declares:
- a unique name used for identification.
- a version (optional). If not provided, version 0.0.0.0 is considered.
- a list of dependencies (optional), which are other features required by the declaring feature to work properly.
Example of features could be database or web server.
Feature managers are shared application services used to initialize particular features during the application startup or to finalize them during the application finalization.
- Service contract: IFeatureManager
- Instancing: singleton (shared)
- Mode: multiple
- Metadata attributes: [FeatureInfo]
A feature manager provides the following members:
-
InitializeAsync(appContext: IAppContext, [cancellationToken: CancellationToken]): Task: Performs initialization tasks within the application feature boundary. -
FinalizeAsync(appContext: IAppContext, [cancellationToken: CancellationToken]): Task: Performs finalization tasks within the application feature boundary.
By convention, feature managers class names end with
FeatureManager.
For convenience, Kephas provides the base class
FeatureManagerBasefor feature managers.
With defining a feature manager the feature metadata may also be defined. This is done by annotating the feature manager service with the [FeatureInfo] attribute.
[ProcessingPriority(Priority.AboveNormal)]
[FeatureInfo("Web", version: "1.2.0.0", dependencies: new[] { "Database" })]
public class WebHostFeatureManager : FeatureManagerBase
{
// This feature is called "Web" and it depends on the "Database" feature.
//...
}The following values may be set:
- name: indicate the feature name (required).
- version: indicate the feature version (optional).
- dependencies: an array of names of the features upon which the annotated feature is dependent.
If no [FeatureInfo] attribute is declared:
- the feature name is considered to be the name of the service class without the FeatureManager or Manager ending.
- the feature has no dependencies.
As expected, the feature managers are invoked in their dependency order, however combined with the priority order. This means that the following rules are applied in this very order:
- if feature A depends on feature B, the feature manager for B is called before the one for A during initialization, and the one for A before the one for B during finalization.
- if no dependency between A and B can be inferred, neither direct nor indirect, then the defined priority order will provide the invocation order.
The life cycle behaviors are applied before/after initialization and before/after finalization of the application or of the feature. They can be used to do stuff like logging, license proofing, auditing, or whatever the application may consider necessary.
The behavior interceptors receive as parameter the application context. By default it contains the application manifest and the ambient services.
-
BeforeAppInitializeAsync(appContext: IAppContext, [cancellationToken: CancellationToken]): Task: Interceptor called before the application starts its asynchronous initialization. -
AfterAppInitializeAsync(appContext: IAppContext, [cancellationToken: CancellationToken]): Task: Interceptor called after the application started its asynchronous initialization.
Note: To interrupt the application initialization, simply throw an appropriate exception.
-
BeforeAppFinalizeAsync(appContext: IAppContext, [cancellationToken: CancellationToken]): Task: Interceptor called before the application starts its asynchronous finalization. -
AfterAppFinalizeAsync(appContext: IAppContext, [cancellationToken: CancellationToken]): Task: Interceptor called after the application completed its asynchronous finalization.
Note: To interrupt the application finalization, simply throw an appropriate exception. Caution! Interrupting the finalization by throwing exceptions may cause the application to remain in an undefined state.
The behavior interceptors receive as parameters the application context and the feature manager metadata, containing the feature name and dependencies.
-
BeforeInitializeAsync(appContext: IAppContext, serviceMetadata: FeatureManagerMetadata, [cancellationToken: CancellationToken]): Task: Interceptor called before the feature starts its asynchronous initialization. -
AfterInitializeAsync(appContext: IAppContext, serviceMetadata: FeatureManagerMetadata, [cancellationToken: CancellationToken]): Task: Interceptor called after the feature started its asynchronous initialization.
Note: To interrupt the feature initialization, simply throw an appropriate exception.
-
BeforeFinalizeAsync(appContext: IAppContext, serviceMetadata: FeatureManagerMetadata, [cancellationToken: CancellationToken]): Task: Interceptor called before the feature starts its asynchronous finalization. -
AfterFinalizeAsync(appContext: IAppContext, serviceMetadata: FeatureManagerMetadata, [cancellationToken: CancellationToken]): Task: Interceptor called after the feature completed its asynchronous finalization.
Caution! Interrupting the finalization by throwing exceptions may cause the application to remain in an undefined state.