🗺️ Analogy
Drawing component boundaries is like dividing a city into neighbourhoods. Carve them around how people actually live and travel (workflows) and the city flows; carve them around bureaucratic categories on a form (entities) and you get districts nobody can navigate. Good boundaries follow real usage, not the filing cabinet.
The iterative identification flow
Components are never right on the first try. Fundamentals and Head First describe a continuous loop you run for greenfield design and on every change:
graph TD A["1. Identify initial components"] --> B["2. Assign requirements / stories"] B --> C["3. Analyse roles & responsibilities (cohesion)"] C --> D["4. Analyse architecture characteristics"] D --> E["5. Restructure"] E --> A
- Identify initial components — a best guess per top-level thing the system does, based on your chosen partitioning.
- Assign requirements — map each story to the responsible component.
- Analyse roles and responsibilities — use cohesion to check each component owns what it should and isn’t a dumping ground.
- Analyse characteristics — verify each aligns with the driving characteristics. This step most often forces a split.
- Restructure — iterate continually with developers.
Discovery techniques
- Actor / Actions (from RUP) — identify actors, then their primary actions; best for systems with many user types.
- Workflow — trace major user journeys and map steps to components; best for one user type with complex journeys.
- Event storming (from DDD) — assume events/messages and build components around their handlers; fits microservices.
Combine them: use actor/action to find actions, then workflow to order them.
⚠️ The entity trap
Creating a Bid Manager or Order Manager component per database entity is the entity trap — that’s an ORM of framework-to-database, not an architecture. Warning signs: names ending in manager, supervisor, or handler, and components that become overloaded dumping grounds. (A few are fine — “Reference Data Manager” for codes is acceptable.)
Characteristics drive the split: Bid Capture
A worked example: Bid Capture originally accepts bids, finds the highest, writes all bids to the database, and notifies the auctioneer. But the driving characteristics — scalability (thousands of bids/sec), availability, and performance — are all hurt by the synchronous DB write. The fix is to split out a new Bid Tracker component so Bid Capture hands off bids asynchronously and never waits on the database. The split exists because the characteristics differ.
graph LR subgraph Before["Before — one overloaded component"] BC1["Bid Capture<br/>accept · find high · WRITE DB · notify"] --> DB1["(Database — sync write blocks)"] end subgraph After["After — split by characteristics"] BC2["Bid Capture<br/>(scalable, fast)"] -->|"async handoff"| BT["Bid Tracker<br/>(durable writes)"] BT --> DB2["(Database)"] end
The architecture quantum: monolith or distributed?
The decisive concept is the architecture quantum (The Hard Parts): an independently deployable artifact with high functional cohesion, high static coupling, and synchronous dynamic coupling. Three parts matter:
- Independently deployable — includes everything it needs, including its data. A system sharing one database is a quantum of one (a monolith), no matter how many services it has.
- High functional cohesion — it does something purposeful (a
Customercomponent, not a grab-bagUtility). - Synchronous coupling — synchronous calls bind operational characteristics for the call’s duration; async decouples them.
💡 Key insight
Let the number of quanta decide monolith vs distributed. If every component shares one set of characteristics, you have one quantum — choose a monolith for its simplicity. When components need differing characteristics (a read-only video stream vs a high-write bid capture; an auctioneer vs thousands of bidders), you have multiple quanta and a distributed architecture earns its complexity.
See also
- Components and coupling — the building blocks.
- Connascence — synchronous vs asynchronous communication coupling.
- Identifying architecture characteristics — what drives the splits.
When to use it — and when not
✅ Reach for it when
- When designing the logical architecture of a new system or a major feature
- When deciding how fine-grained components or services should be
- When choosing between a monolith and a distributed architecture
⛔ Think twice when
- When you would model components after database entities (the entity trap)
- When a simple CRUD app needs only scaffolding, not full component design
Related topics
Components as the architect's primary building block, and the afferent/efferent coupling that measures how they depend on one another.
fnd-componentsConnascenceA precise vocabulary for HOW two components are coupled — static vs dynamic connascence — and the three properties that guide refactoring toward weaker forms.
fnd-characteristicsIdentifying Architecture CharacteristicsHow architects uncover the driving -ilities from domain concerns, explicit requirements, and implicit domain knowledge — without over-specifying.
Check your understanding
Score: 0 / 41. What is the entity trap anti-pattern?
Modelling components after entities (names ending in manager/handler/supervisor) produces vague, overloaded components — that's an ORM mapping, not architecture.
2. Which step of the component-identification flow most often forces a component to be split?
When part of a component needs different characteristics (e.g. an auctioneer needs higher reliability than thousands of bidders), you split it.
3. What is an architecture quantum?
The quantum bundles everything needed to function (including its data); a system sharing one database is a quantum of one.
4. What decides monolith vs distributed?
One set of characteristics across components favours a monolith; differing characteristics per component point to multiple quanta and a distributed architecture.
Comments
Sign in with GitHub to join the discussion.