Monday, September 25, 2006

Domain Driven Design : The Leaky Model AntiPattern

Lots of people have blogged about the importance of maintaining the sanctity of the domain model in a domain driven architecture. Everyone agrees in principle that the domain model should encapsulate the domain logic to the fullest, that the service layer should be lean and the Web layer should not intrude into the implementations of the domain model.

Well, that's great for a starter! Approaching the entree, we find things moving south when various forums in TSS and Spring, Yahoo groups and individual blogs start discussing about domain models going anemic through rich service and presentation layers. Many people have suggested using the DTOs to force a separation between the domain layer and the presentation layer and getting rid of the dreaded LazyInitializationException from Hibernate-backed persistence layer. DTOs create an extra level of indirection to ensure the separation and form a valid architectural paradigm for use cases of distribution and serialization of deep nested object graphs. For others, where all layers are colocated and possibly replicated using clustering, we should always try to maximize the reachability of the domain model.

Domain Model is Crosscutting

The domain model is built upon the layers below it and serves all the layers above it. The domain layer itself encapsulates the business logic and provides fine grained behavioral POJOs to the service layer. The service layer builds up coarse-grained services out of the POJOs exposed by the domain layer and serves the presentation layer above. Hence the POJOs of the domain layer crosscuts all layers above it although exposing different views to each of them. For a particular domain entity, the interface that it publishes within the domain layer may be different from the interface that it exposes to the service or web layer. Hence we need to ensure that the model enforces this multiplicity of interfaces and prevents any implementation specific artifact leaking out of the domain layer.

Let us have a look at a real life example ..

A Domain POJO Exposing Implementation

A domain level POJO named Order, which contains a List of Items ..


package org.dg.domain;

import java.util.List;

public class Order {
  private int orderNo;
  private String description;
  private List<Item> lineItems;

  public Order(int orderNo, String description) {
    this.orderNo = orderNo;
    this.description = description;
  }

  /**
   * @return Returns the description.
   */
  public String getDescription() {
  return description;
  }

  /**
   * @param description The description to set.
   */
  public void setDescription(String description) {
    this.description = description;
  }

  /**
   * @return Returns the orderNo.
   */
  public int getOrderNo() {
    return orderNo;
  }

  /**
   * @param orderNo The orderNo to set.
   */
  public void setOrderNo(int orderNo) {
    this.orderNo = orderNo;
  }

  /**
   * @return Returns the lineItems.
   */
  public List<Item> getLineItems() {
    return lineItems;
  }

  /**
   * @param lineItems The lineItems to set.
   */
  public void setLineItems(List<Item> lineItems) {
    this.lineItems = lineItems;
  }

  public float evaluate( .. ) {
  }
}



This is a typical implementation of a domain POJO containing all getters and setters and business logic methods like evaluate(..). As it should be, it resides in the domain package and happily exposes all its implementation through public methods.

Leaky Model Symptom #1 : Direct Instantiation

The first sign of a leaky domain model is unrestricted access to class constructor thereby encouraging uncontrolled instantiations all over. Plug it in, make the constructor protected and have a factory class take care of all instantiations.


public class Order {
  // same as above

  protected Order(int orderNo, String description) {
    ..
  }
  ..
}

package org.dg.domain;

public class OrderFactory {
  public Order createOrder(int orderNo, String description) {
    return new Order(orderNo, description);
  }
}



If u feel the necessity, you can also sneak in a development aspect preventing direct instantiation even within the same package.


package org.dg.domain;

public aspect FlagNonFactoryCreation {
    declare error
      : call(Order+.new(..))
      && !within(OrderFactory+)
      : "OnlyOrderFactory can create Order";
}



Leaky Model Symptom #2 : Exposed Implementation

With the above implementation, we have the complete Order object exposed to all layers, even though we could control its instantiation through a factory. The services layer and the presentation layer can manipulate the domain model through exposed setters - a definite smell in the design and one of the major forces that have forced architects and designers to think in terms of the DTO paradigm.

