Monday, June 16, 2008

Domain Driven Design without AOP

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

  1. Dependency Injection in Domain entities and vo's
  2. Handling domain logic
  3. Handle Cross Cutting concerns
Of these, IMHO the only 'real' need for AOP is to handle cross cutting concerns like transaction management, auditing etc.

Dependency Injection without AOP

DI'ing services/policies/repositories into an Entity/VO can be done without using AOP in other simpler ways like
  1. 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.


  2. 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.
Domain Logic without AOP

In his presentation Ramnivas takes the example of a PaymentAuthorization service failure.
  1. If PaymentAuthorization fails, try authorizing using other service providers.
  2. In case all providers fail, do a temp auth based on payment history. When product is about to be shipped secure payment.
The example uses an around advice for PaymentProcessor.process which has retry logic. To do temp auth an Inter-type declaration is used.

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: ,

Subscribe to comments for this post

9 comments:

Anonymous said...

This is what I would do to get an OrderRepository object from Order:

new OrderRepository().getPaymentHistory();

This is the simplest possible solution, and contrary to what many may think, it does NOT prevent the unit testing of Order.

Also, classes such as OrderRepository usually can and should be final, because no alternate implementation will ever be needed in the life of the application.

Kaushik said...

@Anonymous

When you instantiate OrderRepository, how would you inject the EntityManager/ SessionFactory into the repository ? Would the repository look them up from a factory /registry? If they do isn't it the same as getting the pre-configured repository from the Registry.

Yes I agree with OrderRepository should be final.

Anonymous said...

I wouldn't need to inject EntityManager/Session anywhere, because I prefer to encapsulate persistence operations behind a static facade. Such a class (say, "Persistence", containing only static methods) can obtain the EntityManager or Hibernate Session (for its own use only) from a ThreadLocal.

Kaushik said...

@Anonymous

Just curious, is your approach similar to the one described in this thread in the DDD forums?

Anonymous said...

Similar, yes, although the details are probably different. For me, persistence is one of several sub-systems provided by the infrastructure layer. Providing access to this sub-system through a static facade brings several advantages, such as: 1) a way to enforce architectural conventions (such as having all queries use HQL instead of a mix of HQL and Criteria API); 2) a place to hide workarounds for different JDBC driver issues or different ORM tool versions; 3) simplified client code, since the facade can use Java 5 language features such as varargs and generic methods, not to mention the ability to use static imports.

Kaushik said...

@Anonymous

Your approach looks elegant and simple to implement as well. Thanks for detailing it up.

Do you only expose generic CRUD methods on the Static Facade or have any specific methods exposed as well ?

Anonymous said...

Only generic CRUD and querying methods are exposed, plus a few rarely used methods for more special needs that inevitably appear in the real world. The point is to encapsulate and expose everything persistence-related (but not including transactions) needed by a complex business web application, without allowing any unnecessary details to leak out.

Hendry Luk said...

@Anonymous...

Why doesn't it prevent unit-testing of order?

At very best, the only thing I can see is by mocking out persistence implementation from OrderRepository. Which in this case, reduces a lot of SRP-ness of Order unit-test (it becomes more like whitebox integration test).

And what benefit does it offer compared to ServiceLocator approach?

pub-2190557680964036 said...

don't inject any technology(such as Service/Repository) into business model(entity), decouple "What" from "How". we can use Domain Events pattern to do communication with them. all these have implemented in my famework(DDD framework for Java) jdon.dev.java.net

 
Clicky Web Analytics