The Trouble with the Always-Valid Entity

There is no better way than to kick off a new blog with a bit of controversy. When it comes to Domain-Driven Design, there is no greater divergence in interpretation and opinion than what is meant by the “always-valid entity” and how it is actually implemented. Even amongst the consummate experts, how validity and validation are put into practice can vary greatly. Over the years, I have slowly drifted between the different camps, finding that one interpretation better fit one project than the next. However, I now find myself wondering if the solution is not really as black-and-white as it has been presented.

Defining “Always” in a DDD World

The largest difficulty with the “Always-Valid Entity” paradigm is not the aspect of the validity, but the interpretation of “always”.  It implies a level of infallibility which, at the actual point of transition, cannot logically be true.  Yet, even today, you do not need to look far to see code that looks like this:

Ignoring that there is simply a thrown exception being used as a stand-in for actual validation, what exactly is wrong with this approach?  There are two egregious issues here.  First of all, this pattern will rapidly lead down a path of Anemia.  There is an immediate code smell here, and it is a direct result of this “Entity” having no behavior.  Ultimately, this class is nothing more than a Data Transfer Object which arbitrarily will throw exceptions when it is not used as expected.  It is a very developer-centric solution, rather than a domain-centric solution.

Secondly, the property has bounded the operation to the actual setter operation.  What this means is that every single state transition for this entity has been limited to being scoped within the property.  By its own definition, a property describes an attribute of an object.  Here, though, it is being used for describing an attribute and removing the ability to participate in any sort of broader purpose; the property has become an atomic state transition.  It is painful to model anything but the simplest of entities with this approach.  Instead of only a User Name being required, what if both a User Name and Password are both required?  That is a very realistic expectation and following the approach above the entity could never be in a valid state, at all, since they would be set linearly, requiring two atomic operations to achieve a single transition.

No matter how trivial, for the remainder of this exercise, it would be beneficial to describe the Entity in our Bounded Context in the way that it would be described by a Domain Expert:

  • An Account consists of a User Name and a Password.
  • The User Name and Password are required.
  • When changing the User Name, a valid User Name is required.
  • When changing the Password, a valid Password is required.

While overly-simplified, we have a definition for an Account.  It is the Aggregate Root, although there are no other Entities or Value Objects to fill out the graph, just yet.  The attributes that define an Account are that it has a User Name and a Password, and that both of those attributes are required.  Additionally, an Account has two defined behaviors, which are that the User Name can be changed and that the Password can be changed.  This partially matches the example above, with the exception of having explicitly captured some of the behaviors.

Improving on the first example, slightly, the next pattern limits the ability of the caller to manipulate the properties, and exposes both an established creational pattern and explicit behavior, as follows:

This approach is better.  Now, the properties cannot be arbitrarily changed, and there are exposed behaviors in ChangeUsername() and ChangePassword().  There are also two means of setting these properties, being through the expressed behavior of the Entity, or through the construction of the Entity.  So, where is the problem now?

The problem is with the constructor, itself.  Again, in a simple Entity example, the problem may not be outwardly apparent.  But what were to happen if an Account entity required a User Name and then either a Password or a numeric Pin?  At that point, the constructor would need to change to support the operation, either through the inclusion of optional parameters or through constructor overloads.  There is nothing semantically incorrect in doing that, but the Account is now required to express those constraints in both the behaviors that it exposes and through one or more constructors.  This will eventually lead to either large constructor signatures or countless overloads, along with continuing complexity in the bodies of those constructors.  There has to be a better way.

Building a Better “Always”

Constructors, by their nature, are meant to construct instances of a class.  That, however, has no direct relevance to its role in the Ubiquitous Language.  At best, a constructor is an implementation detail.  Too often, constructors are simply used to seed data, when they have more intrinsic value in providing system-awareness and configuration.  This is clearly demonstrated through Dependency Injection, where a class understands that it requires more information on how it operates within its specific scope and that additional information will be provided at a time and within a context deemed appropriate.

Keeping that in mind, and adding the newly-introduced concept of a Pin to the Account, the amended Ubiquitous Language around an Account may read a little differently:

  • An Account consists of a required User Name and either a required Password or a ranged, four-digit Pin between 1000 and 9999.
  • An Account can be created with a User Name and Password.
  • An Account can be created with a User Name and Pin.
  • When changing the User Name, a valid User Name is required.
  • When changing or setting the Password, a valid Password is required and any existing Pin will be removed.
  • When changing or setting the Pin, a valid Pin is required and any existing Password will be removed.

Not only has the concept of a Pin been introduced, the behavior of “creation” has also been addressed.  Previously, creation was implied, much like the implementation had demonstrated.  To a degree, it could also be taken to mean that there was a shortcoming in the Ubiquitous Language, at that point, as there is an implied need to actual create an Account.  Now, there are two explicitly defined creational scenarios, being a declarative behavior of creating an Account with both a User Name and Password or with a User Name and Pin.  Additionally, it is clear that the attributes of the Account can be changed after it has been created.  It is a simple example, but one which is relatable and described in a way that anyone can understand, which is a required goal of the modelling exercise.

In the spirit of iterative change, now consider the following example:

