Chapter 8: Component-Based Thinking
fsa components component-design logical-architecture
Status: Notes complete
Overview
Before committing to an architectural style, an architect must define the building blocks of the system: components. Chapter 8 provides a systematic approach to identifying and structuring components from business requirements, introduces the distinction between logical and physical architecture, and presents a principled process for creating a logical architecture. The chapter culminates in the “Going Going Gone” case study — an online auction platform — which illustrates the full component discovery process in practice.
What Is a Component?
A component is the fundamental modular building block of an architecture. It is a named, cohesive grouping of related code that encapsulates a well-defined set of responsibilities and exposes behavior through an interface.
Components are above the class level in the hierarchy of abstraction:
- Classes / functions: individual units of behavior
- Modules: collections of related classes
- Components: collections of related modules that together fulfill a business capability
- Architecture: the system of components and their relationships
In most architectural contexts, a component maps to a deployable artifact in a physical architecture (a JAR, a package, a microservice) or a logical module in a modular monolith. The architect works at the component level; developers work within components.
Logical vs. Physical Architecture
This distinction is one of the most important in the chapter.
Logical Architecture
A logical architecture describes the system in terms of conceptual components and their relationships, independent of deployment decisions. It answers: What are the functional building blocks of the system, and how do they relate to each other?
Characteristics:
- Technology-agnostic (no mention of specific frameworks, databases, or cloud services)
- Focused on responsibility and behavior boundaries
- Describes interfaces and data flow between components
- Used for communication with stakeholders, domain experts, and development teams
- The appropriate level of abstraction during early design
Physical Architecture
A physical architecture describes how logical components map to actual deployment units — servers, containers, processes, databases, message queues. It answers: How is this system actually deployed and operated?
Characteristics:
- Technology-specific (AWS Lambda, PostgreSQL, Kubernetes Deployment, etc.)
- Concerned with operational characteristics (scaling, replication, network topology)
- Derived from the logical architecture plus architectural characteristics requirements
- Subject to change as infrastructure choices evolve
The Sequence Matters
Architects should define the logical architecture first, then derive the physical architecture. Jumping straight to physical decisions (choosing microservices, choosing Kubernetes) before understanding the logical components leads to premature commitment and structural mistakes that are expensive to reverse.
Defining Components: Top-Down vs. Bottom-Up
Top-Down (Architecture-First)
Start with the business capabilities and decompose them into components. This is the preferred approach for greenfield systems.
- Identify major business functions from requirements
- Group related functions into candidate components
- Assign responsibilities explicitly
- Validate against user stories and operational requirements
Bottom-Up (Code-First)
Identify components by examining existing code — group related classes, find natural cohesion clusters, and surface implicit component boundaries. Used in brownfield (existing system) analysis.
Entity Trap (Antipattern)
A common mistake in top-down component identification is creating components that mirror database entities: CustomerComponent, OrderComponent, ProductComponent. These are entity-centric rather than behavior-centric and result in anemic components that contain only CRUD operations. This is called the entity trap.
Real components should represent business capabilities or workflows, not data records. A better decomposition might be OrderFulfillment, CustomerOnboarding, ProductCatalogManagement.
Creating a Logical Architecture: The 5-Step Process
Richards & Ford provide a systematic 5-step process for discovering and structuring components from business requirements.
Step 1: Identify Core Components from Requirements
Read the requirements and catalog the major business capabilities described. At this stage, think in terms of nouns (things the system manages) and verbs (actions the system performs). Each major capability is a candidate component.
Do not over-refine at this step. The goal is to get a first-cut list of components — expect it to change.
Deliverable: A list of candidate component names with one-line responsibility descriptions.
Step 2: Assign User Stories to Components
Take the system’s user stories (or use cases) and assign each story to one or more candidate components. This stress-tests the component list:
- If a user story cannot be assigned without touching every component, the decomposition may be too granular.
- If one component absorbs 70% of all user stories, it may need to be split.
- User stories that fall between components reveal missing components or incorrect boundary placement.
Deliverable: A mapping of user stories to components, revealing gaps and overlaps.
Step 3: Analyze Roles and Responsibilities
Review the assigned user stories for each component and ask: does this component have a single, coherent role? Is it doing too many conceptually different things? Does it have the right level of responsibility?
This step often surfaces components that should be split (doing too much) or merged (too thin, anemic). Focus on cohesion: each component should have a clear, single reason to exist.
Deliverable: Revised component list with clear responsibility statements, noting any splits or merges.
Step 4: Analyze Architectural Characteristics per Component
For each component, identify the relevant architectural characteristics from Chapter 7’s scoping work:
- What availability SLA does this component need?
- What scalability requirements does it have?
- What consistency model is appropriate?
- What performance targets must it meet?
Components with dramatically different characteristic profiles may need to be separated (different quanta). Components with identical profiles that collaborate closely may be candidates for merging.
Deliverable: A characteristics profile per component, with quantum boundaries annotated.
Step 5: Restructure Components Based on Analysis
With the information from steps 1–4, restructure the component list:
- Split components with multiple distinct responsibilities or divergent characteristic profiles
- Merge components that are overly granular and share the same profile
- Add missing components identified in step 2
- Rename components to better reflect their business purpose (not their technical implementation)
Iterate: steps 2–5 are not strictly linear. Expect multiple passes.
Deliverable: The finalized logical component architecture, ready for physical mapping.
Component Coupling
Understanding coupling between components is essential for evaluating the quality of a component architecture. Two forms are discussed:
Static Coupling (Compile-Time Coupling)
Static coupling exists when one component has a compile-time dependency on another — it imports, calls, or references the other component’s code directly. This creates a hard dependency that must be managed at build and deployment time.
- High static coupling → components must be versioned and deployed carefully together
- Low static coupling → components can evolve independently
- Goal: minimize static coupling across component (and quantum) boundaries; it is acceptable within a component
Tools for managing static coupling: dependency inversion, interface-based programming, shared-nothing data ownership.
Temporal Coupling (Runtime Availability Dependency)
Temporal coupling (also called runtime availability coupling) exists when one component can only function if another component is available at the same moment. Classic form: synchronous API calls.
- If A calls B synchronously, A is temporally coupled to B
- B’s downtime directly causes A’s failures
- A must scale in proportion to B’s capacity
Temporal coupling is the runtime manifestation of what, at the architectural level, merges quanta. It is more dangerous than static coupling because it affects system resilience at runtime, not just build-time.
Mitigating temporal coupling: asynchronous messaging, caching, circuit breakers, bulkhead patterns.
Law of Demeter
The Law of Demeter (Principle of Least Knowledge) states that a component should only interact with its immediate collaborators — it should not reach through one component to interact with another component’s internals.
Formally: A method M of object O should only invoke methods of:
- O itself
- Objects passed as parameters to M
- Objects M creates
- O’s direct components/fields
In component architecture terms: Component A should not know the internal structure of Component B just to reach Component C. A should talk to B; B should expose what C needs through its own interface.
Architectural Implications
- Violating the Law of Demeter creates hidden coupling — A becomes dependent on the internal structure of B and the existence of C, even though none of this is expressed in A’s declared dependencies.
- This makes refactoring brittle: changing C’s interface or B’s internal structure breaks A.
- In distributed systems, this manifests as services that make multiple hops to compose a response, creating deep synchronous chains.
Solution: Design component interfaces that expose the data/behavior needed by callers, rather than exposing internal structure. Use façades and service layers to shield callers from internal complexity.
Case Study: Going Going Gone (Online Auction System)
“Going Going Gone” is the primary case study in Chapter 8. It is an online auction platform and serves as a vehicle for demonstrating the full component discovery process.
System Description
The system supports live online auctions. Key behaviors:
- Auctioneers run live auctions, advancing lot by lot
- Bidders watch a live auction stream and place bids in real time
- Bids are captured and validated in near-real time
- The system tracks the current highest bid and notifies participants
- Online bidders compete with in-room bidders
Identifying Core Components (Step 1)
Reading the requirements, the following candidate components emerge:
| Component | Responsibility |
|---|---|
| Bidder | Represents an online bidder; manages bidder profile and authentication |
| Auctioneer | Represents the auctioneer; manages lot progression and auction control |
| Auction | Core auction lifecycle — open, active, closed states per lot |
| Bid Stream | Streams live auction activity to online bidders in real time |
| Bid Capture | Receives and validates incoming bids from online bidders |
| Bid Tracker | Tracks current highest bid per lot, enforces bid increment rules |
Component Architecture Diagram
┌─────────────────────────────────────────────┐
│ GOING GOING GONE SYSTEM │
└─────────────────────────────────────────────┘
┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ Auctioneer │────────▶│ Auction │───────▶│ Bid Tracker │
│ Component │ │ Component │ │ Component │
│ │ │ │ │ │
│ - Login │ │ - Open auction │ │ - Track high bid │
│ - Advance │ │ - Advance lot │◀───────│ - Enforce incr. │
│ lot │ │ - Close lot │ │ - Notify winner │
│ - Close │ │ - Close auction │ └──────────────────┘
│ auction │ └──────────────────┘ ▲
└──────────────┘ │ │
│ lot state │ bid valid/
▼ changes │ rejected
┌──────────────────┐ ┌──────────────────┐
│ Bid Stream │ │ Bid Capture │
│ Component │ │ Component │
│ │ │ │
│ - Push lot state │ │ - Receive bids │
│ to bidders │ │ - Validate bids │
│ - Push bid │ │ - Forward valid │
│ updates │ │ bids │
└──────────────────┘ └──────────────────┘
▲ ▲
│ subscribe │ submit bid
│ │
┌──────────────────────────────────────────────┐
│ Bidder Component │
│ │
│ - Bidder registration & authentication │
│ - Watch live auction stream │
│ - Submit bids │
│ - View bid history │
└──────────────────────────────────────────────┘
Analyzing Roles and Responsibilities (Step 3)
Auction vs. Bid Tracker: Initially, bid tracking might be lumped into Auction. But Auction is about lifecycle management (open/advance/close), while Bid Tracker is about real-time bid state. These have different operational characteristics (Bid Tracker needs very low latency), so they are separated.
Bid Stream vs. Bid Capture: Both relate to bids. But Bid Stream is outbound (pushing data to bidders) while Bid Capture is inbound (receiving bids from bidders). They have different scalability profiles and communication directions, warranting separation.
Analyzing Architectural Characteristics (Step 4)
| Component | Availability | Scalability | Latency | Consistency |
|---|---|---|---|---|
| Bidder | Moderate | High (many concurrent bidders) | Normal | Eventual |
| Auctioneer | High | Low (few auctioneers) | Normal | Strong |
| Auction | High | Moderate | Normal | Strong |
| Bid Stream | High | Very High (many subscribers) | Very Low | Eventual |
| Bid Capture | High | Very High (bid burst) | Very Low | Strong |
| Bid Tracker | High | Moderate | Very Low | Strong |
Key observation: Bid Stream and Bid Capture have very different characteristic profiles from Bidder management and Auction lifecycle. In a microservices deployment, Bid Stream and Bid Capture would be separate quanta with dedicated scaling and latency optimization.
Restructuring (Step 5)
The initial six-component decomposition holds well after analysis. Key decisions:
- Bid Stream communicates asynchronously (push/WebSocket/SSE) to Bidder — this creates a quantum boundary
- Bid Capture to Bid Tracker is synchronous in the initial design (low-latency validation) — they share a quantum
- Auction to Bid Tracker communication is synchronous (lot state changes must propagate immediately) — same quantum
- Auctioneer communicates with Auction synchronously — same quantum
Resulting Quanta
In a microservices deployment:
- Quantum 1: Bidder (registration, auth, profile) — moderate availability, high read scalability
- Quantum 2: Auction + Auctioneer + Bid Tracker (core auction logic) — high availability, strong consistency, low latency
- Quantum 3: Bid Stream (outbound streaming) — very high scalability, very low latency, eventual consistency
- Quantum 4: Bid Capture (inbound bid processing) — very high scalability, very low latency, strong consistency for bid validation
Component Granularity Guidelines
There is no formula for the “right” component size, but the following heuristics guide good judgment:
| Signal | Implication |
|---|---|
| Component has multiple unrelated responsibilities | Split it |
| Component is referenced by every other component | May be an infrastructure concern; consider a shared library or service |
| Two components always change together | Consider merging |
| Component has very different characteristics from its collaborators | Separate quantum candidate |
| Component is too thin (just passes data through) | Merge with the component it serves |
| Component maps directly to a database entity | Entity trap — reconsider responsibility orientation |
Trade-offs and Decision Framework
| Option | Pros | Cons | When to Choose |
|---|---|---|---|
| Coarse-grained components (fewer, larger) | Simpler dependency management, easier transactions, less overhead | Harder to scale independently, larger blast radius for changes | Early design, monolithic deployment, tight team |
| Fine-grained components (more, smaller) | Independent scalability, clear single responsibility, easier to replace | More interfaces to manage, more coordination overhead, risk of quantum sprawl | Clear distinct operational profiles, microservices deployment |
| Logical-first design | Clear communication artifact, defers premature physical decisions | Extra design step, may feel slow | All greenfield systems, any complex system |
| Physical-first design | Faster to “something concrete”, familiar for infra-oriented teams | Premature commitment, structural mistakes hard to fix | Rarely recommended; only for tiny systems |
| Entity-oriented components | Easy to reason about data | Leads to anemic services, entity trap, poor cohesion | Never for behavior-rich domains |
| Capability-oriented components | High cohesion, maps to business language, natural team ownership | Requires domain understanding, harder upfront | Always preferred for business systems |
Common Antipatterns
Entity Trap: Designing components that mirror database tables (CustomerComponent, OrderComponent). Results in CRUD-only components with no business logic cohesion and leads to a procedural-style “service layer” that does all the real work.
God Component: One component absorbs 50%+ of the system’s responsibilities. Often called “Service” or “Manager.” Reveals insufficient decomposition effort. Split by identifying distinct business capabilities.
Chatty Components: Components with many fine-grained synchronous calls between them, creating high temporal coupling. Often signals that the two components should be merged or that the interface needs to be redesigned around coarser-grained operations.
Premature Physical Architecture: Jumping straight to “we’ll use microservices with Kubernetes” without first doing logical component design. Results in deployment-driven decomposition rather than responsibility-driven decomposition.
Implicit Dependencies: Components that depend on each other’s internal implementation details rather than declared interfaces. Violates the Law of Demeter and creates hidden coupling that makes refactoring dangerous.
Key Takeaways
- Component: The fundamental building block of architecture — a named, cohesive grouping of related code that encapsulates a business capability and exposes behavior through an interface.
- Logical Architecture First: Define logical components (technology-agnostic, responsibility-focused) before committing to physical deployment decisions. Premature physical decisions create structural debt.
- Entity Trap: The antipattern of mapping components directly to database entities, producing anemic CRUD services instead of behavior-rich capability components.
- 5-Step Process: Identify core components → assign user stories → analyze roles → analyze architectural characteristics → restructure. Iterate, especially steps 2–5.
- Static Coupling: Compile-time dependency between components; must be managed through interfaces and careful versioning. Minimize across component boundaries.
- Temporal Coupling: Runtime availability dependency — if A calls B synchronously, A cannot function without B. More dangerous than static coupling because it affects production resilience.
- Law of Demeter: A component should interact only with its immediate collaborators, not reach through them to access internal structure. Violations create hidden, brittle coupling.
- Component Granularity: No formula exists; use responsibility cohesion, user story mapping, and characteristics profiles to determine appropriate size. Watch for god components and overly thin components.
- Going Going Gone: The auction case study demonstrates that real-time bidding naturally decomposes into distinct components (Bid Capture, Bid Stream, Bid Tracker, Auction) with different operational profiles, each deserving independent design attention.
- Components Inform Quanta: After logical component design, analyzing operational characteristic profiles per component reveals natural quantum boundaries for the physical architecture.
Related Resources
- Chapter 7: Scope of Architectural Characteristics (quantum boundaries from component profiles)
- Chapter 9: Foundations of Architecture Styles (how components are deployed in each style)
- Chapter 10: Layered Architecture (component organization in layered style)
- Chapter 17: Microservices Architecture (component-to-service mapping in microservices)
- Richards & Ford: “Software Architecture Patterns” (O’Reilly) — supplementary component patterns
Last Updated: 2026-05-29