Strengthening your domain: Encapsulating operations

Other posts in this series:

In previous posts, we walked through the journey from an intentionally anemic domain model (one specifically designed with CRUD in mind), towards a design of a stronger domain model design.  Many of the comments on twitter and in the posts noted that many of the design techniques are just plain good OO design.  Yes!  That’s the idea.  If we have behavior in our system, it might not be in the right place.  The DDD domain design techniques are in place to help move that behavior from services surrounding the domain back into the domain model where it belongs.

Besides managing the creation of aggregates and relationships inside and between aggregate roots, there comes the point where we need to actually…update information in our domain model.  One of the tipping points from moving from a CRUD model to a DDD model is emergent complexity in updating information in our model.

Fees and Payments

Let’s suppose that in our domain we can levy Fees against Customers.  Later, Customers can make Payments on those Fees.  At any time, I can look up a Fee and determine the Fee’s balance.  Or, I can look up all the Customer’s Fees, and see a list of all the Fee’s balances.  Based on the previous posts, I might end up with something like this:

[Test]
public void Should_apply_the_fee_to_the_customer_when_charging_a_customer_a_fee()
{
    var customer = new Customer();
    
    var fee = customer.ChargeFee(100m);

    fee.Amount.ShouldEqual(100m);

    customer.Fees.ShouldContain(fee);
}

The ChargeFee method is rather straightforward:

public Fee ChargeFee(decimal amount)
{
    var fee = new Fee(amount, this);

    _fees.Add(fee);

    return fee;
}

Next, we want to be able to apply a payment to a fee:

[Test]
public void Should_be_able_to_record_a_payment_against_a_fee()
{
    var customer = new Customer();

    var fee = customer.ChargeFee(100m);

    var payment = fee.AddPayment(25m);
    fee.RecalculateBalance();

    payment.Amount.ShouldEqual(25m);

    fee.Balance.ShouldEqual(75m);
}

We store a calculated balance, as this gives us much better performance, querying abilities and so on.  It’s rather easy to make this test pass:

public Payment AddPayment(decimal paymentAmount)
{
    var payment = new Payment(paymentAmount, this);

    _payments.Add(payment);

    return payment;
}

public void RecalculateBalance()
{
    var totalApplied = _payments.Sum(payment => payment.Amount);
    
    Balance = Amount - totalApplied;
}

However, our test looks rather strange at this point.  We have a method to establish the relationship between Fee and Payment (the AddPayment method), which acts as a simple facade over the internal list.  But what’s with that extra “RecalculateBalance” method?  In many codebases, that RecalculateBalance method would be in a BalanceCalculationService, reinforcing an anemic domain.

But we can do one better.  Isn’t the act of recording a payment a complete operation? In the real physical world, when I give a person money, the entire transaction is completed as a whole.  Either it all completes successfully, or the transaction is invalid.  In our example, how can I add a payment and the balance not be immediately updated?  It’s rather confusing to have to “remember” to use these extra calculation services and helper methods, just because our domain objects are too dumb to handle it themselves.

Thinking with commands

When we called the AddPayment method, we left our Fee aggregate root in an in-between state.  It had a Payment, yet its balance was incorrect.  If Fees are supposed to act as consistency boundaries, we’ve violated that consistency with this invalid state.  Looking strictly through a code smell standpoint, this is the Inappropriate Intimacy code smell.  Inappropriate Intimacy is one of the biggest indicators of an anemic domain model.  The behavior is there, but just in the wrong place.

But we can help ourselves to enforce those aggregate boundaries with encapsulation.  Not just encapsulation like properties encapsulate fields, but encapsulating the operation of recording a fee.  Even the name of “AddPayment” could be improved, to “RecordPayment”.  The act of recording a payment in the Real World involves adding the payment to the ledger and updating the balance book.  If the accountant solely adds the payment to the ledger, but does not update the balance book, they haven’t yet finished recording the payment.  Why don’t we do the same?  Here’s our modified test:

[Test]
public void Should_be_able_to_record_a_payment_against_a_fee()
{
    var customer = new Customer();

    var fee = customer.ChargeFee(100m);

    var payment = fee.RecordPayment(25m);

    payment.Amount.ShouldEqual(25m);

    fee.Balance.ShouldEqual(75m);
}

We got rid of that pesky, strange extra “Recalculate” call, and now encapsulated the entire command of “Record this payment” to our aggregate root, the Fee object.  The RecordPayment method now encapsulates the complete operation of recording a payment, ensuring that the Fee root is self-consistent at the completion of the operation:

