ECS is Columnar. Objects are Rows. You Shouldn’t Have to Care
The relational model solved this problem fifty years ago.
ECS vs OOP is a debate that only exists because programmers forgot that the relation model dissolved the distinction between logical and physical models.
The heart of FREST
This, right here, gets at the core of why I’m pushing FREST. There shouldn’t be a debate or a decision about whether to use Entity-Component or Object based modelling. We only think there needs to be a choice because we lost sight of what the relational model really is.
ECS: OOP that might have been?
The game industry has discovered an alternative to traditional OOP classes, called an Entity Component System. Rather than group different states with different purposes into objects, ECS stores state by purpose. This design allows greater flexibility than many OOP systems in associating behaviours with objects, and can have substantial cache-related performance advantages.
The history is informative. OOP using objects and classes became popular in the 70s, while ECS first started becoming popular after a talk by Scott Bilas in 2002.
The core idea behind ECS had appeared even before OOP came into view — Sketchpad achieved much of its very impressive technical features using something like ECS, in 1963.
Here is a very long, very good dive into the history.
Contemporary discussion often frames ECS as the OOP that might have been. We got so caught up with Alan Kay’s vision of objects as being like biological cells that we forgot to consider another way.
ECS advantages
Say we have these objects:
WORLD STATE
Position Velocity Health Team
Entity A ● ● ● ●
Entity B ● ● ● ●
Entity C ● ● ● ●
Entity D ● ● ● ●
In traditional OOP, we collect the rows of the table into objects, in memory:
┌─────────────────────┐
│ Entity A │
├─────────────────────┤
│ Position │
│ Velocity │
│ Health │
│ Team │
└─────────────────────┘
┌─────────────────────┐
│ Entity B │
├─────────────────────┤
│ Position │
│ Velocity │
│ Health │
│ Team │
└─────────────────────┘
…
In ECS, we group the properties by purpose, which ECS calls a “Component”:
POSITIONS
┌────┐
│ A → (1,2) │
│ B → (5,3) │
│ C → (8,9) │
│ D → (2,7) │
└────┘
VELOCITIES
┌────┐
│ A → (1,0) │
│ B → (0,1) │
│ C → (1,1) │
│ D → (0,0) │
└────┘
…
What would be objects in traditional OOP become little or nothing more than an identifier, used to look up the properties in the Components.
ECS offers two principle advantages over traditional OOP.
First, components and their associated behaviours can be associated and disassociated from objects at runtime very easily, along the way rendering inheritance and behaviour composition decisions much simpler.
Second, code that needs to process properties en masse (say, physics code looping over all the moving objects to calculate their new positions) benefit tremendously from cache behaviour on modern CPUs when large blocks of data are processed together.
It is common now in games projects to use ECS. The very widely used Unity game engine is built around this pattern.
ECS vs Objects is Columns vs Rows
In databases, we have the remarkably similar choice between row-oriented and column-oriented data stores. A row-oriented database groups all the fields of a row into one chunk in storage. A columnar database will instead keep all the values of a column together.
There is a similar benefit with a columnar store to the cache benefits of ECS: analytic/statistical processes are often concerned with all the values in a subset of a column at a time, and keeping the data grouped according to use is much faster for the same cache-related reasons.
We shouldn’t have to commit
In Postgres, we can decide which tables are columnar and which row-based. In either case, the SQL we write to update and query the data remains the same. And we can change some of the data to the other representation without changing our logic.
This, right here, is one major feature of the relational model. By expressing our data processing using the relational algebra, it becomes independent of representation.
Logic is simpler than code
Most things that can be represented in the relational algebra (i.e. First Order Logic) are simpler and more general when represented that way. The representation of the logic is independent of concerns about underlying representation, parallelism and a bunch of things that make performance-oriented development (like games) particularly tricky.
Imagine being able to write game code like this:
visible(A, B) :-
position(A, PA),
position(B, PB),
nearby(PA, PB),
line_of_sight(PA, PB),
not occluded(PA, PB).
Performance?
You may now be wondering how well game logic represented in Datalog would perform.
In 1975, many programmers would have looked at SQL and concluded it could never compete with hand-written record processing code. They would have been right about early implementations and wrong about the long-term trajectory. Once the logical model and the physical model were separated, database engines became free to invent indexes, join strategies, caching schemes, materialized views, column stores, partitioning, and distributed execution. Datalog offers the possibility of a similar separation for game logic. The programmer specifies relationships. The engine chooses an execution strategy.
SQL is an abomination. But Datalog is lovely. “Can Datalog be fast?” Yes. Very much so.


