Thursday, September 21, 2006

AOP meets DDD

Yesterday I attended the Dallas Spring Users Group. The September speaker was Ramnivas Laddad. The topic of the presentation was Spring AOP and AspectJ. I was interested in hearing about how Spring leverages the AspectJ pointcut syntax in Spring 2.0. I am quite familiar with Spring 1.2's type of AOP. I have experimented with the AspectJ pointcut language, but I wanted to get some guidance on best practices.

Well, this topic was covered quite well. But at the end of the presentation he focused on how to leverage the full power of AspectJ - advising classes vs. advising objects via proxies. One of the last slides he presented was how to use AspectJ to inject dependencies into domain objects. This point was sort of mentioned in passing, but it raises a very interesting debate. Is using aspects to inject domain objects with dependencies (possibly service-type objects) the right way to create a richer domain model?

After the presentation we discussed a concrete example. Suppose you have a Customer object. In the domain, the Customer has an Account, but in the object model, a Customer object only has a reference to its Account's id (following the notion of an aggregate root in domain driven design). Now suppose the service layer supports the method getNetWorth(Customer). Without a rich domain object, the service would be responsible for retrieving the Account object on behalf of the Customer object and returning the value of the Account. Something like this:

...
Account account = accountRepository.getAccount(customer.getAccountId();
return account.getValue();
...

Bu
t suppose we want this logic to exist in the Customer object, as it should with a rich domain model. How would the Customer object obtain a reference to its Account object? The answer is it needs access the AccountRespository, And AspectJ + Spring can help achieve this. Basically, an AspectJ aspect (woven into the Customer class) would be invoked that would inject the AccountRepository bean from the Spring context whenever a Customer object in instantiated.

Now, for those who are knee-deep in full blown aspects, this is probably not a novel idea. But for those of us who have simply lived in the proxy-based aspect world., this opens up a whole new realm of possiblities. The implication of having a rich domain model (as opposed to much of the business logic remaining in the service layer) is quite intriguing. However, it raised many questions:

  • Is going whole-hog aspects right for my application? Going from proxy-based AOP to full-blown aspects is not a small step.
  • Is giving the domain layer access to other services/data access objects a good design?
  • What sort of recursive dependencies will be introduced when the domain becomes aware of these layers?
I am looking forward to delving into these questions. The possibilty of having "real" domain layer is way too interesting to ignore, I am not ready to drink the Kool-Aid, but I am ready for a taste.

4 comments:

Kyle Wright said...

I am tasting the kool-aid as well. I'm currently using this technique to inject data access objects into the domain objects (and a few other objects ;-)). My Pre-AspectJ approach was to use a Hibernate interceptor (entityInterceptor on the sessionFactory) that was BeanFactoryAware. There's alot of talk on the Spring forums about both approaches. The Spring team recommends using AOP, and after I heard Ramnivas speak at the No Fluff Just Stuff Cincinnati Symposium, I was ready to give it a try.

For spring documentation, look in section 6.8.1 of the 2.0 doc: http://static.springframework.org/spring/docs/2.0.x/reference/aop.html#aop-atconfigurable

There were some bugs in 2.0 RC-3 in aspect weaving but they have been fixed in RC-4.

Also, this technique requires load-time weaving or pre-compilation which can be a pain to remember to do when running a unit test in an IDE.

Ryan Breidenbach said...

Yeah, I considered the Hibernate interceptor approach myself. The limitation is that this is only useful when Hibernate is managing the lifecycle of the domain object. What about in other circumstances - specifically new DomainObject()? This is where class-level aspects shine - being able to advise constructors.

Regarding your other issue of unit testing...I would take the approach of testing a domain object that now has visibility to service/data access objects just as I would any other similar scenario - I would mock these dependencies. Of course, I have not done this with a domain object (yet). I plan on writing some example code and posting a follow-up this weekend.

Anonymous said...

All Wrong, at first,your domain model 'Customer ' is wrong:

Customer should refer to Account object, not Account's accountId.

Colin Jack said...

I have to agree with Sunny, your Customer should have full blown Accounts.

If that causes performance problems then I guess they'll have to call repositories but don't underestimate the trouble this causes, mocking sounds like a good approach but it quickly gets out of hand so I'd avoid it unless completely necessary.