Site icon Fanzoo Technology, Inc.

Static Standard Types as Entities and Value Objects

In a previous post I wrote about so-called Static Entities. I defined Static Entities as,

[S]tatuses and types […] usually established at the beginning of the design as core indicators of business logic; implemented as immutables and not managed by the client through any kind of [user] interface. In other words, they are hard-coded. However, we do still want to store these statuses and types in our database to make our queries more efficient. This compels us to implement these as [E]ntities as opposed to [E]nums.

I went on to show how you could implement Entities with application defined identities and trick the ORM into ignoring them. This was and still is an acceptable solution, albeit fraught with pain managing the ORM (at least in the case of Entity Framework Core). With that said, I no longer believe this is the best solution in most cases. In the year since writing that post and actually using the technique in a real-world project my thinking on this topic has evolved. Due, in large part, to having re-read Vaughn Vernon‘s seminal work on Domain-Driven Design (DDD), Implementing Domain-Driven Design.

Standard Types

In his book, Vernon defines these statuses and types as Standard Types. He describes them as,

[D]escriptive objects that indicate the types of things. There is the thing (Entity) or description (Value) itself, and there are also the Standard Types to distinguish them from other types of the same thing.

Here Vernon focuses on the concept of a type with the more literal meaning of a descriptive type whose purpose is to add distinction from one Entity to another. In my previous post I used an example of an Order Entity with an OrderType having possible values of Customer, Commercial, or Internal. However, this idea of Standard Types equally applies to the descriptive state of an Entity. For example, an Order with an OrderStatus of Active, Complete, or Canceled.

Standard Types as Entities

Implementing Standard Types as Entities, as illustrated in my post, is not a wholly aberrant technique. In fact, it’s deceptively intuitive. What we know to be true is that an Entity has an identity. As Eric Evans clearly states in Domain-Driven Design,

An object defined primarily by its identity is called an ENTITY.

Our Standard Types have an Id attribute, their identifier. And from our Domain’s point-of-view, we likely only care about a Standard Type’s identity. While the underlying record in the database may have more fields such as Name or Description, these are generally only relevant to the Presentation Layer and not important to an Entity’s invariants. So with this understanding of what an Entity is, implementing Standard Types as Entities makes sense. This was the basis for my own Static Entities implementation as outlined in my post. In fact, Vernon calls this out,

[Y]ou can use an Aggregate as a Standard Type with one instance of the Aggregate per type.

Putting the Static in Static Entities

A Standard Type, once implemented, as an Entity is functionally indistinguishable from any other Entity in your Domain. It is likely to share the same Context as the Entity whose type or state it describes. For example, our Order, OrderItem, OrderType and OrderStatus Entities share the same Context. More importantly, from the standpoint of our chosen ORM, these Standard Type Entities are expected to be loaded and tracked as any other Entity would be.

However, this is cumbersome. It requires additional logic and database calls to be executed just to set the Standard Type attribute of an Entity. Techniques like data caching can cut down on database calls. However, you are still required to implement supporting architecture to load the Standard Type, be it cached or otherwise. Having worked on many projects that implemented Standard Type’s this way I was always a little annoyed having to write the same code everywhere to load the Standard Type from the repository only to set an Entity attribute. Want to set the Order.Type to Complete? You first have to load the correct OrderType from the database. Why do we need to do this?

The answer to this question is the whole reason we implemented Standard Types as Entities to begin with–because Standard Types have an identity. More specifically they have a database identity. We know that beyond our persistence layer these Entities are being synthesized into database records with id’s and primary and foreign keys. We know the Order table has columns for OrderTypeId and OrderStatusId that reference our OrderType and OrderStatus tables. If the database identity of our Standard Types is generated by the database during it’s creation we won’t know that key unless it’s provided to us, likely by the Presentation Layer. Now, even worse our Entity load becomes a query whereby we search for a known name:

    var completeStatus = Repository<OrderStatus>
        .Where(status => status.Name == "Complete")
            .Single();

    order.Status = completeStatus;

As stated, this is cumbersome, and it gets more complicated. The above example is more in line with an anemic domain model. How would this work in DDD? The answer is not very well. We don’t want data access bits in our Entity, so that’s out of the question. Could we pass the OrderStatus as a parameter? We could, but how would we validate it? For example:

public class Order
{
	public Complete(OrderStatus completeOrderStatus)
	{
		if(completeOrderStatus.Name != "Complete")
		{
			throw new ArgumentOutOfRangeException(); 
		}
	}
	
	...

In addition to having really bad code smell, there is no guarantee it will work as intended. Since we don’t know the identity of the correct status, we’re only hoping that the OrderStatus passed in matches the identity of the Entity based on it’s name.

The solution I chose, and outlined in my previous post, was to simply define the identity in the application. This is the static part of Static Entities. In my case I use a KeyCatalog in each application to serve this purpose.

public static class KeyCatalog
{
    public static class OrderStatuses
    {
        public const string Active = "F1F275D7-3F88-48F8-BDF5-D35F6A5FF5E0";
        public const string Canceled = "1D86DA43-75EF-4EAA-A596-75BFC88F361B";
        public const string Complete = "9AF61DD1-9CD6-4E93-B214-C300209208A9";
    }