This implementation of Account meets the requirements of the Ubiquitous Language, as it has been defined up to the current point.  The concept of implied “creation” is removed from the implementation, through the use of a private constructor.  This means that there are now only two paths to create an Account.  First, being through CreateAccountWithPassword() and, secondly, CreateAccountWithPin().  Both of these methods are declared as static, and leverage the internal constructor to create a definitive instance that is bounded within the atomic operation.  Of primary importance, the only parts of the class that are publicly scoped are the class, itself, along with its getters and the behavioral methods.  Every decision made in during the development of this simple Entity was done to for the sole purpose of expressing the Ubiquitous Language with a minimal amount of implementation detail.  With this simple approach, the Entity will never end up in an invalid state.  If the assumption can be made that the Account is in a valid state before an atomic operation, and that the payload passed into that operation is valid, it can be deduced (with a high level of accuracy) that the resultant Account will also be in a valid state after that payload is applied in the context of that operation.  This is true, because each operation on the Account exhibits an expressed behavior of the domain and are only applied if the expectations are met.

While it was all done without requiring a heavy hand, the trade-off is that the code is more verbose.  However, it is more verbose without becoming complex or requiring any sort of special knowledge of how to use it.  This solution scales very well without growing in complexity.  In keeping the methods short and concise, they are easy to understand for developers of any skill-level.  Additionally, any developer can show this code to any technical or non-technical member of the team and that person will recognize the Ubiquitous Language and how it is expressed.  Too often, code is written for the benefit of the developer, whether that is for time-to-market or sheer laziness.  Seeing as Domain-Driven Design relies on self-documentation, this should be captured in code as much as possible.  It extends beyond comments or code organization, and it becomes beneficial to take a moment during development to answer one simple question.  “Could I explain this to our Domain Expert in terms of the Domain, with a minimal amount of technical interference?”  For core Domain concepts, the answer should always be an affirmative one.

Improving “Always”

Is this the correct answer to “always”?  That is a loaded question.  Contextually, it is correct for the situation that is trying to be addressed.  That being said, it might not fit the requirements or tastes of every team.  That is the ultimate point, really.  We, as developers, are on a constant quest for “the” correct answer.  Nowhere is there a requirement that there can only be a single solution for a given requirement.  Too much focus is put on what is “more” correct, using edge cases to define normalcy.  Stepping back, it is important to realize that those edge cases describe exceptional circumstances and do not always represent the path taken to find one of the multiple correct answers.  What is listed within this article is a just one potentially correct solution that meets the needs of addressing a single concern.  That concern is:  “Given a majority of standard domain requirements, establish a solution that will work with a majority of standard usage scenarios while conveying that implementation in terms of the Ubiquitous Language.”  Will there be times that this approach does not fit?  There most certainly will be.  When those appear, they will be dealt with in the same fashion and, as much as possible, be standardized so that the technical debt taken on to address it does not become a recurring one.

Since this is merely an example, there is still a lot to be desired.  In future posts, this model will be expanded on to support other concepts, including:

  • Mapping Persistence to Entities.  One thing that is not readily apparent is how persistence fits in to this model, but it does.  There are a couple of different approaches, in fact, that can be implemented to allow the persistence mechanism to create valid instances from whatever data source is being used.  The best part is that it can be accomplished without allowing unfettered creation of Invariants in doing so.  There will be more trade-offs involved, but those will be equally as easy to live with.
  • Validation Everywhere.  The “validation” shown in these samples are hardly validation at all.  Realistically, what is shown here has just been Guards to enforce the simple requirements that were established.  This model readily supports command or method-level validation within the domain, and will be equally as capable of participating in persistence validation outside of the domain.  There is no marriage to any validation approach, yet, because that is dependent upon other implementation details within the domain.  Not only how validations are performed, but also how the results are reported, will be a very important determination in finalizing that approach.  Again, however, nothing established here will prevent validation from happening everywhere that it needs to.
  • Domain Events.  While this sample focuses on isolated change within an Aggregate or objects within its graph, it is also ready for Domain Events.  In fact, it is in a great position for Domain Events.  The only place that they will be raised from is within a method, and those methods currently all adhere to the Single Responsibility Principle.  An argument can be made that having the Create…() methods invoke other methods violate that, and it would be a valid argument.  With a bit of actual validation in place, though, the code samples would not be depending on the Guards which are what, is ultimately, being reused.  Each method could easily be made to be self-contained at those price of just slightly more verbosity.

Entities and Aggregates are an interesting beast.  Eric Evans has gone on record as stating that he wished that he had only included them as an appendix in his book, because people are putting too much emphasis on them.  On the one hand, his implication would seem to be that people are focusing on the implementation ahead of the actual aspect of modelling.  The challenge, however, is that an improperly implemented Entity can unintentionally alter the intent of Domain Model in ways entirely different from being improperly modeled.

In our next installment, we will take a step back to look at how to structure our projects to come up with a standardized structure that expresses the domain, infrastructure, and application concerns.  As much as possible, we will look for opportunities to leverage best practices that can be shared over multiple projects.  Done correctly, a majority of the scaffolding should be a one-time investment, with subtle adjustments made over time.