DEV Community

Grady Zhuo
Grady Zhuo

Posted on

Understanding DDD Aggregates Through Board Games and Battlefields

I've been learning DDD for almost two years now. My understanding of Aggregate kept evolving — from accidentally treating it as just a Write Model, to roughly grasping that "an Aggregate protects invariants." But something always felt unclear. Then a couple days ago, while explaining it to a teammate, I tried using a game analogy, and suddenly everything clicked.


An Aggregate Is a Board Game

Think of a board game. One game is one Aggregate. It has characters, items, and events — but most importantly, it has rules. Every action taken inside that game must follow those rules. The rules of Catan only apply in Catan. You can't bring Monopoly's "collect $200 when passing Go" rule into it.

Catan itself is the Aggregate — not the characters, not the items, but the game as a whole. Once you have those characters and items living inside the Aggregate, you can design Commands to operate on them. So an Aggregate protecting its invariants is really just like a board game enforcing its rules. The only difference is that in the real world, those rules are called business rules. And in the real world, there's no escaping them.

Within a single turn, every action must be resolved at once — you can't do half a move and come back later. This maps to another core concept:

An Aggregate is a Transactional Consistency Boundary. Either everything succeeds, or everything fails. No in-between.


The Battlefield vs. The War Room

Later, while discussing with another teammate, I needed to explain how the same entity can exist across two different Aggregates. I found that a war room sand table analogy works surprisingly well.

The real battlefield is one Aggregate. The commander's sand table is another. On the sand table, flags represent each general — but the flag is not the general. It's just an identifier (an ID) that tells the commander "this person exists." The general's full state lives on the battlefield.

The two Aggregates have completely different concerns about the same person. The sand table doesn't need to know how badly the general is wounded or how many troops he has left. It only needs to know: "is he still available?"

That's why across Aggregate boundaries, you only store an ID — never an object reference. Boundaries must stay clear.

When something happens on the battlefield, a messenger rides to the war room and the commanders update the sand table. That messenger is what DDD calls a Policy. It listens to a Domain Event fired by one Aggregate, then decides whether to trigger a reaction in another.

This is exactly why DDD pairs so naturally with EventSourcing and CQRS — the entire system collaborates through events flowing between boundaries. Not direct calls. Not shared objects. Events.


Wrapping Up

Once these two things clicked, a lot of design decisions that used to feel stuck suddenly had clear answers.

I'm not sure these analogies will work for everyone, but for me, they were the first time DDD stopped feeling like a pile of jargon. Sometimes you're not stuck because you're not trying hard enough — you just haven't found the right angle yet.


If you've had a similar moment of clarity, I'd love to hear about it in the comments.

Top comments (0)