Tuesday, December 12, 2006

Domain Driven Design : Service Injection Strategies in Spring and their Pitfalls - Part 2 - Service Injection into Aspects

In Part 1 of this series, I had discussed one way of injecting services into domain objects not instantiated by the Spring container - the @Configurable annotation, which provides a nice declarative semantics for encouraging rich domain models. I had also pointed out some of the pitfalls which can bite you in course of the implementation using the @Configurable technique.

One of the highlights to remember regarding the @Configurable approach is that the annotation works on a per-class basis and cannot be meaningfully enforced on class hierarchies all at once. Let us consider the case where we need to inject a service to a number of domain classes, not related by inheritance. I came across this situation recently in modeling the financial domain for developing a solution for a capital market back office system.


class Trade {
  // models a trade of a security for an account
}

class Settlement {
  // models a settlement of a trade
}

class Position {
  // models the security and cash position of an account
}



These three are only examples of some of the many domain classes which needed a validation sevice for the account on which they operate. It is fairly simple to inject the validation service using @Configurable - note that being domain classes, these are not instantiated by Spring container. Hence @Configurable works like a charm !


@Configurable("trade")
class Trade {
  private IAccountValidationService accountValidationService;
  // setter ..
  // ..
}

@Configurable("settlement")
class Settlement {
  private IAccountValidationService accountValidationService;
  // setter ..
  // ..
}

@Configurable("position")
class Position {
  private IAccountValidationService accountValidationService;
  // setter ..
  // ..
}



and we have the corresponding applicationContext.xml :


<bean id="trade"
  class="org.dg.biz.Trade" scope="prototype">
  <property name="accountValidationService">
  <ref bean="defaultAccountValidationService"/>
  </property>
</bean>

<bean id="settlement"
  class="org.dg.biz.Settlement" scope="prototype">
  <property name="accountValidationService">
  <ref bean="defaultAccountValidationService"/>
  </property>
</bean>

<bean id="position"
  class="org.dg.biz.Position" scope="prototype">
  <property name="accountValidationService">
  <ref bean="defaultAccountValidationService"/>
  </property>
</bean>

<bean name="defaultAccountValidationService"
  class="org.dg.biz.DefaultAccountValidationService">
</bean>



One of the disturbing points of the above configuration is the boilerplate repetition of the service injection for accountValidationService. If tomorrow we need to change the validation strategy, we need to change entries for all of them separately - a violation of DRY. And had it not been for this service, we need not have an entry in the configuration file for these domain classes at all !

When Dealing with Repetitions, Think Aspects

Clearly the above domain classes cannot be related through any common parentage - so we cannot capture them directly on their head. Why not have an extra level of indirection that enables us to do so ? Adrian Coyler explains this strategy succinctly in this article and I will try to summarise my experience in using it in a domain modeling exercise.

Let us have the domain classes themselves advertise the services that they want to subscribe to ..


class Trade implements IAccountValidationClient {
  // ..
}

class Settlement implements IAccountValidationClient {
  // ..
}

class Position implements IAccountValidationClient {
  // ..
}



Aha .. now at least we have the head to catch - we need to determine how we can inject the service in all of them using the head. Think Pointcuts ..


pointcut clientCreation(IAccountValidationClient aClient) :
  initialization(IAccountValidationClient+.new(..)) &&
  !initialization(IAccountValidationClient.new(..)) &&
  this(aClient);



This will capture all instantiations of classes that subscribe to account validation service by implementing IAccountValidationClient. Once we have the instantiations intercepted through a pointcut, can we inject a service into each of them through an aspect ? Note that the service injection cannot be done through inter-type declarations or type introductions, since all of the classes will actually be using the service in-situ, while inter-type declaration introduces the new service off-site in the aspect definition. e.g.


class Trade implements IAccountValidationClient {
  // ..

  public void validate(..) {
    // validate account using validation service
    // the validation service has to be declared in-situ
  }
}



Inject the Service into an Aspect

Create an aspect with the above pointcut, inject the service into it and use the injected service in an aspect advice to reinject it into the client using the matching joinpoint. The only question is how to inject the service into an aspect .. and this is where Spring rocks. Spring allows you to specify a factory method which the container will use for instantiation of the bean. And AspectJ exposes the method aspectOf for every aspect, which precisely fits the situation like a glove. Marry the two and what you have is pure magic :


<bean name="accountValidationServiceInjector"
  class="org.dg.biz.AccountValidationServiceInjector"
  factory-method="aspectOf">
  <property name="service"><ref bean="accountValidationService"/></property>
</bean>



The aspect gets the service injected after instantiation through the aspectOf factory method.

The Missing Block in the Puzzle - the Advice that Gets Weaved

Here is the complete aspect :


