Data modeling with anemic and rich models
Data modeling is a crucial part of any development job. Some people keep it away from the code and reduced it to de database scope. But this is not the case for me.
The code is at the heart of any application, not the database. So, what means data modeling from the code perspective? Is something about Domain-Driven Design? Does it depend on the chosen architecture? Let’s see it.
🥔 The CRUD reality of anemic models
First of all, what are anemic models? We refer to one of the most widespread practices in software development with this horrible name. They are also known as Plain Models or Data Transfer Objects.
In any case, it means a way of coding that uses classes as transporters for data, with no logic inside.
🍏 No Logic no pain
It is commonly used on simple systems based on CRUD operations. And for that situation are very suitable, due to the simplicity of development and execution performance.
Also recommended for microservices (narrow scope usually tends to simple models) or query intensive applications (carriers, not operators).
If your job is to transport data with none or very little manipulation, then anemic model for the win.
🥡 The Logic is outside
Problems arise as soon you need to enforce complex validation rules before accepting data. Or even when you need to transform them in any direction.
In this situation, you will end up with your data separated from the logic and that is anything but the cohesion we like in the Object-Oriented world.
🍟 Rich models are homes for the logic
And what do we have to enrich those poor buddies? Well, there are plenty of named solutions, but all have one thing in common: put logic and data on the same basket.
Some of them are very related, but not specific to the Domain-Driven Design approach.
🍹 Value Objects
These are the most straightforward. They are classes that enforce rules on their constructor or in any setter if they are mutable.
That’s all; this way you can ensure that:
- a range of dates are well-formed (with a start date prior to an end)
- the age of an employee is acceptable (between legal margins)
- an address is well-formed (has a Zip code, street number…)
Those rules associated with data are called invariants.
Even you can extend that to not only validate but transform the representation. Some ideas are:
- a temperature value presented on Celsius or Fahrenheit
- an economic amount expressed with or without currency
For sure, any of these functionalities can and should be, expressed on utility functions. But enforcing its use inside a class for any change on its property is a plus.
🍺 An Entity
The main difference with V.O. is that entities must have one property that acts as a unique identification. You can think of it as the primary key of a relational table.
An entity is something unique, protect it.
And yes, an entity could be and should be, a composition of several value objects. Following with prior examples:
- an employee with his age and his address
- a client with his address and his expenses
So, entities are normally bigger than VO and have more logic than simple validation inside. Having the data and logic on the same structure leads to very much wanted cohesion.
🍻 Aggregates
Another important player in this kind of data modeling is the Aggregate. If you think of an entity as a table, then an aggregate could be formed by any root entity and its related tables.
Some common examples are:
- a client with its orders
- an invoice with its list of items, and maybe their payment lines
- an appointment and their participants or any location-related data
Usually, when you create, add or change any data of an aggregate, it should be persisted as one transaction on a database.
An aggregate tells a story about entity relationships
Anyway, the important part here is that you get a cohesive model, that ensures valid data and integrity between related tables.
🚀Applied in TypeScript
Samples of different models written in TypeScript
Learn, code, enjoy, repeat.
Alberto Basalo