17

In DDD you should never let your entities enter an invalid state. That being said, how do you handle the validation of a unique constraint?

The creation of an entity is not a real problem. But let say you have an entity that must have a unique name and there is a thousand instances of this entity type - they are not in memory but stored in a database. Now let say you want to rename an instance.

You can't just use a setter... the object could enter an invalid state - you have to validate against the database.

How do you handle this scenario in a web environment?

W3Max
  • 3,328
  • 5
  • 35
  • 61

2 Answers2

21

A uniqueness constraint can be reduced to a persistence exception, rather than being seen as an "invalid state". It's not an invalid state until the object is persisted. Uniqueness only makes much sense in the context of persistence. Realistically, you can put this kind of rule in your validation mechanism to help reduce the likelihood of this error, but in any real multiuser system, you can't be guaranteed of uniqueness until a successful unit of work completes the persistence action.

So you may want this in your validation mechanism, but you must enforce it in your persistence layer.

I'm generally a fan of DDD as a methodology, but I think that the "don't allow objects to get into invalid states" can require some tortuous abstractions. In a web application, having a separate "View Model" is one possible solution, as an intermediary layer before persistence, but I don't usually do that until I'm convinced it will cause me less pain than the simpler alternative.

JasonTrue
  • 19,244
  • 4
  • 34
  • 61
  • Thank you for your answer. I pretty much agree with what you say but I would like to hear what the purists think of "a uniqueness constraint can be reduced to a persistence exception" - not being an invalid state... And for the View Model, I almost always use it but there is a moment you will have to update your "real" model with the data from the View Model and you will still face the same problem. – W3Max Apr 18 '10 at 02:32
  • 1
    DDD isn't quite a religion; it's not really about purity, it's about clarity. To the extent that it is about purity, you'll find that my thinking tends to line up pretty close to canonical DDD most of the time. If you do want to look at solving uniqueness outside of the persistence layer, though, you might try tweaking the Specification pattern to fit your needs. – JasonTrue Apr 18 '10 at 03:29
  • Well im not sure I fully agree with the answer here. I don't think you should rely on the persistence layer for enforcing the business rule. That would spread the domain logic over to the persistence layer. Instead, consider doing the uniqueness check as part of your use case. There are uniqueness constraints that can't be enforced on the persistence layer. For example, validating "a department can only have 2 managers" can't be done on the persistence layer. – alaboudi Mar 06 '21 at 03:40
  • As noted above, it's possible that you may want to validate uniqueness in the domain layer, but unless you have some atypical contortions in your domain layer to break session boundaries, even your counter-example can't be safely enforced in a multiuser/multinode system on the domain layer alone. Also it is possible to enforce your example in the database layer. You can use a CHECK or deferrable constraint on most decent relational databases, or a TRIGGER on less capable ones; https://www.postgresqltutorial.com/postgresql-check-constraint/ – JasonTrue Mar 07 '21 at 02:00
3

In search for an answer to your question I came across this article: https://thinkbeforecoding.com/post/2009/10/28/Uniqueness-validation-in-CQRS-Architecture.

In essence: Look for the scope of the uniqueness and store an authoritative list of unique values inside an aggregate root representing that scope.

For example, given the use case "when registering a new user, the requested login must be unique" this would be: A login has to be unique whithin a tenant (or company, or country, ...). Store the information about given logins within a list in the aggregate root representing the tenant.

This way you can make sure, that the tenant never enters an invalid state.

  • 8
    While this does indeed solve the issue, what if you have millions of users under in a single aggregate? It just isn't efficient then, no matter how elegant. – Greyshack Jul 19 '19 at 08:38
  • 1
    @Greyshack while that might be true, it might be an indication that your aggregates are not correctly designed. With really "millions" of users you will have to resort to eventual consistency anyway. – istepaniuk Oct 16 '20 at 19:38