I’d like to continue the topic of domain modeling trilemma. Just to re-iterate, the trilemma states that you can’t have all 3 of the following attributes:

  • Domain model completeness — When all the application’s domain logic is located in the domain layer, i.e. not fragmented.

  • Domain model purity — When the domain layer doesn’t have out-of-process dependencies.

  • Performance, which is defined by the presence of unnecessary calls to out-of-process dependencies.

You have 3 options here, but each of them only gives you 2 out of the 3 attributes:

  • Push all external reads and writes to the edges of a business operation — Preserves domain model completeness and purity but concedes performance.

  • Inject out-of-process dependencies into the domain model — Keeps performance and domain model completeness, but at the expense of domain model purity.

  • Split the decision-making process between the domain layer and controllers — Helps with both performance and domain model purity but concedes completeness. With this approach, you need to introduce decision-making points (business logic) in the controller.

Here’s this trilemma in one picture again:

[Enable Images]

In the article, I brought up the example of checking for user email uniqueness to illustrate this trilemma. There are 3 options of where you can implement this check:

  • In the controller and thus lose domain model completeness because the domain layer would no longer contain all the business rules (the email uniqueness check is one such rule).

  • In the User class and thus lose domain model purity because the domain layer would refer to the out-of-process dependency (the database).

  • Load all existing users into memory and pass them to User as an argument — This option keeps both domain model purity and completeness intact, but gives up application performance.

There were several interesting comments to that article that I’d like to share in this email.

You didn’t consider an option taking just a bool?

This seems like a good balance between completeness and purity:

// User domain class
public Result ChangeEmail(string newEmail, bool isEmailExisting)

That’s a good idea, and in this particular example, it brings back application performance with no downsides to domain model purity or completeness.

It doesn’t scale very well, though. If you have another similar requirement (say, we need to check for user name uniqueness too), then this approach will still lead to performance drawbacks. You will have to do 2 database roundtrips in order to get both the isEmailExisting and isUserNameExisting values, even when the first value is false.

Granted, this is not as bad as querying all users from the database, and it might be an acceptable compromise.

The best part about this option is that these checks are present in the signature of the ChangeEmail method, and thus it’s impossible to accidentally overlook them.

Another comment, similar to this one, suggests that you could query not all users but a list of one or two:

What about a method like GetByEmail() in the repository which returns a user to be then passed to the ChangeEmail() method? This will keep all three attributes to a maximum.

This would help too. And in fact, it’s even better than the Boolean flag, because it allows the User to check not only whether the user with the same email exists, but also whether it’s the same user as them:

// User
public Result ChangeEmail(string newEmail, User userByEmail)
{
    bool emailIsTaken =
        userByEmail.Email == newEmail && userByEmail != this);

    if (emailIsTaken)
        return Result.Failure("Email is already taken");

    Email = newEmail;

    return Result.Success();
}

Notice the userByEmail != this part here. This is helpful in scenarios when the customer tries to change the email to the same one. It’s also something that’s impossible to capture with just a Boolean isEmailExisting flag because you can’t know if the existing email belongs to the same user or not.

--Vlad


Enjoy this message? Here are more things you might like:

Consulting and training — I offer consulting and group training for organizations on Unit Testing and Domain-Driven Design. Reply to this email with the word "consult" to discuss.

Unit Testing Principles, Patterns and Practices — A book for people who already have some experience with unit testing and want to bring their skills to the next level.
Learn more »

My Pluralsight courses — The topics include Unit Testing, Domain-Driven Design, and more.
Learn more »