Chapter 5 Flashcards — Component-Based Decomposition Patterns

flashcards saht decomposition component-patterns fitness-functions


What is a “component” in the context of Chapter 5’s decomposition patterns?
?
A component is a top-level namespace or package in the codebase — the architectural unit of analysis for decomposition. Components map to domain concepts (e.g., ss.ticket, ss.billing) and sit one level below the root namespace. Service extraction decisions are made at the component level, not the class or method level.


What is the goal of the Identify and Size Components pattern (Pattern 1)?
?
To produce an inventory of all components in the monolith and assess whether each is appropriately sized. A component that is too large (>~10-15% of codebase) mixes too many responsibilities; a component that is too small (<~1-2%) is likely a fragment that belongs inside another component. Correct sizing is a prerequisite for all subsequent patterns.


What two size signals indicate a component is problematic in Pattern 1?
?

  1. Too large: the component exceeds a configurable percentage of total codebase size (rough guide: >10-15%). It likely holds mixed responsibilities and will be hard to extract cleanly.
  2. Too small: the component is below ~1-2% of total codebase size. It is probably a sub-function fragment that belongs inside a neighboring component rather than standing alone.

What fitness functions govern Pattern 1 (Identify and Size Components)?
?

  • Component size fitness function: fails CI if any single component exceeds a configured percentage of total codebase size.
  • Component count fitness function: alerts when total component count falls outside an expected range, catching both collapse (too few) and fragmentation (too many).

What is the goal of the Gather Common Domain Components pattern (Pattern 2)?
?
To identify shared infrastructure/utility code (logging, database utilities, authentication, email/SMS helpers) that is scattered or duplicated across multiple components, and consolidate it into a single common domain namespace. This prevents duplication in extracted services and makes the shared code’s deployment model explicit.


What are the two deployment options for the common domain component identified in Pattern 2, and what is the key trade-off?
?

  1. Shared library: bundled into each service at build time. Fast, no network latency. Downside: changing the library requires redeploying all services (deployment coupling).
  2. Shared service: deployed independently, called at runtime. Independent deployability. Downside: adds network latency and introduces a new failure point.
    The right choice depends on how frequently the shared code changes and how critical latency is.

What fitness functions govern Pattern 2 (Gather Common Domain Components)?
?

  • Common coupling fitness function: alerts when any non-common component directly references classes that should live in the common domain (prevents drift back to scattered utility code).
  • Shared library version drift: flags when different services depend on different versions of the shared library, preventing compatibility divergence.

What is the goal of the Flatten Components pattern (Pattern 3)?
?
To collapse deep namespace hierarchies to a single level of nesting below the root namespace (root.domain). Deep hierarchies (3-5 levels) obscure component boundaries and make dependency analysis unreliable. After flattening, every second-level namespace is an explicit, visible component; everything below that is internal implementation detail.


What is the target namespace depth after applying Pattern 3, and what happens to orphan classes?
?
The target depth is two levels: <root>.<domain> (e.g., ss.ticket). Everything below <domain> is internal to that component. Orphan classes — classes found directly in the root namespace without a domain segment — must be assigned to the most appropriate second-level component or moved to ss.common.


What fitness functions govern Pattern 3 (Flatten Components)?
?

  • Hierarchy depth fitness function: fails the build if any source file is found at nesting depth greater than 2 below root, preventing re-growth of deep hierarchies.
  • Orphan class fitness function: flags any class living in the root namespace rather than a named component.

What are afferent coupling (Ca) and efferent coupling (Ce)?
?

  • Afferent coupling (Ca): the number of other components that depend on this component (incoming dependencies). High Ca = many callers; changing this component breaks many things.
  • Efferent coupling (Ce): the number of other components that this component depends on (outgoing dependencies). High Ce = this component relies on many things.
    These metrics come from Robert Martin and are the primary inputs to Pattern 4.

What is the instability metric (I) and how is it calculated?
?
Instability (I) = Ce / (Ca + Ce). Range: 0 to 1.

  • I ≈ 0 (stable): many dependents, few outgoing dependencies. This component is hard to change without impacting others — it should be very stable.
  • I ≈ 1 (unstable): few dependents, many outgoing dependencies. This component is easy to change but depends on many things.
    Used in Pattern 4 to understand which components are safe to change and which carry high impact.

