Dependency Injection in Domain Driven Design – Applying Composition Root

dependency-injectiondesigndomain-driven-designobject-oriented

I'm confused about applying the "Composition Root" (CR) to create aggregates in DDD. Seemann (2012) defines CR as a "(preferably) unique location … where modules are composed". He argues for composing object graphs inside CR, near the application's entry point, and warns against the temptation to compose "classes a little at a time to create small subsystems".

An aggregate in DDD appears like such a subsystem — a (sub) graph with entities and value objects. Evans (2004) and other DDD texts (Vernon 2013, Ghosh 2017) recommend constructing aggregates inside factories, located in the domain layer, "near" the aggregate (eg a factory method on the aggregate root) or as standalone services. This seems to contradict
the CR approach.

The question is whether and how the CR and Factory approaches should be combined in DDD. For instance,

  • A CR is put inside the domain layer, rather than at the app's entry. Possibly, the factories are pooled to this single location

    OR
  • The benefits of CR (eg, the "ability to intercept subsystems to modify their behavior"; Seemann) are irrelevant to DDD.
  • CR DI does not apply to creation of aggregates but works at a higher granularity level (eg injection of services). Then CR methods use factories to construct aggregates, to place them on the object graph.

References
M. Seemann, 2012, "Dependency Injection in .NET"
E. Evans, 2004, "Domain-Driven Design"
V. Vernon, 2013, "Implementing Domain-Driven Design"
D. Ghosh, 2017, "Functional and Reactive Domain Modeling"

Best Answer

Typically, the composition root is about satisfying the dependencies of various modules that make up a process/application.

When Evans described DDD in the blue book, he was working in the context of a three tier architecture. You can think of the tiers as being modules - an application module, which knows about the interface(s) of the domain module but not the implementations, and a domain module that knows the interface of the persistence module, but not its implementation.

Because the coupling between modules is interface, rather than implementation, we can replace one module with another that implements the same interface, and it should all "just work".

The role of the composition root is the selection of implementations, and the wiring together of the dependency graph. So the composition root knows enough about the specific implementations to instantiate them, passing the appropriate implementation of each.

In architectures where constructor injection is favored, this might look like

PersistenceAPI persistence = new PersistenceModule(...)
DomainAPI domain = new DomainModule(persistence, ...)
Application app = new Application(domain, ...)

app.run()

Broadly, the modules describe their dependencies -- capabilities that they need but don't implement for themselves, and the composition root does the work of introducing capability consumers with capability producers.

An important thing to notice here; the pattern is deferring the binding of the consumer to a specific capability provider -- instead of forcing a choice when we build a module, we delay that choice until we build the root (or, with some additional work/assistance from DI frameworks, we can defer the choice until run time).

So for DDD, the answer is relatively simple: you use the composition root approach in those parts of your solution where you derive business value from deferring the binding of a consumer to a specific implementation.

Example: one way of persisting domain state is by writing data into an RDMBS, possibly with the help of an ORM. Another way would be to serialize that state into a document written to a document store. That might be a raw serialization of the object, or it might instead be in the form of a message/memento which is less coupled to a specific in memory representation.

Most of the app shouldn't need to care which of these choices that you make; so there can be business value in creating a seam that allows you to change this decision.

The composition root pattern gives you an element where the single responsibility is stitching these seams together into a working application.