5

Looking into DDD and something I noticed is that business logic should be in the model, otherwise you just have property bags. That said how do you handle pieces of validation that require a trip to the database?

For example, you have an domain object that represent categories for a blogging engine.

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
}

From a developer's perspective, you could create an empty category, but this isn't valid in the business world. A Category requires a Name to be a valid category, therefore we end up with:

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Category(string name)
    {
        Name = name;
    }
}

Now, a Category can't be created without a name. But, we can't have duplicate Category names either, that would just cause confusing and redundancy. I don't want the entity to handle it themselves because this would end up more of an Active Record Pattern and I don't want the model to be "self-aware/persisting" so to speak.

So to this point, where should this validation take place. It is important that if the user types an duplicate Category, we would notify the user of this. Should there be a "service" layer that takes a UI model and turns it into the domain model and attempts to validate it, pushing errors back to the UI, or should there be something closer to the data layer that handles this?

Someone please set me straight because I really like where DDD is going, I just can't make all the connections to get there myself.

  • 3
    DDD is a design methodology, not a coding methodology. The only place where you can do this reliably is in the database, since that is the only part of the system that has awareness of all of the transactions that have taken place for every user. – Robert Harvey Oct 8 '14 at 22:14
  • @RobertHarvey Does DDD prescribe some coding methodology? For example, Anemic Domain Models. This is saying that the Domain models should contain behavior (not saying this isn't just a natural part of OOP, but it seems with all the design patterns, sometimes we just lose track). It is telling me code for behavior belongs in the Domain model. Surely, somewhere DDD states certain concerns should be handled at a certain "layer". – Justin Oct 9 '14 at 13:08
  • If the design methodology/coding methodology you're using isn't doing what you want, why are you using it? – Ampt Oct 9 '14 at 15:31
  • @Ampt It isn't that it isn't doing what I want, just where is the best place to handle this and does DDD prescribe a place, or the respective user's experience, that meshes better than placing it somewhere else. Is there a standard for placement, so to speak. If you looked at a project using DDD, where would YOU expect to find it with your experiences using DDD. This makes it more opinionated, but input from others is important for this if one isn't roughly outlined in DDD "specs". – Justin Oct 9 '14 at 15:38
  • 1
    In my experience, DDD is more about providing software developers and business stakeholders a common language by which they can describe business domains, than it is about rigidly dictating where every possible software artifact goes. – Robert Harvey Oct 9 '14 at 15:46
4

I would create a domain service responsible for validating the uniqueness of the category. In my solution domain defines the interface of the service, infrastructural part implements it using a database.

Please also take a look at https://stackoverflow.com/questions/516615/validation-in-a-domain-driven-design.

  • 3
    It probably makes more sense to have validation occur as part of the save process for the new category. Having a domain service whose sole responsibility is verifying the uniqueness of the new category only guarantees the new category is unique at the time you checked it; the save operation can still fail if some other user created the same category in the meantime (unlikely as that scenario may be, it's still possible). – Robert Harvey Oct 9 '14 at 22:16
3

business logic should be in the model, otherwise you just have property bags

What is business logic?
Business logic is, that your object knows it's state and could answer questions like order.IsDistributed or order.IsQualityAssuranceDone or the like. It is no business logic, if you ask the question is this order already in the database. That is a different responsibility: It belongs not to the order itself, since it isn't a state of the order, it is the state of the database. Therefore you should ask the database for that answer.

From a developer's perspective, you could create an empty category, but this isn't valid in the business world

To maintain a clean state, you should use properties (in C#) / getter and setter, where you implement validation-logic, to keep the object's state clean and tidy.

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }

    public Category(string name)
    {
        Name = name;
    }
}

This design has a big flaw: It mirrors not your requirement exactly of having a name, since the constructor could be called with null, which is not in every sense a valid name. On the other hand, you could use the builder pattern and do the construction via a builder-interface.

Now, a Category can't be created without a name. But, we can't have duplicate Category names either, that would just cause confusing and redundancy.

These are two different concerns. One belongs to the object's state the other to the database's state.

Should there be a "service" layer that takes a UI model and turns it into the domain model and attempts to validate it, pushing errors back to the UI, or should there be something closer to the data layer that handles this?

Whether you use a service-layer is up to you. Sometimes it is drowning little puppies to make the indirection. It depends on how much logic is there.

2

Q:How do we create objects? A: Using Domain Factories!

Since the creation of a category contains complex logic (we need to validate uniqueness), we will utilize a domain factory to create the object for us.

Inside that factory I'll use a UniqueCategoryNameValidator service that calls into the data-store (a DB in ur case) and check if the category is unique. Some people might refer to this approach as using the specification pattern as follows:

UniqueCategoryNameSpecification.isSatisfiedBy(category); 

Using a specification makes the check for uniqueness explicit in your domain. The specification would use an infrastructure service to query the database.

1

What I do is that I build my subjects (subordinates, objects within a domain) in such a way that they can never be instantiated by anyone other than the domain itself. (The main or central domain object.) This is in line with best practices: never use the new operator to instantiate objects that do not belong to you, (objects from other packages/namespaces/assemblies,) always invoke factories to do that. The domain is in a position of using new to instantiate its own subjects, because they belong to it.

Instantiation of subjects only by the domain can be enforced by declaring their constructors as package-private, (assembly-internal,) or, better yet, by declaring the subject classes themselves as package-private, (assembly-internal,) and having them implement public interfaces, (interfaces exposed by the domain,) so that the outside world cannot even think of instantiating them.

So, since subjects can only be instantiated by the domain, it is very easy to have the domain perform the necessary validation when creating them, as another answer suggested, so as to ensure that the subjects are valid right from the start.

However, this does not completely solve the problem, because you may later want to rename a subject, (store a different name in it,) in which case you must again be prevented from giving it a name which is already taken by a different subject. Clearly, access to the database is needed at all times, in order to provide a complete service.

So, the solution that I have found for this is that every subject knows its domain.

The domain is always passed as the first parameter to the constructor of every subject. Every single instantiation of a subject by the domain looks like this: new Subject( this, ... ). In a sense, the role played by the domain in domain-driven programming parallels the role played by the object in object-oriented programming: Just as every method accepts as a hidden first parameter the this pointer, which points to the object, so does every subject accept as its first constructor parameter the domain pointer, which points to the domain.

Thus, each and every subject is perfectly capable of performing whatever validation is necessary, by invoking the domain to do its validation.

If you do not want your subjects to be tied to the domain, then your subjects may be thought of as having an extra state, indicating whether they are at any given moment parts of a domain or not. (persisted or not, equivalent to the "attached/detached" state of Hibernate.) So, an object may begin its life as not part of the domain, (not persisted, detached in Hibernate parlance,) in which case it cannot perform any validation, and later it may become part of the domain, (persisted, attached in Hibernate parlance,) at which point it receives a reference to the domain, and it may do whatever validation is necessary. Later you may even remove it from the domain, (detach it,) at which point you would presumably nullify the domain pointer and the object would again be unable to perform validation.

1

we can't have duplicate Category names either, that would just cause confusing and redundancy.

Assuming that you have a business invariant that requires no duplicate category names, then the responsibility for enforcing that invariant belongs in some aggregate. Since enforcing the rule requires having access to all of the category names currently in use, that same aggregate must include, as part of its state, that collection of names.

This is basic aggregate construction - the consistency check and the state go together.

where should this validation take place?

In the reserveName() command of the aggregate.

Should there be a "service" layer that takes a UI model and turns it into the domain model and attempts to validate it?

No, there shouldn't be anything unusual here. Client wants to use a new name, so it sends a representation of the ReserveName command to the application. The application rehydrates the command, locates the appropriate command handler, and dispatches the command to the handler. The handler loads the aggregate, and invokes the reserveName() method using the arguments supplied. This exactly follows the same pattern as every other command you dispatch to the domain model.

If the aggregate discovers that the category name is already in use, then it throws a domain exception, and your application reports it to the client; again, exactly like every other command routed to the domain model.

Which aggregate is "the aggregate"? Beats me -- you need to look in the ubiquitous language to discover the aggregate that enforces this invariant. For instance, there might be an aggregate that has this responsibility and nothing else. In a toy example like this one, you might pretend that CategoryNameReservationBook is something that makes sense to the business. Or perhaps NameReservationBook, with one NRB instance being used for category names, and another NRB instance used for blog post titles, or some such thing.

When you discuss this with your domain experts, you may find that "no duplicate categories" isn't really a hard constraint. In , it is important to hear the difference between "that must never happen" and "that shouldn't happen"; the latter phrasing implies that it does happen occasionally, and if so there's likely to already be a process for that contingency.

You may also find, in the discussion, that this isn't a business constraint at all; in which cast you get it out of the domain model completely -- if it's something real, maybe the application handles is alone, or maybe there is some other domain model (perhaps in another bounded context) that is responsible for it.

Trying to support this constraint with services and the like doesn't really work; the service running in this transaction can't see what's being changed in other transactions, so you get a data race. You have the same problem trying to use a specification -- the data used to initialize the specification is stale, and another transaction can be running here.

Domain models with race conditions are inherently suspicious anyway; if two users working on separate tasks are blocking each other, then something is wrong. Udi Dahan went so far as to write that race conditions don't exist -- that in the business world, the conflict isn't prevented, so much as it is detected and mitigated.

So check, and double check, and triple check that maybe the uniqueness constraint isn't all that, as far as the domain experts are concerned. But if it really is a business rule that there be no duplicates, then it follows, unconditionally, that there is an aggregate responsible for keeping track of that.

Upon further consideration, a uniqueness problem like this may be scoped; the programmer's blog has a category named , and the health blog has a category named , but they aren't the same thing at all. This could be supported by giving each blog its own reservation book, but it may also be a hint that Category merely an entity, subordinate to some other aggregate.

0

Why don't you create a service holding a collection of all categories? Then you can do validation very easy. An example:

class BlogCategoryService 
{
   private List<Category> _categories = new List<Category>();

   public void AddCategory(string categoryName) 
   {
       if (_categories.Any(c => c.Name == categoryName)) 
           throw new DuplicateCategoryException(categoryName);

       var newId = ...

       _categories.Add(new Category(newId, categoryName));
   }
   ...
}

Do the name validation in your Category class:

public class Category
{
    public int Id { get; private set; }
    public string Name { get; private set; }

    public Category(int id, string name)
    {
        ValidateName(name);

        Id = id;
        Name = name;
    }

    private void ValidateName(string name)
    {
       if (string.IsNullOrWhitespace(categoryName))
           throw new ArgumentException('Category name may not be null or whitespace.');
    }

}

If you want to change name on a category, you could depend on your service:

public class Category
{
    ...

    public void ChangeName(BlogCategoryService service, string newName)
    {
        if (service == null)
            throw new ArgumentNullException('service');

        ValidateName(newName);

        if (!service.IsUniqueCategoryName(newName))
            throw new DuplicateCategoryException(newName);

        Name = newName;
    }
}

public class BlogCategoryService 
{

    ...
    public bool IsUniqueCategoryName(string name)
    {
        return !_categories.Any(c => c.Name == name);
    }
}

Also add a unique constraint on the name column of the blog category table to to guard against someone making changes outside of your BlogCategoryService.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.