    public static class OrderTypes
    {
        ...
    }
}

These same keys are used in the database creation and migrations. So now there is consistency between the Entity identity and the database identity. The Entities can now be statically constructed as immutable and are no longer required to be first loaded from the database. Here is our OrderStatus Entity now:

public class OrderStatus : IStaticEntity
{
	public static readonly OrderStatus Active = 
		new OrderStatus(Guid.Parse(KeyCatalog.OrderStatuses.Active));
			
	public static readonly OrderStatus Complete =
		new OrderStatus(Guid.Parse(KeyCatalog.OrderStatuses.Complete));

	public static readonly OrderStatus Canceled =
		new OrderStatus(Guid.Parse(KeyCatalog.OrderStatuses.Canceled));
		
	protected OrderStatus(Guid id)
	{
		Id = id;
	}
	
	public Guid Id {get; protected set;}

}

In my previous post I joyfully illustrated how easy it was to just detach these Entities from the EF Core ChangeTracker. In practice it was much less wieldy once my Aggregates became more complex. It worked, but I had to fight hard against the ORM; it firmly disagreed with the way I was using these Static Entities. Ultimately, the ORM was correct.

Standard Types as Value Objects

In the same paragraph Vernon asserts you can use an Aggregate as a Standard Type, he goes on to say,

Think twice before you run with this.

And later he instructs us to,

[A]sk yourself if an immutable Entity is by definition really an Entity. If you think not, you should consider modeling it as a shared immutable Value Object instead.

This is an interesting question. While I certainly believe Entities can be immutable, I also concede there is something logically different about Standard Types that place them in their own category.

If we chose to model our Standard Types as Value Objects, what does that look like? First, let’s consider what a Value Object is to begin with. Evans states,

When you care only about the attributes of an element of the model, classify it as a VALUE OBJECT. Make it express the meaning of the attributes it conveys and give it related functionality. Treat the VALUE OBJECT as immutable. Don’t give it any identity and avoid the design complexities necessary to maintain ENTITIES.

* emphisis is my own

This seems to describe our Standard Types well, except our Standard Types do, in fact, have identity and further, their primary attribute is that identity. So it would seem the fundamentals of what separates Value Objects from Entities are at odds with the composition of Standard Types. The equality of two Standard Types is based entirely on it’s identifier. Do we conclude that Standard Types are not to be implemented as Value Objects?

No. As contradictory as it may seem, when implemented as Value Objects, Standard Types do not have an identity, but rather are the identifier of a Standard Type. In this way, we no longer think of Standard Types as Entities, with all the baggage that requires us to carry; we instead think of Standard Types as implementing the Identifier Pattern. Vernon, as well, touches on this idea in his discussion on unique identifiers and Entities. Stating,

Value Objects can serve as holders of unique identity. They are immutable, which ensures identity stability, and any behavior specific to the kind of identity is centralized. Having a focal point for identity behavior, however simple, keeps the know-how from leaking into other parts of the model and into clients.

Here is my own take on the Identifier Pattern using a similar base class as Vladimir Khorikov:

public abstract class IdentifierValue<TPrimitive> : ValueObject 
        where TPrimitive : notnull, new()
{
    protected IdentifierValue() : this(new()) { }

    protected IdentifierValue(TPrimitive id)
    {
        Guard.Against.Null(id, nameof(id));

        Id = id;
    }

    public TPrimitive Id { get; init; }

    protected override IEnumerable<object> GetEqualityComponents()
    {
        yield return Id;
    }

    public override string ToString() => Id.ToString();

}

By using the Identifier Pattern we can easily define our OrderStatusIdentifier Static Standard Type:

public class OrderStatusIdentifier : IdentifierValue<Guid>
{
    public static OrderStatusIdentifier Active => 
            new(Guid.Parse(KeyCatalog.OrderStatuses.Active));

    public static OrderStatusIdentifier Cancelled =>
            new(Guid.Parse(KeyCatalog.OrderStatuses.Cancelled));

    public static OrderStatusIdentifier Complete =>
            new(Guid.Parse(KeyCatalog.OrderStatuses.Complete));

    protected OrderStatusIdentifier() : base() { }

    protected OrderStatusIdentifier(Guid id) : base(id) { }
}

By protecting the constructor we prevent new instances of OrderStatusIdentifier from being created, but our ORM should be perfectly happy mapping it. Now we can reference our OrderStatusIdentifier like any other Value Object:

public class Order : AggregateRootWithGuidIdentifier<OrderIdentifierValue>, IMutableEntity
{
    protected Order()
    {
        Name = string.Empty;
        Status = OrderStatusIdentifier.Active;
    }

    public static Order Create(string name) => new() { Name = name };

    public OrderStatusIdentifier Status { get; protected set; }

    public string Name { get; set; }

    public string? PONumber { get; set; }
}

Conclusion

While my original post does illustrate a valid method for defining and using Static Entities, I’m happy to admit Static Standard Types as Value Objects is a superior solution.

Exit mobile version