Home > Various > Ubiquitous language: what is so blindingly obvious you can’t see it?

Ubiquitous language: what is so blindingly obvious you can’t see it?

Ubiquitous Language is the term Eric Evans uses in Domain Driven Design for the practice of building up a common, rigorous language between developers and users. This language should be based on the Domain Model used in the software – hence the need for it to be rigorous, since software doesn’t cope well with ambiguity.
Martin Fowler

Abstract

In which the author outlines the basics of the ‘Ledger Pattern’, an addition to the ubiquitous language and domain model to help facilitate a more natural approach to DDD and OO. It is specifically designed to deal with issues of validation and object creation that occur during object instantiation.

Concrete

Some things are obvious.  Some things are very obvious. And some things are so obvious that you actually need to take a step back in order to see them.  Udi Dahan helped me take that step back some time ago with his article ‘Don’t create aggregate roots‘.  While I agree with him on most of what he details in his post, I feel that something is missing.

What Udi points out is that we should never create instances of domain objects from the service layer (or command handler layer, whatever the layer is that coördinates calls to domain objects). Instead, it is the responsibility of an aggregate root to create a new entity that belongs to his aggregate.  In some cases, this is obvious:

public class Order {   public void AddOrderLine(OrderLine line)   {     orderLines.Add(line);   }   public void AddOrderLine(Product product, int quantity)   {     orderLines.Add(new OrderLine(product, quantity));   } }

The latter method is actually the better: it is very easy to perform validation on the parameters passed before the orderline object is constructed.  This is closely linked to something I blogged on before: domain object validation is done on a context basis.

And even if you believe that validation can be done, consider the example where we create a customer

public void Handle(CreateCustomerCommand cmd) {   ICustomerRepository repo = RepositoryFactory.Get();   Customer customer = new Customer(cmd.FirstName, cmd.LastName);   repo.Save(customer); }

Where is the validation code now? Is it in the constructor? If so, what if we need different validation logic? What if our application goes from being an application used in a store to a web application? Now a customer comes through your website and you need to ensure that an email-address is supplied. We have to face facts: this solution is not adequate.

What we need is a domain concept that fits with the ‘this object can add customers’. So we use our common sense: in the store, the Employee-object can add a user. But who adds employees? Well, that would be the store owner. But where does he come from?

Back up a minute. In the past few sentences I’ve introduced a whole slew of new terms into the ubiquitous language. All of these need to be modeled, coded, tested and used. And what have we really achieved? Nothing much. No matter how far up the chain you push things, at some point you will run into a simple truth: you need an object that is not created and stored in the normal run of the system but that has been present from the start. Therefore I propose the following guideline:

For every aggregate root that is not the entity of any other aggregate root that can act as its creator, define an artificial aggregate root.

I call this artificial root a ledger, because it performs a function similar to an  order or purchase ledger.  Our customers are prime candidates for this: unless the ubiquitous language explicitly calls for some other object to create customers our CustomerLedger takes care of creating and deleting customers.  Note that all these ledgers to is provide a place to create and delete instances, updates are still processed by the actual domain objects.

Ledgers are also a prime place for validation: if a customer must have a first and last name, the ledger validates this. If our customers are now registering through the website the ledger can be extended to handle the adding of customers with the associated required validation of email addresses.

The odd thing about legders is that they are aggregate roots (and therefore entities) that have no identity*. All instances of a ledger class are equal to each other (disregarding lazy-loading and the likes) because they always contain the same data.

Here is a code example detailing what a ledger looks like.  This uses my implementation of the Notification Pattern to let the repository check if the object is in a valid state and to allow it to report this to the presentation layer.

public class CustomerLedger {   private IList errorList;   public CustomerLedger()   {     errorList = new List();   }   public IList Customers { get; set; }   public ICommandResult AddCustomer(String firstName, String lastName)   {     Validate.IsNotNullOrEmpty(firstName, "First name null or empty", errorList);     Validate.IsNotNullOrEmpty(lastName, "Last name null or empty", errorList);     if(errorList.Count == 0)       Customers.Add(new Customer(firstName, lastName);     return new CommandResult(errorList);   } }

This code is seemingly not very different from regular aggregate root code, and rightly so!  The only actual difference is the way the object is treated from the repository.

public interface ICustomerLedgerRepository {   CustomerLedger Get();   ICommandResult Save(CustomerLedger ledger); }

No id’s, no fancy lookups: there is no difference between the instances*.

Conclusion

The ledger concept is quite confusing at first, and it is a certain risk to implement: it can be confusing to users, domain experts and programmers alike. To introduce the concept into the ubiquitous language is to soil this language with something that is inherently a technical concern, but at the same time a valid domain problem. Everything that enters your system has an origin, and you need to model this origin up to a certain point.If you talk in terms of roles rather than objects it is a lot easier to introduce: the concept of a ‘CustomerAdder’ makes sense: your system needs something that adds customers.

Technically, this can not be considered a design pattern. But I’m going to call it the ‘Ledger Pattern‘ anyway. It’s either that or calling it ‘the concept formerly known as the artifical aggregate root for aggregate roots that lack natural aggregate roots in the ubiquitous language’. Let’s see you type that ten times in a heated mailing list discussion.

* All my ledgers actually do have an identity to facilitate ORM mapping and retrieval. This means that every aggregate root that is managed by a ledger also has a foreign key that points to this ledger. My database contains a ledger-table which contains an id and the name of the legder.

Categories: Various Tags:
  1. March 12th, 2010 at 09:58 | #1

    I want to thank the blogger very much not only for this post but also for his all previous efforts. I found jdt.toron.be to be greatly interesting. I will be coming back to jdt.toron.be for more information.

  1. No trackbacks yet.