Skip to content

Template Method

The Template Method pattern defines the skeleton of an algorithm in a base class, letting subclasses redefine certain steps without changing the overall structure. The base class calls methods in a predefined order; subclasses override the ones relevant to them.

classDiagram
    class GranitModule {
        +ConfigureServices(context)*
        +ConfigureServicesAsync(context)
        +OnApplicationInitialization(context)*
        +OnApplicationInitializationAsync(context)
    }

    class GranitWolverineModule {
        +ConfigureServices(context)
    }

    class GranitFeaturesModule {
        +ConfigureServices(context)
    }

    class GranitValidator {
        #CascadeMode = Continue
    }

    class AbstractValidator {
        FluentValidation
    }

    GranitModule <|-- GranitWolverineModule
    GranitModule <|-- GranitFeaturesModule
    AbstractValidator <|-- GranitValidator
Base classFileHooks
GranitModulesrc/Granit.Core/Modularity/GranitModule.csConfigureServices(), ConfigureServicesAsync(), OnApplicationInitialization(), OnApplicationInitializationAsync()
GranitValidator<T>src/Granit.Validation/GranitValidator.csInherits from AbstractValidator<T> with CascadeMode.Continue by default

Custom variant — Dual Sync/Async: ConfigureServicesAsync() delegates to ConfigureServices() by default. A module can override only the sync version or only the async version — no obligation to implement both.

The module lifecycle (discovery -> configuration -> initialization) is fixed. Only the content of each step varies between modules. The Template Method guarantees that the order is always respected.

[DependsOn(typeof(GranitPersistenceModule))]
public sealed class MyAppHostModule : GranitModule
{
// Override only the necessary steps
public override void ConfigureServices(ServiceConfigurationContext context)
{
context.Services.AddScoped<IPatientService, PatientService>();
}
public override void OnApplicationInitialization(ApplicationInitializationContext context)
{
WebApplication app = context.GetApplicationBuilder();
app.MapControllers();
}
// ConfigureServicesAsync() and OnApplicationInitializationAsync()
// are not overridden -- they delegate to the sync versions above
}