Chapter 20 Flashcards — Architectural Patterns
flashcards fsa architectural-patterns
What is the difference between an architecture style and an architecture pattern?
?
An architecture style defines the overall structure of a whole system (e.g., microservices, layered). An architecture pattern is a reusable structural solution to a recurring design problem within or across components (e.g., CQRS, Broker-Domain, Sidecar). A system uses one primary style but may employ many patterns simultaneously. Styles answer “what does the whole system look like?”; patterns answer “how do I solve this specific structural problem here?”
How do architectural patterns differ from GoF design patterns?
?
GoF (Gang of Four) design patterns (Observer, Factory, Strategy) operate at the code level — solving local object-oriented design problems within a class or module. Architectural patterns operate at the structural level — solving how components communicate, how data flows between services, and how responsibilities are divided across system boundaries. The scope difference: within a component vs. across components/services.
What is the core problem that sidecar and service mesh patterns solve?
?
Operational concerns (logging, auth, retries, distributed tracing, metrics, mTLS) must not be embedded in domain services, but also cannot be shared via a common library without creating build-time coupling. The sidecar pattern delegates operational behavior to a co-located proxy container that intercepts network traffic, leaving domain code with zero operational plumbing. Service mesh scales this across the entire cluster with a centralized control plane.
What is the sidecar pattern?
?
The sidecar pattern deploys a separate process/container alongside each service in the same deployment unit (e.g., same Kubernetes pod). The sidecar intercepts and augments the service’s network traffic — handling auth, logging, retries, metrics, and tracing. The domain service is unaware of the sidecar’s existence and contains no infrastructure code. The sidecar is language-agnostic and upgradeable independently of the domain service.
What is a service mesh and how does it relate to the sidecar pattern?
?
A service mesh is the platform-level implementation of the sidecar pattern at scale. Every service in the cluster receives a sidecar proxy (e.g., Envoy in Istio), and a control plane manages configuration across all proxies. The mesh provides service discovery, load balancing, mTLS, circuit breaking, retries, and distributed tracing — all transparently. Domain code contains zero infrastructure plumbing; the mesh handles it centrally.
What is the distinction between domain services and operational services?
?
Domain services implement business logic — the “what the business does.” They should be pure expressions of business capability with no infrastructure concerns. Operational services handle infrastructure concerns — logging, monitoring, auth, rate limiting. The key principle: operational concerns must not leak into domain services. When they do, domain code becomes polluted — harder to test, change, and reason about.
What are the three mechanisms for operational reuse, ordered by increasing separation?
?
- Shared libraries: versioned packages with operational behavior. Simple, no network overhead, but creates build-time coupling and forces coordinated upgrades across services.
- Sidecar pattern: co-located proxy handles operational concerns without service knowledge. Language-agnostic, independently upgradeable, but adds deployment complexity.
- Service mesh: platform-level sidecar management via control plane. Complete domain/operational separation, centrally managed, but significant operational complexity to manage the mesh itself.
What is the orchestration communication pattern?
?
Orchestration uses a central orchestrator component that explicitly directs the actions of other services in a workflow. The orchestrator knows the entire workflow, calls each participant in sequence or in parallel, manages the state machine, handles errors, and aggregates results. The workflow logic is visible and explicit in the orchestrator — easy to understand and modify, but creates a single point of failure and coupling between participants and the orchestrator.
What is the choreography communication pattern?
?
Choreography coordinates services through events — each service listens for events it cares about, reacts to them, and emits new events. No central coordinator exists; the overall workflow emerges from the sum of event reactions. Services are decoupled (each knows only its input events and output events, not the larger workflow). Highly scalable but difficult to trace — the full workflow is not visible in any single place.
What is the key trade-off between orchestration and choreography?
?
Orchestration provides explicit workflow control, centralized error handling, and easy traceability — at the cost of coupling (all participants depend on the orchestrator) and a single point of failure. Choreography provides loose coupling, independent scalability, and no central bottleneck — at the cost of emergent, harder-to-trace workflows and complex distributed error handling (requiring Saga-style compensating events).
When should you prefer orchestration over choreography?
?
Prefer orchestration when: (1) the workflow has complex conditional logic or many sequential steps; (2) error handling requires complex rollbacks or compensation; (3) traceability and audit trails are critical; (4) the team is small and workflow visibility matters; (5) the throughput requirement is moderate. Start with orchestration for clarity and correctness; migrate to choreography only when coupling or scalability demands it.
When should you prefer choreography over orchestration?
?
Prefer choreography when: (1) scalability and throughput are the primary concerns; (2) services must evolve completely independently; (3) eventual consistency is acceptable; (4) operations are idempotent and retries suffice for error handling; (5) the workflow is a simple linear reactive chain rather than a complex state machine.
What is CQRS?
?
CQRS (Command Query Responsibility Segregation) separates the write model (commands — operations that mutate state) from the read model (queries — operations that return data) into distinct components. The write model is optimized for consistency and validation; the read model is optimized for query performance and denormalization. Each can use different technologies, scale independently, and evolve separately.
Why does CQRS improve performance and scalability?
?
Read and write workloads differ dramatically: writes require strict consistency and complex validation; reads need fast, denormalized access optimized for query patterns. A single model cannot be simultaneously optimized for both. CQRS allows: (1) the read store to use query-optimized technology (Elasticsearch, Redis, graph DB); (2) the read side to be scaled out independently of the write side; (3) multiple different read models serving different query needs from the same write model.
What is event sourcing and how does it complement CQRS?
?
Event sourcing stores state as an ordered log of events (not the current value) — the current state is derived by replaying the event log. Combined with CQRS: the write side appends events (commands produce events); the read side maintains projections (materialized views) built from the event log. Benefits: complete audit trail, ability to rebuild read models at any time, retroactive new projections, and time-travel queries (reconstruct state at any past moment).
What are the trade-offs of using CQRS?
?
| Benefit | Cost |
|---|---|
| Independently optimized read/write models | Eventual consistency — read model may lag writes |
| Independent scaling | Increased complexity — two models, synchronization |
| Read store technology freedom | Developer learning curve |
| Natural audit trail (with event sourcing) | Synchronization failure handling |
CQRS adds complexity only justified when read/write asymmetry exists or when audit trails are required.
When should you use CQRS?
?
Use CQRS when: (1) read and write workloads are asymmetric in scale (e.g., 1000:1 reads to writes); (2) multiple different query patterns must be simultaneously optimized; (3) an audit trail is a business requirement; (4) event sourcing is desired; (5) the domain has complex write-side business rules (DDD aggregates). Avoid it for simple symmetric CRUD operations or when the team is small and eventual consistency adds more confusion than value.
When is CQRS a bad fit?
?
CQRS is a bad fit when: (1) CRUD operations are simple and symmetric; (2) the team is small and maintaining two models adds confusion without commensurate benefit; (3) immediate consistency of the read model is a business requirement (e.g., financial balances that must reflect the latest write instantaneously); (4) the domain lacks the complexity to justify the separation overhead.
What is the Broker-Domain Pattern?
?
The Broker-Domain Pattern separates messaging infrastructure concerns from domain logic into distinct components within the same service. The broker component handles connection management, topic subscriptions, message deserialization, acknowledgment, and dead-letter queues. The domain component receives domain-neutral events from the broker component and executes business logic with no knowledge of messaging infrastructure. Domain code contains no Kafka, AMQP, or queue-specific code.
What problem does the Broker-Domain Pattern solve?
?
Without this pattern, services couple their domain logic to the messaging infrastructure — containing Kafka consumer APIs, topic names, consumer group configuration, and acknowledgment logic directly in business code. This causes: operational concerns leaking into domain code; inability to test domain logic without a real broker; tight coupling to specific broker technology; and mixed responsibilities that make the code harder to understand and change.
What are the benefits of applying the Broker-Domain Pattern?
?
- Testability: domain component can be unit tested by injecting domain events directly — no message broker required
- Technology independence: changing from Kafka to RabbitMQ only requires rewriting the broker component
- Clear ownership: infrastructure engineers own the broker component; domain engineers own the domain component
- Evolvability: domain logic evolves completely independently of broker infrastructure plumbing
How does domain/operational separation scale from shared libraries to service mesh?
?
Three levels of separation with increasing power and complexity: (1) Shared libraries — operational behavior in versioned packages; simple but creates build-time coupling across all services. (2) Sidecar — operational proxy co-located per service; no domain coupling, language-agnostic, but per-service deployment complexity. (3) Service mesh — platform-level sidecar management; complete centralized control with full observability, but the mesh itself becomes a complex system to operate.
Can architectural patterns be used across multiple architecture styles?
?
Yes. Architectural patterns are style-agnostic — the same pattern applies in multiple styles. CQRS is useful in both microservices and modular monoliths. The broker-domain pattern applies in event-driven and service-based architectures. The sidecar pattern appears in microservices and service-based architectures. Patterns solve recurring structural problems regardless of the overall style, which is why they are distinct from styles.
What is the relationship between choreography and the Saga pattern?
?
Choreography-based workflows require distributed error handling because no central orchestrator manages rollbacks. The Saga pattern provides a mechanism for this: each step emits a compensating event if it fails, which triggers previous steps to undo their changes. Without the Saga pattern, choreography has no systematic way to handle partial failures in multi-step workflows — making it unsuitable for workflows requiring transactional consistency.
What structural boundary does the Broker-Domain Pattern enforce?
?
The pattern enforces a strict boundary: broker component translates broker-specific messages into domain-neutral DTOs/events before handing them to the domain component. The domain component only sees plain domain objects — never broker-specific types, offsets, headers, or acknowledgment handles. This translation layer is the key mechanism that prevents infrastructure leakage into domain logic.