Can DDD be Adequately Implemented Without DI and AOP?
Domain Driven Design (DDD) is about mapping business domain concepts into software artifacts. The core ingredient of DDD implementation recipe has been Object Oriented Programming (OOP) where the objects represent the actual entities in the business domain. Domain objects are designed using Plain Java Classes and Interfaces taking the advantage of OOP concepts like Inheritance, Encapsulation, and Polymorphism.
In a typical Unit Of Work, domain objects need to collaborate with other objects whether they are Services, Repositories or Factories. Domain objects also need to manage other concerns such as domain state change tracking, auditing, caching, transaction management (including transaction retry) which are cross-cutting in nature. These are reusable non-domain related concerns that typically tend to be scattered and duplicated all over the code including domain layer. Embedding this logic in the domain code leads to tangling and cluttering of domain layer with non-domain related code.
When it comes to managing the code dependencies without tight-coupling between objects and isolating cross-cutting concerns, OOP alone cannot provide an elegant design solution for domain driven design and development. This is where design concepts like Dependency Injection (DI) and Aspect Oriented Programming (AOP) come into the picture. These concepts can be used to complement OOP to minimize tight coupling, enhance modularity and better manage the cross-cutting concerns.
This was the main discussion point in a recent thread in DomainDrivenDesign user group forum. The discussion is based on the presentation on DDD topic by Ramnivas Laddad where he made the assertion that DDD cannot be adequately implemented without help of AOP and DI. In the presentation, Ramnivas talked about the concept of fine grained DI using AOP to make domain objects regain smart behavior. He mentioned that domain objects need access to other fine grained objects to provide rich behavior and a solution to this is to inject Services, Factories, or Repositories into Domain Objects (by using Aspects to inject dependency at constructor or setter invocation time).
Managing the dependencies between domain objects (for example, the dependency between an Entity and its Repository) is a classic problem that DDD developers often run into. The usual design solution to this problem is to have the Service or Facade class call the Repository directly and when invoked the Repository would return the Entity object to the client. This design eventually leads to a Fat Service Layer and an Anemic Domain Model where facade classes start accumulating more and more business logic and domain objects become mere data carriers with getters and setters.
InfoQ spoke with Eric Evans, the author of Domain Driven Design book and Ramnivas Laddad about the role of DI and AOP in the implementation of DDD. When asked about the assertion that DDD cannot be implemented without AOP and DI, Ramnivas said that the core premise for that assertion is that if a Domain Object needs to be smarter, it needs to collaborate with other objects such as Repositories and Services.
The question becomes how can a domain object get those objects? DI seems like the right thing to do. That is where AOP comes into picture and allows extending Spring-like DI (restricted to objects declared in the application context) to any object regardless of how it is created. Of course, AOP helps with any domain level cross-cutting concerns (where the basic premise is that domain concepts should be mapped to software artifacts as closely as possible).
Eric talked about how OOP, DI, and AOP concepts fit into DDD implementation:
Although DDD can be done with any paradigm that allows abstraction and expression of model concepts, in practice it has almost always been OOP. So, OOP has been a foundation of the practice of DDD.
More recently, DI has become a very widespread technique and, when used well, has helped practitioners of DDD to improve their designs. Most importantly, in my view, DI helps in the "isolation" of the domain.
The potential of AOP is just beginning to be realized. It seems that it has potential to declutter the domain layer. This contributes to the clear expression of the model concepts, and perhaps to isolation. But, unlike the first two (OOP and DI), it isn't a tried and true part of a "foundation" yet.
These form a foundation of a current best practice architecture that supports DDD.
He also emphasized that it is important not to blur the lines between the philosophy of design (DDD) and the technical tool box that helps us fulfill it (OOP, DI, and perhaps AOP).
The recent trend to define and manage Aspects and DI has been to use Annotations. @Configurable is Spring way of injecting the Repository and Services into a Domain Object. InfoQ asked Eric about the role of Annotations in a DDD project. He said:
In general, anything attached to an object representing a domain concept, including an annotation, should say something conceptually meaningful for that object. @Configurable is technical, not domain, so I wouldn't like this kind of annotation on domain objects. On the other hand, for a repository, I usually have an interface and an implementation. The interface I think of as the domain part, which I keep as clean and conceptual as is practical. But the implementation almost always must reference technology. (Typically DAO's, for example). Putting @Configurable on the implementation of a repository seems completely reasonable.
Spring framework extends the "Domain Object DI" idea beyond the @Configurable annotation. Ramnivas recently wrote a blog entry about the latest improvements in upcoming Spring 2.2.5 release (available starting with project snapshot build 379). There are three new aspects (AnnotationBeanConfigurerAspect, AbstractInterfaceDrivenDependencyInjectionAspect, and AbstractDependencyInjectionAspect) to provide simple and more flexible options for Domain Object DI. Ramnivas said the main reason behind introducing the middle aspect (AbstractInterfaceDrivenDependencyInjectionAspect) is to allow domain-specific annotations and interfaces to play a role. He also said that @Configurable is simple but perhaps not the most elegant choice.
There has also been discussion on the orchestrated business services (that have multiple steps in a single unit of work) and domain object lifecycle. Responding to a question on how BPEL impacts the domain object lifecycle and interaction between the different domain objects, Eric said:
Orchestration versus lifecycle is a tricky one. Orchestration can be a great way to take decoupled pieces and make them work together while representing workflow somewhat declaratively. On the other hand, in cases where domain entities have important lifecycles that are central to the model, taking the responsibility for that up into the orchestration layer has the effect of gutting the domain objects and leaving an anemic model. There is no cookbook answer to this one. It is case-by-case evaluation of a good modeler/designer.
Can DDD be Adequately Implemented Without DI and AOP?
Re: Can DDD be Adequately Implemented Without DI and AOP?
@Configurable and Simplicity
"Depending on your specific needs and AspectJ knowledge, you will find one of them helpful towards creating an elegant solution."
Which to me means that you can use @Configurable depending on the use case and complexity.
AOP and IOC incredibly helpful, but not a prerequisite
I've led teams on two projects within the last year using domain driven design as one of our primary design and development tools. I found AOP and dependency injection to be essential in decluttering/partitioning the domain layer from other solution concerns, whether these concerns be technical/infrastructure, or business specific concerns that need to be used in multiple places.
A great book that discusses leveraging AOP using a theme approach can be found here
On a side note, although Eric is the expert, I'm not sure that I agree that all annotations on an business domain participant should say something conceptually meaningful about the object itself, at least not within a strict domain sense anyway. While "@configuration" is probably a bad annotation (exactly what kind of configuration does this mean?) to put on a method, having a nonbusiness specific annotation on a business method such as "@cachingOn expiry =10mins” is perfectly reasonable IMHO. Of course we can always put regular expressions around the aspects themselves to determine when they are called, but sometimes having an annotation is simpler, and a more practical solution. This might not leave us with a conceptually beautiful business domain participant, but the technology needs to come from somewhere.
my agile consulting blog
Caitie McCaffrey Apr 24, 2015