Larsbj
Larsbj

Reputation: 38

DDD: How do structure or resolve more complex behaviour in a domain entity?

Assume the classic Order/OrderLine scenario.

public class Order {
 ...
    public void AddOrderLine(OrderLine ol) {
        this.OrderLines.Add(ol);
        UpdateTaxes();
    }


    private void UpdateTaxes() {
        //Traverse the order lines
        //Collect the VAT amounts etc
        //Update totals 
        var newTaxes = Orderlines.SelectMany(ol => ol.GetTaxes());
        Taxes.Clear();
        Taxes.Add(newTaxes);

    }

}

Now, we figure that we need to handle taxes better, with different ways for customers in various countries etc, where some require VAT to be collected and others not.

In short, the tax rules will depend on the customer's location, items purchased etc. How would we do this? Should we put a lot of code into UpdateTaxes? Could we use tax calculator factory and reference it in UpdateTaxes?

private void UpdateTaxes() {
    var taxRules = TaxRulesFactory.Get(this);
    var taxes = taxRuleCalc.Apply(this);
    Taxes.Clear();
    Taxes.Add(taxes);
}

Upvotes: 1

Views: 747

Answers (3)

Matt
Matt

Reputation: 1678

You might also have a think about whether the concept of Tax and the concept of Orders need to be located within the same bounded context. It perhaps seems logical or at least implicit that when you're in the process of creating an Order you will want to know Tax due, but does this necessarily apply in your domain?

I'm not saying it does or it doesn't, by the way -- I'm simply saying think about it for your particular domain. Very often when the model seems awkward to represent it's because it mixes concerns that don't belong together.

Upvotes: 2

Eben Roux
Eben Roux

Reputation: 13256

Considering your broader question regarding complex behaviour in ARs the preferred way to handle this would be to use double-dispatch. Bear in mind that complex behaviour certainly can be included in the AR if that behaviour is cohesive.

However, for functionality that varies to the degree of tax or even discount calculation, where one would implement various strategies, you could opt for the double dispatch:

public class Order
{
    public void ApplyTax(ITaxService taxService)
    {
        _totalTax = taxService.Calculate(TotalCost());
    }

    public void ApplyDiscount(IDiscountService discountService)
    {
        _discount = discountService.GetDiscount(_orderLines.Count, TotalCost());
    }

    public Money TotalCost()
    {
        // return sum of Cost() of order lines
    } 
}

These services also should not be injected into the AR but rather passed into the relevant method.

Upvotes: 5

Uber Bot
Uber Bot

Reputation: 511

May be you could extract UpdateTaxes into a separate class which would be responsible for tax calculation against a particular order. And itself would chose an appropriate strategy (a separate strategy class) depending on customer, order, etc. I feel that tax calculation is a separate responsibility here.

Upvotes: 2

Related Questions