Divide and Conquer or How to achieve bottom-up behavior in code
Hey everyone. I’d like to share here some of my experience and approach as to code structure and different ways of organizing the code flow during app bootstrapping process. My experience lies mostly in .NET/C# and Node/Typescript world but the principles should be easily applicable elsewhere provided the infrastructure is in place.
Let’s concentrate on one specific case which is the .NET 6 apps (console, web, desktop — doesn’t really matter). One of the most common issues about organizing the app bootstrapping code is that it could become somewhat cluttered even in case of microservices and thus hard to follow and maintain. The issue becomes even more complex as the number of microservices grows and they start to reuse some of the functionality via common modules/packages. Moreover, imagine you have numerous implementations of the same interface and you’d like to juggle between them easily and persist your changes without introducing too much source control noise. If any of the issues sounds familiar, let’s move on :)
The idea here is very simple — keep functional blocks (modules )small with high degree of encapsulation and cohesion (preserving the SRP) and make them as composable and extensible as possible. On top of this, encapsulate the desired bootstrapping flow inside a single entity to increase SRP even further. This leads us to the concept of Bootstrapper.
As you can see here this tiny piece of code actually defines few things:
1. Extensibility — the core functionality can be extended in any way by using middlewares (which are merely fluent extension methods invoked in lazy manner)
2. IoC/DI awareness — the focal point for registration of required services to be injected during app’s lifecycle
3. Composition and Modularity — this functionality is provided by the Solid.Bootstrapping.BootstrapperBase class. If you’re interested in reading more about the way it works please refer to https://godrose.gitbook.io/solid-net-framework/bootstrapping
Using this approach actually does all the heavy lifting of discovery and composition of app’s modules, dependencies/services registration and much more. See the example below:
By using the composable and extensible bootstrapper approach we can achieve very static code of the bootstrapper itself thus preserving the OCP while putting all dynamic parts of code (services registration, etc.) into the dedicated classes (composition modules).
Employing this approach will allow true bottom-up architecture where all assemblies will register their internal dependencies without the knowledge leak into the startup’s one, allowing for more concise and maintainable code.
Hope you enjoyed this article. If you got any questions or comments feel free to contact me in the comments section of via mail (firstname.lastname@example.org) or on GitHub (godrose)