Ideally we would like to have a restricted view of the Order abstraction within the web layer where users can access only a limited set of contracts that will be required to build coarse-grained service objects and presentation models. All implementation methods and anemic setters that directly manipulate the domain model without going through fluent interfaces need to be protected from being exposed. This plugs the leak and keeps the implementation artifacts locked within the domain model only. Here is a technique that achieves this by defining the restricted interface and weaving it dynamically using inter-type declarations of AOP ..

// Restricted interface for the web layer


package org.dg.domain;

import java.util.List;

public interface IOrder {
  int getOrderNo();
  String getDescription();
  List<Item> getLineItems();
  float evaluate( .. )
  void addLineItem(Item item); // new method
}



// aspect preventing leak of the domain within the web layer


package org.dg.domain;

public aspect FlagOrderInWebLayer {
  pointcut accessOrder(): call(public * Order.* (..))
          || call(public Order.new(..));

  pointcut withinWeb( ) : within(org.dg.web.*);

  declare error
    : withinWeb() && accessOrder()
    : "cannot access Order within web layer";
}



Note that all setters have been hidden in IOrder interface and an extra contract has been exposed to add an Item to an existing Order (addLineItem(..)). This makes the interface fluent and closer to the domain user, since he will typically be adding one item at a time to an Order.

Finally, weave in the IOrder interface dynamically through inter-type declarations of AspectJ, when exposing the Order abstraction to the layers above. In fact every layer to which the full abstraction of Order has been restricted can make use of this new interface.


package org.dg.domain;

public aspect RestrictOrder {
  declare parents: Order implements IOrder;

  public void Order.addLineItem(Item item) {
    this.getLineItems().add(item);
  }
}



Hence, the web layer does not get access to the full implementation of Order and has to work with the restricted contract of IOrder, thereby preventing the leaky domain model antipattern.

Tailpiece

Now that we have agreed to plug in the holes of the leaky domain model, how should we decide upon the composition of the published restricted interface ?

I find some dichotomy here. The published interface should, on one hand, be restrictive, in the sense that it should hide the implementation contracts from the client (the web layer, in the above case). While, on the other hand, the published interface has to be humane and should follow the principles of Intention-Revealing Interfaces. The interface should add convenience methods targetted to the client, which will help him use the object more effectively and conforming to the Ubiquitous Language.

11 comments:

Anonymous said...

Good entry,but i'm wondering where you'll be creating/getting Orders.Does this cater for auto binding as provided by Spring MVC for example?

Debasish said...

Talking of creating Orders from user supplied information, the domain object can be created by Spring binding (as in Spring MVC), validations can be done through registered validators (Spring Validator) and the final persistence through the persistence layer. But the main idea will be to restrict the users of the upper layers (layers above the domain model) to the ubiquitous language and not allowing them to misuse the domain model.

Anonymous said...

Ok, i'm not that fluent with AspectJ pointcuts and I thought the weblayer pointcut would disallow even the Spring binder exposed through the formcontrollers to call setters on the domain object.I would have to step up my aspectj.

Debasish said...

Yes, the idea is to prohibit setters of domain model from web layer. Anyway, the following factory call can be used for instantiation :

IOrder o = (IOrder)(new OrderFactory().createOrder(1, "order1"));

I am not sure if Spring binder can be instructed to create objects using the factory.

Alex said...

The getLineItems() and setLineItems(List<Item>) methods are also leaky, if really implemented as in the example...
The getLineItems() should return an unmodifiable list or a deep-copy of the list; and setLineItems(List<Item>) just opens the implementation for client to manipulate, and should be removed. The addLineItem(Item) method should be added to the Order class. It should also copy the Item.

Web layer should not create Order instance at all. It should just invoke a createOrder(List<Item>) on the OrderService or facade...

Debasish said...

@Alex
The web layer will only have access to the domain methods which will be exposed through the interface IOrder. And setters are definitely not one of them. I prefer to keep setters dumb and use them only for MVC frameworks - I prevent illegal access of setters through aspects. In fact in the example as I have shown, I have come up with a restricted interface, IOrder, which will be accessible from the presentation layer. This will be done through inter-type declaration of AspectJ or Spring Introductions, if u use Spring. For the restricted interface, the getLineItems() method will return an unmodifiable collection (as you have rightly pointly out). But the theme of the post is how to make use of the domain object as an end-to-end entity in a complete web application.

