Bootstrapping

Motivation

Each and every app should start from somewhere. The .NET apps are of no exception. There could be any number of concerns/aspects each app takes into account when it starts. Some data should be initialized, some calls must be dispatched, some registration invoked and so on and so forth. However the initial patterns remain the same. In a nutshell app bootstrapping is very similar to the Builder pattern where the producer is configured and then the app is built and started.

Implementation

A bootstrapper could be anything because the associated app could do anything but typically the app would contain an Inversion-Of-Control container of some kind and involve some dynamic parts, so-called Composition Modules. Hence a typical bootstrapper would expose the associated functionality by implementing the correspondent interfaces via delegation to the existing aspects:

public class BootstrapperBase: 
                IInitializable, 
                IExtensible<BootstrapperBase>, 
                IHaveAspects<BootstrapperBase>,
                ICompositionModulesProvider, 
                IAssemblySourceProvider
{
    private ModularityAspect _modularityAspect;
    private DiscoveryAspect _discoveryAspect;
    private readonly ExtensibilityAspect<BootstrapperBase> _concreteExtensibilityAspect;
    private readonly AspectsWrapper _aspectsWrapper = new AspectsWrapper();
    
    public BootstrapperBase()
    {            
        _concreteExtensibilityAspect = new ExtensibilityAspect<BootstrapperBase>(this);
    }
    
    public IEnumerable<Assembly> Assemblies => _discoveryAspect.Assemblies;
    
    IEnumerable<ICompositionModule> ICompositionModulesProvider<ICompositionModule>.Modules 
    =>    _modularityAspect.Modules;
    
    public BootstrapperBase Use(IMiddleware<BootstrapperBase> middleware) 
    =>    _concreteExtensibilityAspect.Use(middleware);
    
    public virtual CompositionOptions CompositionOptions => new CompositionOptions();
    
    public void Initialize()        
    {            
        _aspectsWrapper.UseCoreAspects(CreateCoreAspects());
        _aspectsWrapper.Initialize();
    }
    
    private IEnumerable<IAspect> CreateCoreAspects()
    {            
        var aspects = new List<IAspect> { new PlatformAspect() };
        _discoveryAspect = new DiscoveryAspect(CompositionOptions);
        _modularityAspect = new ModularityAspect(_discoveryAspect, CompositionOptions);
        aspects.Add(_modularityAspect);
        aspects.Add(_discoveryAspect);
        aspects.Add(_concreteExtensibilityAspect);
        return aspects;        
    }
    
    public BootstrapperBase UseAspect(IAspect aspect)
    {            
        _aspectsWrapper.UseAspect(aspect);            
        return this;        
    }
}

As you can see the central part of this bootstrapper is the AspectsWrapper which actually contains all the functionality. This functionality can be invoked by locating the correspondent aspect either by type or by id. Both should be unique and well-defined. The presented implementation also oultines the recommended minimum of aspects for any custom bootstrapper.

Usage

You can use the presented bootstrapper in your app by simply inheriting from it or writing your own from scratch. Any option will work but don't forget to check the dependencies for aspects you're going to include into your bootstrapper. If you include the core aspects you'd only need to add custom extensibility aspects to your own bootstrapper. From there on the usage would be very simple:

public class Bootstrapper : BootstrapperBase
{
    public Bootstrapper(IServiceCollection dependencyRegistrator) 
    : base(dependencyRegistrator)
    {
            
 
    }
    
    //add some functionality if needed
}

var bootstrapper = new Bootstrapper(services);
bootstrapper.Use(new SomeMiddleware<Bootstrapper>());
bootstrapper.Initialize();

Last updated