5

In trying to understand Domain Driven Design I keep returning to a question that I can't seem to definitively answer.

How do you determine what logic belongs to a Domain entity, and what logic belongs to a Domain Service?

Example: We have an Order class for an online store. This class is an entity and an aggregate root (it contains OrderItems).

Public Class Order:IOrder
{
    Private List<IOrderItem> OrderItems

    Public Order(List<IOrderItem>)
    {
        OrderItems = List<IOrderItem>
    }

    Public Decimal CalculateTotalItemWeight()
    //This logic seems to belong in the entity.
    {
        Decimal TotalWeight = 0
        foreach(IOrderItem OrderItem in OrderItems)
        {
            TotalWeight += OrderItem.Weight
        }
        return TotalWeight

    }
}

I think most people would agree that CalculateTotalItemWeight belongs on the entity. However, at some point we have to ship this order to the customer. To accomplish this we need to do two things:

1) Determine the postage rate necessary to ship this order.

2) Print a shipping label after determining the postage rate.

Both of these actions will require dependencies that are outside the Order entity, such as an external webservice to retrieve postage rates. How should we accomplish these two things? I see a few options:

1) Code the logic directly in the domain entity, like CalculateTotalItemWeight. We then call:

Order.GetPostageRate
Order.PrintLabel

2) Put the logic in a service that accepts IOrder. We then call:

PostageService.GetPostageRate(Order)
PrintService.PrintLabel(Order)

3) Create a class for each action that operates on an Order, and pass an instance of that class to the Order through Constructor Injection (this is a variation of option 1 but allows reuse of the RateRetriever and LabelPrinter classes):

 Public Class Order:IOrder
{
    Private List<IOrderItem> OrderItems
    Private RateRetriever _Retriever
    Private LabelPrinter _Printer

    Public Order(List<IOrderItem>, RateRetriever Retriever, LabelPrinter Printer)
    {
        OrderItems = List<IOrderItem>
        _Retriever = Retriever
        _Printer = Printer
    }

    Public Decimal GetPostageRate
    {
        _Retriever.GetPostageRate(this)
    }

     Public void PrintLabel
    {
        _Printer.PrintLabel(this)
    }
}

Which one of these methods do you choose for this logic, if any? What is the reasoning behind your choice? Most importantly, is there a set of guidelines that led you to your choice?

Casey Wilkins
  • 2,555
  • 2
  • 23
  • 31

4 Answers4

1

I would use (2).

It doesn't add extra complexity to your Order Item.

To me, it seems the natural use of a helper service.

Update: in response to comment: The wiki page states:

Anemic Domain Model: With this pattern, logic is typically implemented in separate classes which transform the state of the domain objects

Mitch Wheat
  • 295,962
  • 43
  • 465
  • 541
  • Does using #2 push us towards an anemic domain model in you opinion? – Casey Wilkins Jan 30 '11 at 00:03
  • I don't think so, as far as I uderstand it, it is OK to have a seperate object take a bunch of other objects and make decisions based upon their state as long as the objects in question are not being altered or transformed. – Mitch Wheat Jan 30 '11 at 00:18
  • If we change the state of the order by having the service set a PostageRate property on the order... Does that count as transforming the order? Does something that insignificant mean that we would change our original decision? It seems that DDD is so full of these "Catch 22's" that it becomes very hard to actually implement. – Casey Wilkins Jan 30 '11 at 00:44
  • I would question whether PostageRate belongs to the Order Item. Perhaps it belongs to a PostedOrder(History) or other object? – Mitch Wheat Jan 30 '11 at 00:47
1

I would tend to have an external service determine the shipping rate. For me that's application logic rather then order-specific logic. For example you might decide for a period to offer free shipping for orders over a certain size, or to a particular group of loyal customers. For me that logic will tend to change independently of how an order is constructed.

Most likely I would have the code which is responsible for placing the order (some kind of Order Processor service, in an application layer or in a command handler) hand off to a service to get the shipping rate, and then pass that rate into the order, so I guess option 2.

For printing a shipping label, I'd tend towards having the domain raise an event along the lines of http://www.udidahan.com/2009/06/14/domain-events-salvation/. A separate handler would then deal with printing the label. Again, the logic for this is that the way you print labels is likely to vary independently of how you construct an order, so it makes sense to keep that separate. Using a domain event seems to be the cleanest way of ensuring that the label is printed at the right time without requiring the Order (or indeed the order processor) to be aware of the printing logic.

David
  • 2,038
  • 14
  • 11
  • My instinct is to follow your line of thinking, but if we follow this line of thinking do we end up having an anemic domain with all of our "real" logic outside of our entities? – Casey Wilkins Jan 30 '11 at 00:50
  • I worried about that as well, but it hasn't really panned out that way. Definitely some logic has ended up at the application level, but I actually think that's right anyway. – David Jan 30 '11 at 01:10
  • Just reading over your original question again. If you're calling an external webservice for shipping rates, then that logic is outside the domain regardless. I'd say it's an application-level decision to use an external webservice. If there were rules that you applied to the information you got back - eg you apply a discount to the rate for loyal customers or something - that logic might be still applied in the domain, so then you'd pass the result of the web service back into the domain. – David Jan 30 '11 at 01:23
  • Thanks for the input David. Your answer prompted me to review some DDD principles, and I think I am trying to stuff too much behavior into my domain entities when that behavior isn't really related. I would agree with the other posters here that 2) is the best solution for this case. – Casey Wilkins Jan 30 '11 at 22:23
1

If you are accessing external webservices to get Postage rate, it is better to create interface in Application layer, because evan itself suggested that if you want to talk with external webservices you should construct interface in Application layer, you would have the service implementation injected into your Domain Object. For printing shipping label, because label is printed only when postage rate is determined, so is a kind of event like PostageRateConfirmed your domain can raise this event.

http://danhaywood.com/2010/04/30/accessing-domain-services-from-entities/

kamal
  • 739
  • 1
  • 9
  • 21
0

My point of view: The domain is what contains the logic of your application, without the infrastructure cruft. The logic is that when an order is confirmed a label is printed and the shipping rate is determined. That should be in the domain.

The infrastructure then accomplishes what the domain wants to do. The domain may let the infrastructure know through messaging, or events.

That way no infrastructure leaks into the domain, you only require a way to transport messages out of the domain.

flq
  • 22,247
  • 8
  • 55
  • 77