HB said...

You seem afraid to expose your Domain objects to the web layer directly, and are calling this leaky. You indicate that you want to prevent the consumer from misuing your domain layer. But if you have implemented your domain layer invariants properly, it should be impossible to get it into an invalid state, and it should apply all necessary validations on methods to prevent misuse.

I've read your blog entry thoroughly and I am not convinced that exposing your domain directly to a consumer is an anti pattern.

Check out this presentation:
http://www.parleys.com/display/PARLEYS/Domain%20Driven%20Design%20with%20AOP%20and%20DI

Debasish said...

@HB:
I fully agree with you that implementing invariants properly at the domain layer does not leak any implementation to the layers above. But consider the case when you use a domain object as the glue object of the MVC layer to be set directly from the request. In that case you need to expose the setters as public methods that will be used by the MVC framework. Exposing public setters is *not* always very helpful in maintaining invariants of the domain layer. This is, where things become leaky. The other option is, of course, to use DTOs as the glue objects, but that is the topic for a separate discussion. Using aspects, we can control what we expose to the Web layer, and more importantly, where we expose. Aspects can restrict exposure of setters *only* to the MVC framework, while preventing the rest. This way we can address the leaky model anti-pattern.

HB said...

I agree with you that some properties cannot be freely settable while maintaining invariants. For example, on a recent project I was dealing with travel requests. If you changed the dates of your trip to span a fiscal year, it involved the creation of various additional expense estimates. Per Evans DDD book, I made the properties read-only and introduced a method that handled everything in a single "atomic" operation. Obviously you can't bind directly to this object as a method needs to be invoked. But ultimately, as the domain developer, I don't care how the consumer gets the data into the object, as long as it does it MY way. It also seems to me that changing your domain object (i.e. adding properties or methods using AOP) for the sake of the client architecture is possibly infringing on the idea of separation of concerns. As I sometimes say to my son, "Just because you can, doesn't mean you should." I guess I'm just not particularily convinced by this application of AOP, but it's a good discussion nonetheless.

Debasish said...

When I design my domain layer, I try to decouple my thoughts from all other layers and focus only on the modeling aspects that the domain drives me into. Then if I decide to use the domain class as the glue of the MVC layer, I am doing a compromise - on one hand I am saving myself the trouble of managing an extra layer of DTOs, but on the other I am adding public setters that the MVC framework needs to transport request data. This is a design decision that I need to take everytime I design my Java EE application. But these public setters constitute the so called leaks of the domain model and provide developers an opportunity to subvert the invariants. And this is when I use aspects to plug this hole. I know this is not the best of strategies, but I am yet to find a better one. Will be helpful if u can elaborate on your thoughts on using the domain objects directly in the MVC layer. Or u prefer to use DTOs.

Sameer Nambiar said...

Have been following your blogs and forum posts on this topic and have found your ideas very useful. Thank you.

Some of these posts are over a couple of years ago, and I am wondering if there have been any new improvements that render some of implementation approach-suggestions obsolete. Choice of DTO vs Domain objects in the presentation layer is taking up a lot of my mindshare.

Some of the OSGi developments have been interesting and I am looking at a design of this nature:

1. Web bundle - JSF+WebFlow (Presentation + Application/Workflow layer)

2. Domain bundle - (Service Facade + Domain Objects + Repository + Hibernate persistence)

OSGi allows hiding of the domain implementation objects from the web bundle and exposing only interfaces (for application and domain services) and for domain pojo interfaces.

Reading the blogs, one option is to allow the domain objects to be usable on the web layer via interfaces for read-only and mutable operations on the domain object.

This can be extended further to make the application layer function as an anti-corruption layer passing data to/from JSF managed beans (presentation layer) and domain bundle. Benefits include the avoidance of the DTOs and also allows decoupling between my bundles (web and domain).

This would overload the application layer a bit (thinning the managed bean), but that is something I might anyway need to do to use domain functionality spread across multiple domain bundles.

Would appreciate your thoughts on whether this would be a good approach.

Thanks,
/Sameer