logical-component-design · git:20261025.ebda69c · 2026-10-25 · sha256 c9d9d7e212d2d051

logical-component-design git:20261025.ebda69cA

Immutable. This exact content never changes and is served at /api/v1/blob/c9d9d7e212d2d051.

---
name: logical-component-design
category: architecture
environments: coding
description: Decomposes a NEW system or feature into named logical components; not for measuring an existing decomposition. Use when asking what components a new system needs.
---

# Logical Component Design

A **logical component** is a building block of the system with a distinct role —
represented in code as a namespace or directory (`com.app.order.placement`,
`order/placement/`). This skill takes a system or feature you are *about to
build* and produces a first set of named components, then refines them. It is the
generative front end to the analysis skills: it *creates* the decomposition;
`members/coupling-cohesion/SKILL.md` (read via the `architecture` router) *measures* one that already exists.

The core idea, from *Fundamentals of Software Architecture* (ch. 8), is that
component identification is an **iterative feedback loop**, not a one-shot act of
getting it right. You know least about the system at the very start, so trying to
perfect the initial components is wasted effort. Make a best guess, assign work
to it, see where it strains, and restructure. The loop never fully stops — it
runs on greenfield systems and every time a feature is added or changed.

Detailed approaches, worked examples, the Going-Going-Gone case study, and a
fill-in worksheet live in
[references/component-identification.md](references/component-identification.md).
Read it when you need the full examples or the worksheet; this file is the
workflow.

## When to use

Whenever someone is starting a design and asks "what components / modules do I
need for …", "how do I break this new system into components", "decompose this
domain", "how should I organize these user stories into components", or
"identify the building blocks for my <app>", or is filling empty buckets,
applying the Workflow or Actor/Action approach, or worrying about the Entity
Trap — even without naming the book. Not for analyzing existing code
(→ coupling-cohesion) or choosing system topology and folder layout
(→ `members/architecture-pattern-advisor/SKILL.md`, read via the `architecture` router).

## Relation to siblings

Sibling sub-skills, each read via the `architecture` router:
- `members/ddd/SKILL.md` — aggregates live inside components, and bounded contexts group
  components. The Entity Trap below is about naming components after nouns,
  not about modeling domain entities.
- `members/architecture-pattern-advisor/SKILL.md` — use it for the top-level
  domain-vs-technical partition; this skill decomposes within that split.
- `members/c4-modeling/SKILL.md` — render the final component list as a C4 Component view.
- `members/microservices-design/SKILL.md` — reach for it when components become deployment
  units.

## The cycle

Run these five steps in order, then loop. Enter at whatever step matches what the
user already has (they may arrive with components and only need step 3–5).

### 1. Identify initial core components

Think of each component as an **empty bucket**: a placeholder named for its
proposed role, with no responsibilities yet. Pick one of two approaches to
generate the initial buckets — and avoid a third that looks tempting.

- **Workflow approach** — walk the major happy-path (non-error) workflows a user
  takes through the system and assign a component to each meaningful step. It is
  *not* one component per step: steps that do the same job share a component
  (e.g. two "notify the customer" steps both map to `Customer Notification`).
  Good when you know the general flow but not the detailed requirements yet.
- **Actor/Action approach** — list the actors, **including the system itself**
  (it performs automated actions like billing or restocking), and the major
  actions each takes; map actions to components. Generates more, finer-grained
  components than the Workflow approach. It is the sensible **default** when
  there are no special constraints and you want a good general decomposition.
