Before, read my other post explaining why this subject is so important.
The idea around the Composition Root pattern is to create an assembly exclusively to register all the dependencies, the architecture will look like this:
The CompositionRoot.dll references all the other projects, except the View (see the red arrow). Note that the View.dll (which is the startup point of our system) is the only one who references the CompositionRoot.dll.
To register the dependencies, the View.dll should call a method form the the CompositionRoot.dll. When the View.dll need to resolve a dependency, it calls the CompositionRoot.dll again.
The CompositionRoot.dll is the only one responsible of register and resolve dependencies. So, here are the examples:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public static void RegisterDependencies() { // Registrations for the Data.dll container.Register<BlogRepo>(); container.Register<PostRepo>(); container.Register<CommentRepo>(); // Registrations for the Bussiness.dll container.Register<BlogBusinessRules>(); container.Register<PostBusinessRules>(); container.Register<CommentBusinessRules>(); // And so on } |
Then, the clients of this CompositionRoot will need to call the RegisterDependencies() method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
namespace View { class Program { static void Main(string[] args) // The entry point of the application { // The method who register "ALL" dependencies. CompositionRoot.CompositionRoot.RegisterDependencies(); // The dependencies of the View still should be registered in the CompositionRoot. // To do so ,we need to implement convenient methods in the CompositionRoot class, like the Register<T>() below. CompositionRoot.CompositionRoot.Register<BlogAdministrationScreen>(); } } } |
Note the line 12: To register the dependencies of the View.dll I provided some public convenient methods in the CompositionRoot class to do so. In that way, the container is created and managed by the Composition Root only. The View.dll does not even need to reference the DI Container Library. If you curious, the register method of the Composition Root is really simple:
1 2 3 4 |
public static void Register<T>() where T : class { container.Register<T>(); } |
You can add more “convenience” methods if you want, I developed some in my testing project.
Pros
- Dependencies are managed in just only one place.
- The dependency graph becomes simpler and flatter.
- Avoid the use of the Service Locator anti-pattern because none of the projects, except the View.dll, can ask the CompositionRoot.dll to resolve a dependency (otherwise it will cause a circular dependency).
Cons
- To integrate with Asp.Net the CompositionRoot.dll needs to reference some asp.net infrastructure dlls to be able to offer methods that provide that integration. See the public static void ConfigureServices(IServiceCollection services) method in here
This is not even a so negative point because you can still do this integration in the ASP.NET project (View.dll), however, for the sake of separation of responsibilities, I prefer to do this in CompositionRoot.dll
Texto normal texto texto n
Check my Github for a complete project that shows the technique explained above.
REFERENCES:
https://stackoverflow.com/a/9503612/256925
https://stackoverflow.com/a/19121286/256925
https://medium.com/@benjamintodts/keep-your-asp-net-core-startup-project-clean-88e66aadf0cf
https://medium.com/volosoft/asp-net-core-dependency-injection-best-practices-tips-tricks-c6e9c67f9d96
https://stackoverflow.com/a/5272874/256925