What is the goal of the Determine Component Dependencies pattern (Pattern 4)?
?
To build a dependency graph of all components and their relationships, then identify and eliminate dependency tangles — particularly circular dependencies — that would prevent clean service extraction. Pattern 4 produces a directed acyclic graph (DAG) of components that can be analyzed for domain groupings in Pattern 5.


What is a circular dependency and why is it the most dangerous finding in Pattern 4?
?
A circular dependency is when component A depends on B, B depends on C, and C depends on A (A → B → C → A). It is the most dangerous finding because components in a cycle cannot be independently deployed — they are functionally inseparable. Any service boundary drawn through a cycle will require synchronous cross-service calls that recreate the same coupling in a more expensive distributed form.


What are three techniques for breaking circular dependencies found in Pattern 4?
?

  1. Extract a shared component: move the code that A and B both need into a new neutral component. Both depend on it; the cycle is broken.
  2. Apply Dependency Inversion: introduce an interface in a neutral package. A and B depend on the interface, not on each other.
  3. Merge: if A and B are tightly coupled because they represent one cohesive concept, merge them into a single component and revisit sizing (Pattern 1).

What fitness functions govern Pattern 4 (Determine Component Dependencies)?
?

  • No circular dependencies: fails CI if any circular dependency is introduced (ArchUnit, JDepend, Dependency Cruiser). Should remain permanently enabled.
  • Maximum afferent coupling threshold: alerts when a non-common component’s Ca exceeds a threshold, suggesting it is taking on shared-domain responsibilities.
  • Maximum efferent coupling threshold: alerts when a component’s Ce exceeds a threshold, suggesting its boundaries are too broad.

What is the goal of the Create Component Domains pattern (Pattern 5)?
?
To group related components into named domains (bounded contexts) based on shared business capability. A domain becomes the unit of service extraction in Pattern 6. Components within a domain can call each other freely (intra-domain); components in different domains must use defined interfaces (cross-domain). This grouping transforms the component inventory into a service candidate map.


What test determines whether two components belong in the same domain (Pattern 5)?
?
Two components belong in the same domain if:

  1. They share the same primary entity (e.g., both work primarily with Ticket).
  2. They are always deployed together in practice.
  3. Separating them would require constant synchronous cross-service calls — the network overhead would make independent services impractical.
    If two components meet these criteria, forcing them into different domains creates more problems than it solves.

What fitness functions govern Pattern 5 (Create Component Domains)?
?

  • Cross-domain dependency fitness function: alerts when the ratio of cross-domain to intra-domain dependencies for a domain exceeds a threshold. A domain calling more things outside itself than inside is likely mis-bounded.
  • Domain namespace fitness function: enforces that component namespaces in source code match their declared domain assignment in the architecture decision record.

What is the goal of the Create Domain Services pattern (Pattern 6)?
?
To extract each domain identified in Pattern 5 into a separately deployable service, one domain at a time, starting with the least-coupled domain. Pattern 6 covers ranking extraction order, choosing an extraction strategy (strangler fig or branch by abstraction), defining the service API, migrating data, and retiring the monolith code for that domain.


How should teams sequence domain extraction in Pattern 6, and why?
?
Teams should extract domains in ascending order of coupling — the domain with the fewest cross-domain dependencies is extracted first. This minimizes risk because:

  • Fewer callers in the monolith need to be updated.
  • The extracted service has fewer runtime dependencies on the remaining monolith.
  • Errors in the extraction process affect a smaller blast radius.
    TicketManagement (high coupling, shared data) goes last; Reporting (low coupling, read-only data) goes first.

What are the two main extraction strategies in Pattern 6?
?

  1. Branch by abstraction: introduce an interface in front of the domain. The monolith calls the interface. Later, swap the implementation from an in-process class to a remote service call. The monolith continues to work throughout.
  2. Strangler fig: route specific endpoints or requests to the new service while the monolith handles others. Progressively migrate endpoints until the monolith’s portion of the domain is fully replaced. Named after the strangler fig tree that grows around a host and eventually replaces it.

What fitness functions govern Pattern 6 (Create Domain Services)?
?

  • Service dependency fitness function: enforces that the extracted service’s code has no compile-time imports back into the monolith codebase.
  • Data ownership fitness function: alerts when a service accesses database tables declared as owned by another service.
  • API consumer-driven contract fitness function: uses tools like Pact to verify the service’s API matches all consumer expectations.