- **Entity Trap (antipattern — avoid)** — deriving components from entities
  (`Customer` → `Customer Manager`, `Order` → `Order Manager`). This trap is
  about naming components after nouns, not about modeling domain entities —
  a well-modeled entity is fine; a component named after one is the problem.
  Avoid it:
  entity-noun names describe nothing ("Order Manager manages orders"), and the
  component becomes a dumping ground for every bit of order logic — a god-
  component that is hard to test and deploy. Red-flag suffixes: **Manager,
  Supervisor, Controller, Handler, Engine, Processor**. A name passes when
  its role statement names one job — changing the suffix alone is not a fix;
  the suffixes are only a prompt to write the role statement. `Inventory
  Management` passes because its role names a single responsibility: stock
  levels. Prefer role names that
  say what the component *does* — `Validate Order`, not `Order Manager`.
  *(Escape hatch: if the system truly is CRUD over entities with no real logic,
  it doesn't need an architecture at all — reach for a CRUD/low-code framework.)*

### 2. Assign user stories / requirements to components

Start filling the buckets: attach each user story or requirement to the
component whose role it belongs to. This is what gives a component its concrete
responsibility. When a story fits **no** existing component cleanly — a cross-
cutting concern like "email the customer on every status change" that three
components would otherwise duplicate — that is the signal to **create a new
component** (`Customer Notification`) and have the others communicate with it,
rather than copying the code. Stories arrive over time, so this step recurs.

### 3. Analyze roles and responsibilities (cohesion)

Check that each component's assigned work actually belongs together and that no
component is doing too much. Write a **one-sentence role and responsibility
statement** for the component and read it critically:

> The **conjunction test**: if the statement leans on *and*, *also*, *in
> addition*, *as well as*, or a pile-up of commas to list what the component
> does, it is probably carrying too many responsibilities → split it.

Because a component maps to a single directory/namespace, "too much
responsibility" is also literally "too much code in one place." Extract the
strayed responsibilities into their own components (e.g. pull payment,
inventory, and email out of an over-broad `Order Placement`). For a deeper,
metric-backed split/merge/leave decision on a specific class or module, hand off
to the **`coupling-cohesion`** skill.

### 4. Analyze architectural characteristics

Look at the architecture characteristics the system must support — scalability,
reliability, availability, elasticity, fault tolerance, agility — and ask whether
any of them should reshape a component. A purely functional view might put all of
some capability in one component, but differing characteristic needs can force a
split: in the GGG auction, one `Bid Capture` component handled bids from both
bidders and the auctioneer, but bidders need high scalability/elasticity
(thousands of them) while the auctioneer needs high reliability/availability
(one connection that must not drop) — so it splits into `Bid Capture` and
 `Auctioneer Capture`. This step assumes you already know which characteristics
matter most; ask the user for the top 3 driving characteristics, or take them
from an existing ADR or architecture-pattern-advisor's trade-off analysis —
do not assume them.

### 5. Restructure and iterate

Fold the results back into step 1 and go around again — after any split or
merge as much as at the start. Stopping "because the diagram is done" is the
mistake; the loop is the method. Few systems have only one valid decomposition:
assess trade-offs honestly and pick the **least-worst** set.

**Pass complete when:** every story is assigned to a component; each role
statement is one sentence and passes the conjunction test; no component has
an entity-only or red-flag name; temporal couplings are listed; the top-3
driving characteristics have been reviewed. After any split or merge,
re-run these checks on the changed components. End the pass when all checks
pass and record any open questions under "Next iteration" in the output.

## Refinement lens: coupling

Once a candidate set of components exists, examine how they depend on each other
— more coupling means the system is harder to maintain and test.

- **Static coupling** — synchronous dependencies; track per component the
  **afferent count (Cᴀ)** of components that depend on it and the **efferent
  count (Cᴇ)** of components it depends on.
- **Temporal coupling** — non-static dependencies of timing or transaction
  order: `Order Placement` must run before `Order Shipment`. Tooling rarely
  catches this; surface it from the design and note it explicitly.

For coupling **metrics on code that already exists** (instability, distance from
the main sequence, zones of pain/uselessness), hand off to **`coupling-cohesion`**.

## Refinement lens: Law of Demeter (Least Knowledge)

A component should have **limited knowledge** of the rest of the system — the
Principle of Least Knowledge. Knowledge is coupling: if `Order Placement` knows
that inventory must be decremented, *and* that low stock means reorder from a
supplier, *and* that low stock means adjust the price, *and* that the customer
must be emailed, it is tightly coupled to four components. Push the knowledge
that doesn't have to live there *into* the downstream component (let `Inventory
Management` own the "if stock is low, reorder and reprice" knowledge), shrinking
the hub's efferent coupling.

Be honest about the trade-off: applying the Law of Demeter usually
**redistributes** coupling rather than removing it system-wide — the downstream
component's coupling goes up as the hub's goes down. Move knowledge to where it
naturally belongs, don't just chase a lower number on one node.

## Output format

Lead with the approach used, then the component list, then what's still open.
Keep it proportional — a small feature needs a few components, not a report.
Give each component its own bullet rather than a table row, so the answer
stays readable on a phone.

```
Approach: <Workflow | Actor/Action> — <one-line why>

Components:
- **<Component>** — <single-sentence role, passes the conjunction test>
  - Assigned stories: <...>
  - Cᴀ / Cᴇ*: <fan-in> / <fan-out>
  - Notes: <...>

*Cᴀ / Cᴇ is optional for designs with fewer than about 5 components.

Characteristics reviewed: <which -ilities drove any split>
Next iteration / open questions: <unmet checks from above + what to revisit as requirements firm up>
```

## Source

Mark Richards & Neal Ford, *Fundamentals of Software Architecture*, 2nd ed.
(O'Reilly), ch. 8, "Component-Based Thinking" — the component identification and
refactoring cycle, the Workflow / Actor-Action approaches, the Entity Trap,
component coupling, and the Law of Demeter.

Raju Gandhi, Mark Richards & Neal Ford, *Head First Software Architecture*
(O'Reilly, 2024), ch. 4 — deciding which architecture characteristics matter
and designing for them.