Ramnivas Laddad, in this presentation during SpringOne, makes the assertion that "Domain Driven Design cannot be adequately implemented without help of AOP and DI".
I don't quite agree with this. I believe you can get by fine and do good DDD without using AOP.
According to the talk, AOP can be roughly used for the following purposes
- Dependency Injection in Domain entities and vo's
- Handling domain logic
- Handle Cross Cutting concerns
Dependency Injection without AOP
DI'ing services/policies/repositories into an Entity/VO can be done without using AOP in other simpler ways like
- Using callbacks methods like onLoad in Hibernate's Interceptor, which contains the injection logic based on the entity type. This is a slightly intrusive solution though.
A better way is to have interfaces for each service/repository that is to be injected and making the entity implement the required interfaces.
public interface OrderRepositoryAware {
public void setRepository(OrderRepository orderRepo);
}
public interface CreditRatingAware {
public void setCreditRatingSvc(CreditRatingSvc svc)
}
public class Order implements OrderRepositoryAware,CreditRatingAware { ... }
public class DependencyInjectingInterceptor extends EmptyInterceptor {
public boolean onLoad(Object entity,
Serializable id,
Object[] state,
String[] propertyNames,
Type[] types) {
if(entity instanceof OrderRepositoryAware) {
((OrderRepositoryAware) entity).setRepository(...);
}
......
}
However the same logic needs to be replicated in Factory objects which creates new Entities. This is the drawback of this issue. - Use a ServiceRegistry to get a reference to required service/repository etc. To unit test you just need to stub out ServiceRegistry.
public class Order {
public void validatePayments() {
ServiceRegistry.getRepository().getPaymentHistory();
}
}
The singleton initialization would have to change to create mocks when used in testing.
In his presentation Ramnivas takes the example of a PaymentAuthorization service failure.
- If PaymentAuthorization fails, try authorizing using other service providers.
- In case all providers fail, do a temp auth based on payment history. When product is about to be shipped secure payment.
In my mind, the clarity of code is vastly reduced by using AOP here. This domain logic that is weaved, is not be apparent and visible when looking at Order class. The domain class Order, is incomplete without considering the two aspects. This breaks the clarity of the code and model that is offered by DDD. There is no apparent gain using AOP here.
In this example, both aspects are core domain logic and in on real world model some object would have ownership of these bits of functionality. Either Order or a Domain Serice like OrderProcessSvc, would be the best objects to own this logic. Choosing between entity and service to host this logic, depends on whether the Order should own PaymentAuthorization (which I think it should) or not. But main thing is to encapsulate this logic into its rightful owner.
To me aspects in DDD can be used for things like enforcing rules (augmenting java's limited support for scoping - use an aspect that throws a compilation error whenever an entity is constructed using 'new' operator), handling service failures/issues etc at boundary with infrastructure services.
I believe Domain Logic should only be contained in POJOs to maintain clarity and readability. Any thoughts?
Technorati Tags: DDD, Domain Driven Design