What are fitness functions in the context of evolutionary architecture?
?
Fitness functions are automated mechanisms that evaluate a specific architectural property and produce a pass/fail result (or metric). The term is borrowed from evolutionary computation, where a fitness function scores how well a candidate solution meets an objective. In software architecture, they are implemented as CI checks using tools like ArchUnit (Java), Dependency Cruiser (JavaScript), JDepend, SonarQube, or Pact. They prevent architectural drift by catching violations at commit time rather than during architectural reviews.


What makes a fitness function effective for architectural governance?
?
An effective fitness function is:

  • Automated: runs without human judgment — triggered in CI/CD.
  • Fast: completes within the normal build window.
  • Specific: tests exactly one architectural property.
  • Threshold-based where possible: expressed as a metric with a configurable threshold rather than a binary rule, so thresholds can be tightened incrementally as the codebase matures.

What is the full six-pattern workflow for component-based decomposition?
?

  1. Identify and Size Components — inventory and size-check all namespaces.
  2. Gather Common Domain Components — extract shared infrastructure into ss.common.
  3. Flatten Components — collapse deep hierarchies to root.domain depth.
  4. Determine Component Dependencies — build dependency graph; break circular deps.
  5. Create Component Domains — group components into business-capability domains.
  6. Create Domain Services — extract each domain as a deployable service, least-coupled first.
    The patterns are sequential but may require iteration: findings in later patterns can send the team back to earlier ones.

Why does the book recommend starting service extraction with the least-coupled domain rather than the most business-critical one?
?
Starting with the least-coupled domain reduces risk at every step: fewer callers in the monolith break, less data needs migrating, the new service has fewer runtime dependencies to manage. Starting with the most business-critical domain (often the most-coupled) would maximize blast radius for any extraction error and potentially destabilize the monolith that the business depends on. Build confidence with easy extractions first; tackle complex ones when the team has established patterns and tooling.


What is the key difference between intra-domain and cross-domain dependencies, and why does it matter?
?
Intra-domain dependencies are calls between components within the same domain — these become in-process calls inside a single service (fast, cheap, reliable). Cross-domain dependencies are calls between components in different domains — these become network calls between services (slower, more complex, potential failure points). A domain where most dependencies are cross-domain is likely mis-bounded: it either belongs with another domain or is missing a shared component. Minimizing cross-domain dependencies is the primary design goal of Pattern 5.


In the Sysops Squad Saga, what was the circular dependency found in Pattern 4, and how was it resolved?
?
ss.ticketmgmt queried customer data from ss.customer, while ss.customer called back into ss.ticketmgmt to update customer status after ticket resolution — creating a cycle. Resolution: a new ss.customerprofile component was extracted to own read-side customer data. ss.ticketmgmt reads from ss.customerprofile but never writes back. The status write-back was moved to an asynchronous event, eliminating the synchronous cycle.


What were the five domains the Sysops Squad defined in Pattern 5, and which was extracted first in Pattern 6?
?
The five domains were: TicketManagement, CustomerManagement, ExpertManagement, BillingAndPayments, and Reporting. ss.common was a shared library outside all domains. Reporting was extracted first in Pattern 6 — it had the fewest inbound dependencies, owned its own data tables, and could tolerate slight data staleness (read-only analytics). TicketManagement was extracted last due to its high coupling and shared data.


What is the strangler fig pattern and why does the book prefer it over a big-bang rewrite?
?
The strangler fig pattern incrementally replaces a monolith by routing traffic to a new service for specific features while the monolith handles the rest. Over time, the monolith’s responsibilities are progressively strangled until it is fully replaced. The book prefers it because: (1) the monolith continues to operate throughout migration, (2) each step can be validated independently, (3) failure in any step is recoverable, and (4) the team builds confidence and operational muscle progressively. Big-bang rewrites require the entire system to work at once before any value is delivered, and have a high historical failure rate.


How does data ownership relate to service extraction in Pattern 6?
?
Each domain service must eventually own its own database schema — no other service should query its tables directly. During extraction, the team must identify which tables the domain owns, move those tables to a dedicated database, and use dual-write or event-sourcing patterns during the migration window to keep data consistent. Domains whose tables are heavily shared with other components are the hardest to extract and should be tackled last. The data ownership fitness function enforces no cross-schema queries in production.


Total Cards: 30
Priority: HIGH
Last Updated: 2026-05-30