Domain Signature Concerns

As I am beginning to use DDD in more of my projects, a few things have stood out. Based on these things, it may be that my most recent project isn’t well suited for it. I’d like to share my though on it here and see how your own opinions compare so I can make an intelligent decision on my latest project.

In most cases, an application well suited for DDD has entities with readily identifiable, non-volatile, unique identifiers. Examples of this would be a employee id such as “SR123” or an order id of “20110131A”. These kinds of values remain constant throughout the lifetime of the entity, even though other properties, such as First and Last names, may change.

In DDD, entities with these kinds of id’s are coded to received their values in the constructor, and exposed in protected set properties to avoid accidental change.

This is the accepted way to handle things.

But what happens when you take on a large project, and choose to use DDD with it, then discover during the unit testing phase that very few of the numerous entities actually require such a value?

Allow me to pull up an entity from my old forum project as an example:

using System;
using System.Collections.Generic;
namespace Genesis.Domain
{
    using Enterprise.Foundation;
    using Enterprise.Foundation.Domain;
    public class Reply : Entity
    {
        private IList<Reply> replies = new List<Reply>();
        public virtual string Title { get; set; }
        public virtual string Keywords { get; set; }
        public virtual string RawMessage { get; set; }
        public virtual string ParsedMessage { get; set; }
        public virtual string HostAddress { get; set; }
        public virtual DateTime PostedOn { get; set; }
        public virtual bool IsApproved { get; set; }
        public virtual bool IsRemoved { get; set; }
        public virtual User User { get; set; }
        public virtual IEnumerable<Reply> Replies { get { return replies; } }
        public virtual void AddReply(Reply reply)
        {
            Logic.Check(reply == null, "Reply is null!");
            Logic.Check(reply == this, "Reply is self!");
            if (!replies.Contains(reply))
                replies.Add(reply);
        }
        public virtual void RemoveReply(Reply reply)
        {
            Logic.Check(reply == null, "Reply is null!");
            Logic.Check(reply == this, "Reply is self!");
            if (replies.Contains(reply))
                replies.Remove(reply);
        }
        public Reply() { }
    }
}

This entity represented a reply to either a Thread or another Reply. Here are my observations on this example:

Ignoring any unique constraints that might eventually exist in the database (we are being db agnostic at this point), there simply isn’t anything that makes this entity unique accept the Id property in the base Entity class. Even the Title property is volatile, and may even be null. If the posting user is deleted, it needs to be replaced with the GuestUser.

HostAddress and PostedOn, however, have my interest here. They could have potentially been used are a compound domain signature, as they probably won’t change. But they could. The import and export facilities for example can change these based on options selected. They may also be changed by an administrator in special cases.

So here is the question I put before you:

Is the example above still a valid example of DDD? If not, is there something that could have been done to it to make it so?

Use any DDD concept that makes sense to you in any application, it’s not a dogma, take what you need and use it.

Of course. =)

However, I was hoping to get more actual input in order to make a better decision regarding “what makes sense”.

Currently, my equals implementation tests the following for entities:

if (GetType() != other.GetType()) return false;
if (ReferenceEquals(other, null)) return false;
if (ReferenceEquals(this, other)) return true;
if (!IsTransient() && !other.IsTransient() && Equals(Id, other.Id)) return true;
if ((IsTransient() && !other.IsTransient()) || (!IsTransient() && other.IsTransient())) return false;

If it makes it past this, I compare all properties with a signature attribute. The problem is, more often then not, not all required properties are present.

Consider a moment the idea of nested categories. The unique id might consist of Title and ParentId. But if your design dictates that you not include back references, only title is available. Alone, that won’t work because a given query might return mixed results which include categories from many levels with the same title.

It would seem to me that when it comes to persisted entities, the only field we need worry about is the Id.

It just makes me wonder if using Signature attributes at all is simply counter-productive. They seem to be designed to approximate database constraints, in which case, we’ve introduced a database concern where it should’nt be.

Thoughts?

This is a good read

Indeed, I recall reading that very post about a year ago, but I still have the following issue.

Let’s take the example of an online store like Amazon, that sells literally thousand of things every day. Each product has a unique code that never changes. Now, they may wish to move products they no longer sell to a separate DiscontinuedProduct table in order to make table scans faster for the AvailableProducts table. In such a case, the database assigned Id would probably change, but the ProductCode would not.

Before you comment on the above consider my base entity classes (stripped down of course):


public abstract class Entity<T>
{
public virtual T Id { get; protected set; }
}
 
// and for the most common use
 
public abstract class Entity : Entity<int> { }

If both AvailableProduct and DiscontinuedProduct derive from Entity, they each have a numeric Id, but they’ll be different. This would preclude basing any comparisons on Id. Logically though, since they are of different types, they shouldn’t be equal anyway.

So then I get the idea that maybe these entities should Derive from Entity<string> and don’t even use auto-incremented id’s.

Now here’s the real question…

What do you do if, while designing the entities, you discover that there simply isn’t anything that uniquely identifies them?

Back to my Blog category example.

Disregarding auto-incremented integer id for the moment, a category is made unique from the combination of it’s title and it’s parent category. By personal convention, I’d rather not have back-references, so part of the comparison is missing. At this point, the only thing unique about it is the primary key, since title can be changed. What this means is I wind up with a parameterless constructor.

If this happens on the majority of the entities, it just doesn’t seem real domain-like. I am wondering if using auto-increment id’s are a bad thing to start with. Perhaps using a Guid is in order. This way, I can force some sort of domain identity that would remain constant, even if the record is moved to a clone table?


// Category : Entity<Guid>
 
Guid childId = Guid.NewGuid();
 
Category child = new Category(childId)
{
Title = title,
Description = description
};
 
parent.AddCategory(child);

Opinions?

If both AvailableProduct and DiscontinuedProduct derive from Entity, they each have a numeric Id, but they’ll be different. This would preclude basing any comparisons on Id. Logically though, since they are of different types, they shouldn’t be equal anyway.

First I’d have only one Entity identified by ProductCode (the DB doesn’t exist remember ? :slight_smile: ) and simply a property IsDiscontinued . ProductCode itself is a value object.

About the blog category, the “c#” category at level 1 is the same as the “c#” category at level 4? What if I rename a category from “asp.net” to “c#”, do I want to treat both the categories as different or as the same?

Well, on your first point, in a small setting I would probably have two entities mapped to the same table using a discriminator. In the example above, the tables would get really large if old stuff was left there. That aside…

I think you got my problem. Consider the following:

TopCat1

  • SubCat1
    – SubSubCat1
  • SubCat2
    – SubSubCat1

The two with title SubSubCat1 are different entities because they have different owners. Now consider this:

var results = categoryRepository.GetWhereTitleContains(“SubSub”);

Results will have both entities in it, but since they contain no back reference to parent, I can’t accurately compare them. Based on title alone, they are the same, but this isn’t true. This only leaves the Id.

The problem here is that should the need arise to purge the main table due to size, and move older items to a separate table, the Id changes. If I were to use a guid, it won’t. So if for some unforseen reason, the same item was in both tables, it could still be compared and detected.

Does this make sense?