A design principle that helped me survive complexity, maybe it’ll help you too.
Yesterday I was invited to a private OOP design workshop held by Soheil Karami. The task was to explore, learn, model, and design the board game Catan. For someone like me who hasn’t played many board games, this was a seriously complex domain. I was quietly following how the group was approaching the problem.
The funniest and at the same time most interesting part of the day was watching three developers with different experience in terms of quantity, quality and depth, trying to model something none of them fully understood. They were playing the game, drawing diagrams, writing tests, sketching relations, talking and rewriting code. The table quickly turned into a jungle of nouns:
Hexagon, Space, Board, Player, Dice,
Road, Village, City, Robber, Trade,
Brick, Wheat, Wood, Sheep, Ore, …
And pretty soon, they were lost in a jungle of nouns. Endless class discovery. Endless property mapping. Endless shape-drawing that didn’t really move the design forward.
I walked around and finally interrupted them and shared a design principle I use a lot when facing complex domains:
The question that has saved me many times
- Is this class supposed to model merely the type of something, or the instances that actually matter when the system runs?
2. Are we modeling the static entity or the real happening?
And I encouraged them:
Start modeling with active domain concepts instead of passive ones.
Design your concepts (aggregates, classes, objects, methods, behaviors, etc.) from the PoV of the actual user and the real usage.
Don’t start by obsessing over static pieces, your objects aren’t going anywhere. Instead, start by describing what actually happens in the system. In the sections below, I’ll break down active vs. passive domain concepts, why they matter, and how they can save you from drowning in complexity. Maybe later I’ll share the other design ideas I mentioned, once I stop tripping over my own metaphors. 😄
What I mean by Active vs Passive Domain Concepts
Passive domain concepts
These are things that exist but don’t do anything on their own. They don’t start any workflow. They just sit there waiting to be used.
In accounting, for example:
GeneralAccount
SubsidiaryAccount
Currency
CostCenter
FiscalYear
They exist. They’re important. But they don’t trigger anything. You define them once and they sit silently until something uses them.
Active concepts
These are the living concepts, things that move the system, drive behavior, and glue other concepts together. These are entities where something happens, they trigger flows, decisions, and collaboration among other objects.
Examples from accounting:
FinancialTransaction
Posting
TransferMoney
ClosePeriod
SettleBalance
A FinancialTransaction is active, it triggers validations, affects balances, creates journal entries, maybe even publishes integration events. Something happens.
PostTransaction() is what actually causes the accounting system to move. It updates balances. It validates rules. It may generate events. It triggers work. Something happens.
Why This Principle Matters (beyond convenience)
Why Start With Active Instead of Passive?
This is not about shortcuts or avoiding modeling work, it’s about creating clarity and direction in the middle of complexity.
To put it simply, because active concepts reveal how the passive ones interact(believe me, just give it a try). They define the flow and the collaboration between objects. They automatically show which objects are needed and how they’re connected.
Starting with passive concepts gives you:
- A beautiful diagram of boxes
- Zero clarity about how they behave
- Early premature structure
- Over-modeling and abstraction paralysis
Starting with active domain concepts gives you:
- A clear entry point into the system
- A sequence of behaviors rather than infinitely debating structure
- Natural discovery of required passive concepts
- Actual business meaning
- Collaboration and interaction maps, instead of isolated definitions
Starting with passive nouns is like describing every item in a kitchen without knowing what recipe you’re cooking. You’ll list:
Spoon, Salt, Oven, Knife, Table, Fork, Plate…
Great. Now what? What are we cooking? Who is doing what?
Behavior gives shape to structure, not the other way around.
Passive-first modeling gives you a graveyard of objects. Active-first modeling gives you a living system.
Active Concepts as the Glue
Active domain concepts are the glue that connect everything. They combine, trigger, coordinate, and chain objects together into meaningful behavior.
Without them:
- You only know who exists
- You don’t know why they exist
- Relationships are speculative rather than discovered
With them:
- You see the object map form naturally
- You see how entities collaborate
- You model the real world, not static diagrams
It’s the behavior and interaction between concepts that matter.
Objects in isolation are useless. The system’s value is in how they work together and how they change state over time. Modeling is not about naming nouns. Modeling is about choreographing collaboration.
Applying the Idea to Catan (as an example)
Let’s pretend we are designing Catan. Full disclaimer: I really don’t know the game well (at least at the time of writing this 😄). But it’s just an excuse to illustrate the idea.
When beginners start modeling Catan, they often begin by listing nouns:
Board
Hexagon
Tile
Brick
Wood
Player
Village
City
Road
Dice
Island
Port
These are passive concepts. If you model them first, you’ll drown in details and relationships and constraints, and you won’t get far.
Catan Example, Starting With Player(an active domain concept)
In Catan, instead of beginning with nouns like Board, Tile, Road, Resource… let’s start with the most active concept: Player
What does a Player do?
Player.StartTurn()
Player.RollDice()
Player.CollectResources()
Player.BuildRoad()
Player.BuildSettlement()
Player.UpgradeToCity()
Player.TradeResources()
Player.MoveRobber()
Player.StealResource()
Player.ScorePoint()
Active behavior organically reveals passive structure
From
Player.RollDice():
Dice.Roll()
Board.NotifyTiles(diceResult)
Tile.ProduceResource()
Player.ReceiveResource(resource)
Now we know we need:
Dice, Board, Tile, Resource, ProductionRules
discovered through behavior, not speculation.
From Player.BuildRoad():
Board.ValidateRoadPlacement(position)
Player.PayResourceCost(ResourceCost.Road)
Road.Place(position)
Now we know:
Road, Board, Position, ResourceCost
These concepts appear at the right moment, when needed.No guessing. No overdesigning. No UML graveyard.
Real-World Examples
Food Delivery System
Passive
Restaurant, Menu, Dish, Customer, Address, Courier
Active
PlaceOrder
AcceptOrder
AssignCourier
StartDelivery
DeliverOrder
CancelOrder
RefundPayment
Start from PlaceOrder(), and see the result. I won’t show you the result, it’s your turn!
Healthcare System
Another example for you…
Passive
Patient, Doctor, Prescription, Medication, Clinic
Active
BookAppointment
Diagnose
IssuePrescription
DispenseMedication
ScheduleFollowUp
Start with BookAppointment() and the domain becomes architecture.
So When Should You Model Passive Concepts?
Later. When the behavior reveals what data structure is actually needed.
To twitterify it:
Passive modeling is organization. Active modeling is understanding.
Sorry, It’s Not a Silver Bullet
Is this the only way? No, of course. Is this the right way in every domain? Probably not.
Sometimes starting with passive structure is required (e.g., reverse engineering an existing system or working from legal documents or standards). Sometimes relational modeling is necessary early.
But in greenfield modeling, workshop settings, collaborative exploration, or domains you don’t fully understand yet:
- Starting with active concepts gives direction.
- Starting with passive concepts gives confusion.
This principle gives you traction. It fights the paralysis of endless abstraction. It forces you to speak about real business actions instead of hypothetical data buckets.
To Wrap
This workshop reminded me again how powerful the shift can be when you stop listing nouns and instead describe what happens.
Model the living parts.
Start with behavior.
Let interactions give birth to structure.
Maybe in the future I’ll write more about the other ideas I shared with the guys that day, such as:
- Design your concepts (aggregate, class, object, methods, behavior, etc.) from the PoV of its users and usages.
- Is this class supposed to model the type of something, or the instances that actually matter when the system runs?
- Are we modeling the static entity or the real happening?
And yes, maybe by the next workshop I’ll finally learn how to actually play Catan 😄
Leave a Reply