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:

  1. O itself
  2. Objects passed as parameters to M
  3. Objects M creates
  4. 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:

ComponentResponsibility
BidderRepresents an online bidder; manages bidder profile and authentication
AuctioneerRepresents the auctioneer; manages lot progression and auction control
AuctionCore auction lifecycle — open, active, closed states per lot
Bid StreamStreams live auction activity to online bidders in real time
Bid CaptureReceives and validates incoming bids from online bidders
Bid TrackerTracks 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)

ComponentAvailabilityScalabilityLatencyConsistency
BidderModerateHigh (many concurrent bidders)NormalEventual
AuctioneerHighLow (few auctioneers)NormalStrong
AuctionHighModerateNormalStrong
Bid StreamHighVery High (many subscribers)Very LowEventual
Bid CaptureHighVery High (bid burst)Very LowStrong
Bid TrackerHighModerateVery LowStrong

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:

SignalImplication
Component has multiple unrelated responsibilitiesSplit it
Component is referenced by every other componentMay be an infrastructure concern; consider a shared library or service
Two components always change togetherConsider merging
Component has very different characteristics from its collaboratorsSeparate quantum candidate
Component is too thin (just passes data through)Merge with the component it serves
Component maps directly to a database entityEntity trap — reconsider responsibility orientation

Trade-offs and Decision Framework

OptionProsConsWhen to Choose
Coarse-grained components (fewer, larger)Simpler dependency management, easier transactions, less overheadHarder to scale independently, larger blast radius for changesEarly design, monolithic deployment, tight team
Fine-grained components (more, smaller)Independent scalability, clear single responsibility, easier to replaceMore interfaces to manage, more coordination overhead, risk of quantum sprawlClear distinct operational profiles, microservices deployment
Logical-first designClear communication artifact, defers premature physical decisionsExtra design step, may feel slowAll greenfield systems, any complex system
Physical-first designFaster to “something concrete”, familiar for infra-oriented teamsPremature commitment, structural mistakes hard to fixRarely recommended; only for tiny systems
Entity-oriented componentsEasy to reason about dataLeads to anemic services, entity trap, poor cohesionNever for behavior-rich domains
Capability-oriented componentsHigh cohesion, maps to business language, natural team ownershipRequires domain understanding, harder upfrontAlways 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

  1. 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.
  2. Logical Architecture First: Define logical components (technology-agnostic, responsibility-focused) before committing to physical deployment decisions. Premature physical decisions create structural debt.
  3. Entity Trap: The antipattern of mapping components directly to database entities, producing anemic CRUD services instead of behavior-rich capability components.
  4. 5-Step Process: Identify core components → assign user stories → analyze roles → analyze architectural characteristics → restructure. Iterate, especially steps 2–5.
  5. Static Coupling: Compile-time dependency between components; must be managed through interfaces and careful versioning. Minimize across component boundaries.
  6. 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.
  7. 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.
  8. 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.
  9. 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.
  10. Components Inform Quanta: After logical component design, analyzing operational characteristic profiles per component reveals natural quantum boundaries for the physical architecture.

  • 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