public Payment RecordPayment(decimal paymentAmount)
{
    var payment = new Payment(paymentAmount, this);

    _payments.Add(payment);

    RecalculateBalance();

    return payment;
}

private void RecalculateBalance()
{
    var totalApplied = _payments.Sum(payment => payment.Amount);
    
    Balance = Amount - totalApplied;
}

Notice that we’ve also made the Recalculate method private, as this is an implementation detail of how the Fee object keeps the balance consistent.  From someone using the Fee object, we don’t care how the Fee object keeps itself consistent, we only want to care that it is consistent.

The public contour of the Fee object is simplified as well.  We only expose operations that we want to support, captured in the names of our ubiquitous language.  The “How” is encapsulated behind the aggregate root boundary.

Wrapping it up

We again see that a consistent theme in DDD is good OO and attention to code smells.  DDD helps us by giving us patterns and direction, towards placing more and more logic inside our domain.  The difference between an intentionally anemic domain model (a persistence model) and an anemic domain model is the presence of these code smells.  If you don’t have a legion of supporting services propping up the state of your domain model, then there’s no problem.

However, it’s these external domain services where we are likely to find the bulk of domain model smells.  Through attention to the code smells and refactorings Fowler laid out, we can move towards the concepts of self-consistent aggregate roots with strongly-enforced boundaries.

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

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 Domain-Driven Design. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://weblogs.asp.net/dotjosh Josh Schwartzberg

    I definitely agree striving for this style of thinking when writing code, but I do have to wonder how you would proceed if calling recalculatebalance() took a long time and you needed to increase it’s performance. Your first style being a way of handling that, since you could record several times before having to call recalculatebalance(); but of course that leaves you with the original problem.

  • Henning Anderssen

    Josn,
    One way to solve that issue would be to send multiple payments in the parameter, such as a list of payments. That would allow you to add multiple payments while recalculating the balance just once.

  • Mark Allen

    I have a similar question as Josh. I aspire to building a strong domain model, but I have difficulty balancing that simplicity against complex requirements. Case in point, in our application the formula for calculating the fee is defined in the database and requires a parsing engine to evaluate its amount. It seems silly to try and load that type of functionality into the Fee class, as that class would bloat beyond maintainability. Where do capabilities like these live in the domain model?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Josh

    We could just as easily deducted the amount from the already-calculated balance, instead of recalculating it each time. The original problem is that it’s expensive to calculate entire swaths of balances, say listing all the customer’s fee’s balances. Or if you want to query on that information, it becomes quite difficult.

    @Mark

    In another app we had, all customer catalog prices were cached and pre-built, simply because it was too expensive to calculate up-front. We had a bounded context around catalog management, and could use events to notify when certain things changed. Individual prices could then be re-calculated, but now in a domain object format that was centered around price calculation.

    The big thing here is that the Fee object is self-consistent. We can give the Fee object things to help calculations, but ultimately we don’t want half-baked operations.

  • amir

    I feel customer.ChargeFee(100m) somehow is not really describe the scenario.

    I will prefer to change to

    Customer customer = new Customer();

    Shopper s = new Shopper();
    s.ChargeFeeTo(customer).Amount(100m);

    customer.MadePayment(50m);

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @amir

    So if I only call ChargeFeeTo(), and not Amount(), my code compiles, yet the operation is not complete. Can I charge a fee without an amount? Probably not, so I’d want to be explicit in my domain with that.

  • amir

    Ya i agree with you and actually beside using fluent method chain, we can explicit add amount parameter

    s.ChargeFeeTo(customer,amount);

    Customer will not charge their self, so we need to give this role to correct party. But we still need a method that receive command that reflect the customer state right. I will probably will have IsCharged(amount) in Customer

  • amir

    Sorry typo.. it is not Shopper .. but suppose to be ShopOwner

    ar customer = new Customer();
    customer.HasBeenCharged(100,shopOwner);

    or

    customer.HasBeenCharged(new CustomerChargeFees(100,shopOwner));

  • Joe

    Jimmy, Whatever happened to that awesome idea you had to use a magic character to replace spaces in your unit test names?

  • http://www.lostechies.com/members/bogardj/default.aspx bogardj

    @Joe

    It was a fun experiment that taught me a lot about the C# spec….but otherwise, it just looked wonky after a while.