DDD Aggregate Component pattern in action

In my last post, I went on a long-winded rant on how the composition and the Aggregate Component pattern can ease the burden of the interaction between Entities and Services.  The question comes up often on DDD or IoC forums:

How do I inject/use a Service/Repository inside an Entity?

You can get really fancy with your ORM or really fancy with your IoC Container to try and make your Entities join in an unholy matrimony.  But there are other, better ways of skinning this OO cat.

The hard way

We may try to get these services injected through our ORM or IoC container, or we could try and do a static, opaque or service located dependency directly inside our entity:

public class Order
    public decimal CalculateTotal()
        decimal itemTotal = GetSubTotal();
        decimal taxTotal = TaxService.CalculateTax(this);
        decimal discount = DiscountCalculator.FindDiscount(this);

        return itemTotal + taxTotal - discount;

In this snippet, we have an Order entity that is apparently responsible for calculating up-to-date tax and discount information.  We’ll get back to that cluster in a second.  First order of business, how do we deal with these two dependencies, the TaxService and DiscountCalculator?  A few options include:

  • Static, opaque dependencies (seen above)
  • Use service location, through a container or other means (ServiceLocator.GetInstance<ITaxService>())
  • Pass dependencies in through the method, still using double-dispatch (CalculateTotal(ITaxService, IDiscountCalculator)

Each of these has its merits, but still becomes quite annoying.  In the first approach, we’re back to just plain bad code, opaque, static, hard dependencies with tight coupling and no visibility from the outside of side effects or potential gotchas.

In the second approach, we’ve removed the tight coupling, but not the opaqueness.  If it’s not plainly obvious to users of this class what it’s responsible for, that’s just more boring debugging and manual analysis on the developer’s side when things go wrong or we need to make a change.  In the final option, we’ve now made our dependencies transparent, but now the burden of locating the dependencies is on the caller.

If we’re using dependency injection, no problem, but in this case we’re not injecting dependencies, we’re just passing them along.  Do we really want our callers to have to figure out how the heck to locate an ITaxService or IDiscountCalculator?  Probably not.  Now, callers of Order not only need to know about what Order needs to do its work, but have to have these dependencies available somehow.  We have better options, such as dependency injection for callers, but it’s still rather annoying that dependencies of Order are now affecting callers.

Studying our original domain model, I see a much larger issue – something’s wonky with our domain.

Fixing the model

When I was in e-commerce, we had a lot of prior art in looking at ordering and processing systems.  In the above example, we have an Entity that has a moving target on what you would think would be one of its core attributes – its Total.  An order with a changing total is bad news in the e-commerce world, as an Order is usually something that is placed and locked down.  So what is this Order that we have here?  Is it an OrderPlaced, or does it simply represent a request to place an order?

If we have a moving Total target, it’s far more likely that this is simply a request to place an order, not a placed order.  Once an order is saved or processed, it usually enters into a larger saga/workflow/process, where the order moves from system to system (processing, payment, fulfillment, etc.).  Maybe our company isn’t that complex, but we’d still not want customer complaining that the discount they saw when they placed the order yesterday is for some reason gone when the order is fulfilled today.

For these reasons, and others, we’re often not working with an Order on the customer side, but a Cart, an OrderRequest, or even a PotentialOrder.  In those cases, it’s far more likely that these Entities, or something very close to them, will have the responsibility for calculating and displaying discounts and tax information.

But what exactly is an PotentialOrder?  Does it have an identity?  Is it ever persisted?  Oftentimes, no.  An PotentialOrder may be persisted, but more likely in a separate, locked-down model where totals and discounts can’t change the same way.

In comes the Aggregate Component

A PotentialOrder is a one-time only view of a potential order, built from a Cart (in our case, just a container of Products with quantities and a Customer), along with a snapshot of the tax and discount calculation at the time of request.  The customer can view a PotentialOrder, then decide if they would like to continue the order and create an actual Order, where other tax and discount rules may come into play.  Other names for PotentialOrder might be Quote, but the idea is still the same.  An Order implies a request on the Customer side to make a transaction.

With this in mind, our PotentialOrder still has to do tax and discount calculations.  In simple cases, we can just give the PotentialOrder the results of calculations, but in our case, we needed additional information about the tax and discounts, notably where they came from.  Our PotentialOrder became an immutable Aggregate Component (looking like a Value Object, but bigger):

public class PotentialOrder
    public PotentialOrder(Cart cart,
        ITaxProvider taxProvider,
        IDiscountFinder discountFinder)

Our PotentialOrder takes exactly what it needs to run – the Cart, a tax provider and a discount finder.  All transparent dependencies, each used for the PotentialOrder to do its job.  I don’t have to look very far to find the real total calculations:

public decimal CalculateTotal()
    decimal itemTotal = _cart.GetTotal();
    decimal taxTotal = _taxProvider.Calculate(this);
    decimal discount = _discountFinder.SumDiscounts(this);

    return itemTotal + taxTotal - discount;

We’re still using the same double-dispatch system from our opaque dependencies, but now we have a lot more leniency in how we can represent our calculations.  Typically, we’ll combine some sort of Composite and Strategy pattern, such that we can combine many complex calculation algorithms into one interface, to be consumed by our PotentialOrder.

So how does our PotentialOrder get created?  Not from a container, but through a specialized Factory instead:

public class PotentialOrderFactory
    public PotentialOrder Create(Cart cart)
        ITaxProvider taxman = _taxLocator.Find(cart);
        IDiscountFinder discounter = _discountBuilder.Build(cart);

        return new PotentialOrder(cart, taxman, discounter);

The TaxLocator and DiscountBuilder encapsulate the potential complexities of resolving the correct tax and discount algorithms.  Once built, the TaxProvider and DiscountFinder are immutable Value Objects, returning the same result every single time.  One TaxLocator might be Texas Sales Tax, which is a percentage of the subtotal of a Cart.  I do know that once my PotentialOrder is built, all of its algorithms are locked in and won’t change, no matter how complex they might get.

So why didn’t we just give the PotentialOrder its total?  Personally, I like keeping behavior associated with an object, defined on that object.  Putting the total calculations, as well as what’s needed to do so, on the PotentialOrder gets away from an anemic domain model.  Before the Aggregate Component concept, behavior about a PotentialOrder was scattershot around the system, making it harder to grasp and talk about what a PotentialOrder was with the domain experts.

Keeping our model smart

I absolutely detest dumb domain models.  The behavior is out there, I promise, but it’s probably in the wrong place.  When we start seeing scenarios where we want our persistent entities calling out to services and repositories to do additional work, I see an opportunity to explore a larger, aggregate component model that pulls all these concepts into one cohesive place.  DDD can lead to service-itis, with anemic domain models and a barrel full of services who seem awfully concerned about one or more entities.

Object-oriented design and modeling was refined and clarified with Evans’ book.  But that doesn’t mean we’re supposed to be constrained to solely the patterns and names we found in the book.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in DomainDrivenDesign. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Michael Hart

    Hi Jimmy,

    Really well written article, and I think you’ve explained the issues in a pragmatic manner too.

    The one approach that you didn’t contrast with (that I could see) was where the CalculateOrder method was passed in the taxTotal and/or discount directly. The calculation logic would still be in method, but the retrieval wouldn’t. This would be done in whatever service/component initiated the method chain to begin with.

    I actually prefer the method you described for many scenarios, but sometimes it’s overkill if all you need is a value, as opposed to a “value-locator”.

  • Jimmy,

    I agree with the general model you’ve proposed and the solution seems elegant and capable. I just have a few thoughts.

    You can still use full-blown dependency injection even if you’re using NHibernate to inject values into your domain objects:

    Based upon my reading of your post above, each of the objects you describe seems very much like domain entity objects. It seems that you hesitate to call them entities because they are being injected with domain and “object” services:

    Perhaps we’re both doing the same thing, but we’re just calling it by a different name because Evans didn’t identify it in his book.

  • Great stuff Jimmy. I’d been using a similar approach after you mentioned it a couple of weeks ago and thought up the PotentialOrderFactory factory approach as a little nicity the other day. It’s good to see that idea parallels what you’re doing.

    I think what I’m saying is… I love you man.

  • @Jonathan

    I don’t call them entities because they don’t have an identity. If anything, they are closer to Value Objects, but at a much larger scope.

    In the Order case, I tried to make the point than in the commerce domain, an Order isn’t something where a taxes and totals are recalculated. Once we found through our conversations with domain experts that a Quote or PotentialOrder is merely a snapshot in time. That would mean it’s not an entity.

    It really has nothing to do with injection, but rather why and how these models are used. That I need to do injection is a symptom of a deeper modeling problem.

  • @Michael

    In many cases, just the value is just fine. In our case, tax and discounts was quite complex, so it was nice to see right inside our object, the “how” object right there.

    That, and we needed more information than just the final discount and tax amount, like the list of discounts and names, the tax reason, etc. Tax and Discounts were value objects themselves, so we didn’t see any point in reducing their value, in our specific case.

  • joe

    Thanks Jimmy — this answered one of the biggest questions I had about how to avoid anemic domain models.

  • Nick Gieschen

    Great article – I was just wondering about the best ways to handle a Potential Order/Order issue.

    One nit to pick though. You don’t actually solve the “How do I inject/use a Service/Repository inside an Entity?” problem. You just show that in this case, the entity shouldn’t be doing the work that requires the dependency. In another scenario, your entity may well need a service and you still haven’t shown an alternative to the three options you listed. (FWIW, I consider aspects a fourth and least crappy of the alternatives, but am not happy with any of them.)

  • @Nick

    In this case, a container call in an entity was a symptom, not the disease. The real problem was bad modeling. There are a few cross-cutting concerns (logging comes to mind) where I might want an IOC container involved, but I would still see these as optional (setter) dependencies rather than opaque or ctor dependencies.

    If I really feel like I need a domain service in an entity, it’s probably for a specific scenario. In that case, I’d still go for the component patten, as that would give me a single class that can handle responsibilities for that one scenario.

  • Nick Gieschen

    I’ve started and followed discussions re this on the ddd and alt.net groups and still must be missing something.

    1. It seems like a fair deal of pain to create a component/service to handle a particular scenario, when you could just be adding a method to an entity.

    2. The component solution has a similar drawback to the “Do we really want our callers to have to figure out how the heck to locate an ITaxService or IDiscountCalculator?” problem. Now our callers have to know about this Component in order to conduct usecase x instead of just having the entity do it. For me this part of it is an issue of discoverability.

    3. I have a vague feeling that this is drifting towards a manager/anemic domain/transactiony way of doing things. If I write a Component everytime my entity has to *do* something, aren’t I becoming a bit anemic and removing reponsibility from my entity for doing things? Won’t I end up with a bunch of scattered Components all with the responsibility to do one thing? I realize they’re delegating to domain services to do these things, but from the point of view of a caller in an upper layer, I seem to have compromised encapsulation.

  • @Neil

    Again, I’d have to go back to the issue that the original design was plain modeled incorrectly. In other cases where I’ve wanted to consume services from an Entity, the Entity knew too much about what was going on.

    Even with the Component idea, I still push all behavior related to the entity down to the entity. In the above scenario, I don’t really need to worry about upper layers “knowing” about how to build components, that’s what a factory is for.

    As for anemic models, if you can recognize the Inappropriate Intimacy smell when you see it, that alone can create very rich entities.

  • Martin

    How do you handle modifications of an order? Is that also a Potential Order? But then it’s an entity because it’s related to a particular order.

  • @Martin

    In our case, modifications of an order took place in an entirely different system altogether. Different users, different rules, different technology (COBOL).

    As far as I could tell, it was a lot of CRUD with lots of validation at that point.