Domain Models with an Attitude

Domain Models with an Attitude

Domain models sit at the heart of business software systems and yet we often build models that are weak and devoid of any real value. This has severe consequences in the long run when we ultimately pay the price. We should instead create models that have substance and an attitude.

In this article I show how we should think about domain models and how to build meaningful domain models that capture domain knowledge, foster domain understanding, and are the building blocks of a robust software system.

The norm these days looks like this:

public class BankAccount 
{
    public string Id { get; set; }

    public decimal Balance { get; set }

    public string Currency { get; set; }
} 

This abomination is not worthy of being called a domain model for sure. This is what Martin Folwer calls anemic domain model. It does not serve any real purpose apart from being a container to pass data around. When we create such models, we are, many times unknowingly, forcing ourselves to push domain rules further up the stack into the so called services. Is it so ludicrous to think that domain logic actually belongs in the domain entity itself?

Having weak models results in:

  • Domain logic spilling into high-level components
  • Difficulty enforcing domain rules
  • Variations of domain rules
  • Service-driven design
  • Anemic domain model

How about we start doing something like this:

public class BankAccount
{
    public string Id { get; }

    public Money Balance { get; private set; }

    public BankAccount(string id, Money balance)
    {
        if(string.IsNullOrEmpty(id))
        {
            throw new ArgumentNullException(nameof(id));
        }

        this.Id = id;
        this.Balance = balance;
    }

    public void Deposit(Money depositAmount)
    {
        if(!this.Balance.HasSameCurrency(depositAmount))
        {
            throw new InvalidOperationException("Cannot deposit using different currency.")
        }

        this.Balance = this.Balance.Add(depositAmount);
    }

    public void Debit(Money debitAmount)
    {
        if (!this.Balance.HasSameCurrency(debitAmount))
        {
            throw new InvalidOperationException("Cannot debit using different currency.");
        }

        this.Balance = this.Balance.Subtract(debitAmount);
    }
}

This is what I call domain models with an attitude. This one will throw exceptions at you if you do not abide by the rules of the domain. It will tell you what operations you can do and how to do them. This model will not bend a knee even to Daenerys Targaryen. This is very different from the former one which will agree to whatever you make it do.

Your domain models should:

  • Capture domain rules
  • Enforce the rules of the domain
  • Speak the language of the domain
  • Capture both data and behavior

The topic of building great domain models is a lengthy one and the main discussion point of Domain-Driven Design (DDD) - an approach to building software that is worthy digging into. I have been exploring DDD for many years now and it has changed how I build software dramatically. I will be posting a lot of articles on DDD so stay tuned.

Learning to build sound domain models does not happen over night. It takes time and lot of trial and error so I would suggest to start small. Next time you are building a model, similar to the sample BankAccount one, keep the following items in mind:

  • Do not automatically provide public setters
  • Use a constructor to enforce creating a valid model
  • Use terms of your domain (For example, debit, credit, money, etc.)
  • Encapsulate domain operations in methods (like Debit, Credit, etc.)

As next steps, please feel free to take a look at the sample project at Github which features the full domain model. Also share with me your experience building models with an attitude if you have been doin that.
What is stopping you from building models with an attitude?

As always, looking forward to your comments. Thank you for your attention.

Published 14 Dec 2018

Building software and sharing knowledge.
Milan Nankov on Twitter