Better Software with Entity Systems

Sep 24, 2017

In today’s post, I want to introduce you a concept called Entity-component System and to show how to use it for web development.
The idea is not new. In fact, it is widely used in game development. But I have never heard about it when talking about backend development and data modeling.

The Problem

I suggest we start with a very typical feature: featured content.
Let’s say we have a list of books:

You can imagine that such a list of features can grow further until the sun goes out.
It has at least two disadvantages:

there is no explicit schema (some objects do not need all those featured* properties)

given that we store last modification date (e.g. updatedAt): each time we change the order of a book, it is marked as changed, even though the essence of the record (title, author, description, etc.) is still the same. Technically the book has changed, but logically it is not.

Let’s see if we can fix this.

Entity-Component Systems

There are many great explanations on the Internet, so I will not dive too deep.
But here is a concise example: let’s say you build a game and you need to model things like swords and axes and bows and whatnot.
For the sake of simplicity let’s consider the weapon to have a few properties:

interface Weapon {
string name;
double damage;
double weight;
}

As in any game you could sell and buy weapons, so we need to add a price:

There can be many more properties such as magical effects (one, or none, or many), or condition (broken, charged, enchanted), or distance (bows, magic weapon), or anything else you can imagine. It is evident that the classical OOP approach would not work very well here: classes will become bloated very fast, they will contain lots of useless properties.

One of the possible solutions is an Entity-Component Systems approach: an entity is an entity, simple. The component is an aspect of an object (can be sold? has magical effects?). Systems: some aspect of the world, that handles specific components of an entity.
Using entities and components, we can create some weapon:

Managing complexity this way is much easier. We can do whatever changes we want without affecting the real data: add more properties (via new collections), remove unneeded properties (by dropping a collection). If we change the order now, then it doesn’t affect the book itself.

However, it comes with a little disadvantage: fetching data is a bit less trivial now. Fortunately, we can use MongoDB Aggregation Framework for this task.

Fetching data

To prepare the data for a client we need to retrieve all FeaturedBooks, then for each featured book fetch the corresponding book (via book_id), then order results based on the order field, and then drop all unnecessary data.
Here is the required pipeline:

$lookup will find all the books which _id equals to the book_id from the
FeaturedBooks. It will put results into a field names book, as we requested.
The book field holds an array since there could be more than one match.

In our case, the book array has only one object inside. We can safely $unwind it to inline the book.

$sort is straightforward. We sort objects based on a value of order field.
1 means sort in ascending order. To flip the order, you can use -1.