public aspect AccountValidationServiceInjector {
  private IAccountValidationService service;

  public void setService(IAccountValidationService service) {
    this.service = service;
  }

  pointcut clientCreation(IAccountValidationClient aClient) :
    initialization(IAccountValidationClient+.new(..)) &&
    !initialization(IAccountValidationClient.new(..)) &&
    this(aClient);

  after(IAccountValidationClient aClient) returning :
    clientCreation(aClient) {
    if (aClient.getAccountValidationService() == null) {
      aClient.setAccountValidationService(this.service);
    }
  }
}



and the relevant portions of a client :


public class Settlement implements IAccountValidationClient {

  private IAccountValidationService accountValidationService;

  public IAccountValidationService getAccountValidationService() {
    return accountValidationService;
  }

  public void setAccountValidationService(
    IAccountValidationService aValidationService) {
    this.accountValidationService = aValidationService;
  }

  // ...
}



It does not need the @Configurable annotation and hence the configuration boilerplates disappear. And if we need to change the validation strategy, we need to change only one entry for the aspect accountValidationServiceInjector in the applicationContext.xml.

Strategy Specialization using @Configurable

The above technique injects an implementation of a service across all domain objects that publish their willingness for the service. It may so happen, that some of them may need to go for a specialized service implementation.

e.g. in our domain, the account validation service for Position needs to check if the Position Management Service is subscribed for the account in the contract with the Custodian. This calls for a special validation service to be injected for Position class. This can be done using @Configurable on top of the above generalized injection.


@Configurable("position")
class Position {
  private IAccountValidationService accountValidationService;
  // setter ..
  // ..
}



Here are the corresponding entries in applicationContext.xml for the specialized service :


<bean name="specialAccountValidationService"
  class="org.dg.biz.SpecialAccountValidationService">
</bean>

<bean id="position"
  class="org.dg.biz.Position"
  scope="prototype">
  <property name="accountValidationService">
    <ref bean="specialAccountValidationService"/>
  </property>
</bean>



With the above specialization, all classes implementing IAccountValidationClient will be injected with DefaultAccountValidationService, except Position, which will get an instance of SpecialAccountValidationService. Note that this may require an explicit setting of aspect precedence as I mentioned in Part 1.

Pitfalls

I found the above technique quite useful in injecting common services to domain classes at large. The main advantages were realized with reduced size of the configuration xml and ease of adaptability to changes in implementation.

The main pitfall with this approach is with respect to injection upon deserialization (the same as @Configurable), which can be addressed using the same technique that Ramnivas has adopted in fixing @Configurable.

Another pitfall of this approach is that all clients need to implement the interface explicitly and have the setters programmed within the domain class. But this is a one time effort and has no impact on future changes to service implementation. It may be an interesting exercise to use Java Generics to reduce the boilerplates that need to be coded in the domain abstractions. The important point to consider here is that every domain class may implement multiple service clients - I tried to model this with Java Generics, but only banged my head with type erasure getting in the way ..


class Trade implements IAppClient<IAccountValidationService>, IAppClient<IAuditService> {
  // ..
}



No good in the current implementation of Java generics.

In the next part of this series, I will have a look at the non-singleton services and how the new Spring 2.0 scoped beans can provide an elegant solution for some such typical use cases. For now, I need some more caffeine kick .. Spring 2.0.1, the NetBeans platform, the new release of Scala .. too many toys to play around with in my list for now ..

2 comments:

Anonymous said...

Great article, thanks!

One question though: I've been looking at AspectJ to enable more flexibility in our domain model.

The main problem (so far) is that the Load Time Weaver increases the start-up time of the application, and hotswapping of classes is almost impossible. This is not a big issue in production IMO, but during development it can make people crazy :/

Are you using the LTW, or do you weave the aspects at compile-time?

I've heard rumors about a an improved LTW that will support hotswapping classes. I've also heard something about doing the weaving using a classloader instead of a JVM agent. If that is true I think it can improve the development experience in AspectJ a great deal.

Unknown said...

[for Anonymous:]

For most of the cases in production aspects, I go with compile time weaving or post-compile time weaving unless (as Adrian Coyler and Matthew Webster mentioned in one of the aspectj-users threads) :

* you need the flexibility of changing the set of aspects you are using from run to run
* you are using the aspects for temporary instrumentation (e.g. using Glassbox) - this is really a special case of the above
* you just want to try things out quickly and don't want the hassle of changing your build process
* you don't own or control the aspects you are using and want to allow them and your application to evolve independently without the need to rebuild and redeploy your application.

LTW has a definite performance penalty. After all why incur the cost many times when u can have it incurred once. Also have a look at this post from Dean Wampler, which mentions that there has been a significant improvement in LTW with the new Java 6.

Cheers.