At Infraspeak, the entire engineering team gets a couple of days, every month, to fully dedicate to pet projects. As me and my friend Nelson were working on our automations Bot, we were introducing the first Entities to our domains without noticing, at first, that we were creating invalid instances. This article shows why those instances were broken from the start, and how to ensure we always work with valid instances even before persisting them in a data store.
Entities are defined by a thread of continuity and identity. This means that due to having a unique ID, whenever their properties’ values change, it’ll still be the same Entity and we’ll always be able to reference to it (that’s the continuity part).
When developing software, it’s broadly accepted that we should strive for immutable objects. If we want to change something inside immutable objects, we make new instances with the updated values. This prevents that during execution of the application logic, an object gets unintentionally mutated, probably putting it in an invalid state. I won’t delve to much into this pattern (rain check for another post), but the point is that when working with immutable objects, they should be in a valid and usable state. Anywhere that accepts that object doesn’t need to worry about constantly check for its validity.
So, if having an identity, via a unique ID, completely defines an Entity, and it should be considered immediately valid upon instantiation, then using sequentially-generated number from a data store as the source of the Entity identity is a problem. And here’s why: we can’t safely predict what the next number is, and it’s not even the Entity responsibility to do so, leaving us in a dilemma: we need to instantiate an Entity to persist it, and get that ID, but we can’t because to instantiate it we need to have an ID. So, what to do?
A naive, instinctive, workaround would be to accept two types for the ID property: an
integer or a
null. This does fix the problem of instantiating the object but doesn’t make it valid, and we implicitly created a dependency on the data store, now, because we need to persist the entity first, and create a new instance with the actual ID, before we can use it further in the application.
The thing is, although clearly broken, this is the most common implementation that I’ve seen when working with Entities. I, too, have done it this way, but I’ve recently found another way of working with totally valid Entities without requiring them to be persisted to the data store before being used in the application.
The trick is to drop the use of internally-generated sequential IDs that come from the data store, at application level, and replace them with randomly, uniquely-identifiable IDs, like UUIDs. By using UUIDS, we can both generate unique IDs and have fully valid Entity instances to work with immediately, and remove a hard dependency on the data store for that purpose.
We can even save on object instantiation times, since we no longer need to re-create an Entity class upon persistence because there’s probably no new properties to fetch, where before we would need to do it to have the ID populated from the data store. Persistence of the Entity becomes an implementation detail.