-1

I’ve been reading about DI and the composition root. I’ve read in the article that only the application should have a composition root, not the libraries.

But let’s assume i have a reusable package with some interfaces and their implementation. I would like to bind that interface to the implementation. I think it would be cumbersome if the user has to do all this themselves.

Would it make sense to include an XML DI configuration file in the reusable module, which would be consumed and processed in the composition root?

  • 1
    You are likely referring to the article [Understanding the Composition Root](https://freecontent.manning.com/dependency-injection-in-net-2nd-edition-understanding-the-composition-root/) which is an excerpt from [Dependency Injection Principles, Practices, and Patterns](https://mng.bz/BYNl). – Steven Jan 15 '20 at 10:01
  • 3
    That same book has a [complete section](https://livebook.manning.com/book/dependency-injection-principles-practices-patterns/chapter-12/121) on working with configuration files and it states that "Experience with XML as a configuration mechanism [...] revealed that this is rarely the best option. XML tends to be verbose and brittle." You should certainly read that section. – Steven Jan 15 '20 at 10:05
  • @Steven Thanks for the reply! I will certainly read that. But let’s assume i use code based configuration instead of XML. Would it still make sense to put those code based config files in their respective module? And process those config files in the application? –  Jan 15 '20 at 14:54
  • 1
    See [DI-Friendly Library](https://blog.ploeh.dk/2014/05/19/di-friendly-library/) – Steven Jan 15 '20 at 15:10

2 Answers2

2

Although class libraries should not contain composition roots, you can always include a factory in your library that creates a default graph for simple use cases. Your types in the library will still be public so that advanced users can compose the types in a custom way (e.g. decorate some types with their special decorator). The factory you include can also be parameterized to support multiple basic use cases.

Regarding XML configuration, although it works, maintaining an application that uses XML for DI configuration is very hard in most cases because once a type has been renamed in the code, the type name in the XML will not be renamed automatically.

Yacoub Massad
  • 27,509
  • 2
  • 36
  • 62
  • Thanks for the reply! But i would like to avoid manually registering factories or other kinds of classes. I’m trying to write a solution which takes the code based config file with the interfaces and their instances configured and automatically process them in the composition root. Do you consider that a good idea? –  Jan 15 '20 at 14:59
  • @ArctiqJens, I don't use a DI container, and the factory I told you about is a simple class (or a static method) that creates individual types from your library and composes them together. Take a look at the concept of [Pure DI](https://blog.ploeh.dk/2014/06/10/pure-di/). I don't like the idea of a config file with interfaces and corresponding implementation types for the reason I told you about before. Code refactoring tools do not work with such config files. – Yacoub Massad Jan 15 '20 at 16:32
0

But let’s assume i have a reusable package with some interfaces and their implementation. I would like to bind that interface to the implementation.

Why does your reusable package provide an interface and an implementation?

You can provide concrete classes if your reusable package is a library. As I write in DI-Friendly Framework:

A Library is a reusable set of types or functions you can use from a wide variety of applications. The application code initiates communication with the library and invokes it.

According to the Dependency Inversion Principle (DIP), the client defines the abstract interface of its dependencies. Any interface provided by the library would violate the DIP.

On the other hand, your reusable package can provide an abstraction (interface or base class) if you expect client code to supply an implementation. In that case, the package begins to look more like a framework, although there's probably a gray area there. Usually, in such cases, the package doesn't have to supply any implementations of the interface, or it can supply some for optional use.

There's probably some edge cases where both an interface and a (default) implementation shipping with the same package might make sense, but I don't see how it warrants more than the sort of default factory that Yacoub Massad recommends. You could make that API a Fluent Builder - that's a common pattern for that sort of scenario.

You can supply all the XML configuration files you wish if you don't want anyone to use your package.

Mark Seemann
  • 225,310
  • 48
  • 427
  • 736
  • Thanks for the reply. I've read a lot of your articles in the past few days. When I wrote this question, my intention was to create a few packages with a few repository interfaces and a default implementation for those interfaces (A UsersRepository for example), which I could share among some projects. But if I understand correctly, it wouldn't make sense to provide a concrete implementation of my repository interface, but rather only create the interface and make the concrete implementation in my application code? –  Jan 17 '20 at 22:20
  • @ArctiqJens The other way around. You can ship database implementations in a reusable library. I could see how that would be useful. If you, however, also ship an interface, then the client code would have to reference your library in order to be able to program against that interface. Client code would now depend on a concrete implementation. DIP suggests that it should be the other way around. *High-end modules should not depend on low-end modules. Both should depend on abstractions.* – Mark Seemann Jan 18 '20 at 08:15