Skip to main content

Module 4: Structural and Creational Patterns: Case Studies

These cases treat patterns as design tools, not decorations. Use them when construction, composition, or interface boundaries are creating real change pressure.


Case Study 1: Adapter for a Payment Provider Migration

Scenario: A checkout system uses Provider A's chargeCard(customerId, amountCents) API. Provider B uses payment intents, idempotency keys, and asynchronous confirmation. The team wants to migrate without rewriting checkout logic.

Source anchor: Design Patterns Catalog - Refactoring.Guru.

Module concepts:

  • Adapter
  • boundary interfaces
  • third-party API isolation
  • contract tests

Wrong Approach

Expose Provider B objects throughout checkout and update every caller to know payment intents.

Better Approach

Define the application's payment port first: authorize, capture, refund, and query status. Implement Provider A and Provider B adapters behind that contract, and write contract tests so both adapters obey the same application-level semantics.

Tradeoff Table

ChoiceGainCost
Direct provider useFast integrationVendor lock-in across app
AdapterStable app contractMapping edge cases need tests
Full abstraction platformMaximum flexibilityMay overbuild early

Failure Mode

Provider B's async confirmation leaks into checkout controllers and forces UI code to know provider-specific states.

Required Artifact

Write a payment adapter contract with operations, error mapping, idempotency behavior, and contract-test cases.

Project / Capstone Connection

Use this contract whenever the project touches an external API.


Case Study 2: Decorator for Caching and Rate Limits

Scenario: A product catalog client calls a slow remote API. Engineers add caching, logging, retries, and rate limiting directly into CatalogClient, making it difficult to test each concern.

Source anchor: Design Patterns Catalog - Refactoring.Guru.

Module concepts:

  • Decorator
  • composition
  • cross-cutting behavior
  • ordering effects

Wrong Approach

Keep adding flags to CatalogClient: useCache, logRequests, retry, respectRateLimit.

Better Approach

Keep a simple CatalogPort and wrap it with decorators such as CachedCatalog, RateLimitedCatalog, RetryingCatalog, and LoggingCatalog. Decide wrapper order deliberately because caching before rate limiting behaves differently from rate limiting before caching.

Tradeoff Table

ChoiceGainCost
Flags in one classFewer objectsCombinatorial complexity
DecoratorsIsolated concernsWrapper order matters
Middleware frameworkConsistent pipelineMore infrastructure

Failure Mode

Retries happen inside the rate limiter and burst through the vendor limit.

Required Artifact

Draw a decorator stack diagram with call order, failure behavior, metrics emitted, and tests for order-sensitive behavior.

Project / Capstone Connection

Use the same stack diagram for caching, logging, or authorization wrappers.


Case Study 3: Facade for Report Generation

Scenario: Monthly finance reports require data from billing, tax, currency conversion, PDF rendering, and object storage. Controllers call all subsystems directly and duplicate orchestration.

Source anchor: Design Patterns Catalog - Refactoring.Guru.

Module concepts:

  • Facade
  • subsystem coordination
  • API simplification
  • orchestration boundary

Wrong Approach

Create a helper function in every controller that calls the same five services.

Better Approach

Introduce a FinanceReportFacade that exposes business-level operations such as generateMonthlyReport(period). Keep subsystem-specific detail inside the facade and return a stable result object with report URL, warnings, and audit ID.

Tradeoff Table

ChoiceGainCost
Direct subsystem callsFlexible per callerDuplication and coupling
FacadeSimple caller APICan become a god object
Workflow engineStrong orchestrationHeavy for simple reports

Failure Mode

One controller skips the currency conversion step and publishes inconsistent totals.

Required Artifact

Create a facade API sketch with operations, dependencies hidden, return types, errors, and ownership boundaries.

Project / Capstone Connection

Use this when a feature coordinates several modules and callers do not need the details.


Case Study 4: Factory Method for Environment-Specific Clients

Scenario: Local development uses an in-memory email sender, staging uses a sandbox provider, and production uses a real provider with signing keys. Construction logic is scattered across app startup and tests.

Source anchor: Design Patterns Catalog - Refactoring.Guru.

Module concepts:

  • Factory Method
  • dependency creation
  • environment configuration
  • test seams

Wrong Approach

Add if env == "production" checks wherever an email sender is needed.

Better Approach

Centralize construction in an EmailSenderFactory or composition root. Return the same interface for every environment, validate configuration at startup, and keep tests from depending on production credentials.

Tradeoff Table

ChoiceGainCost
Inline constructionEasy local codeScattered environment logic
Factory methodCentral constructionAnother abstraction to maintain
DI containerConsistent compositionCan hide simple wiring

Failure Mode

A test accidentally sends real emails because one call site bypassed the environment check.

Required Artifact

Write an environment construction matrix covering local, test, staging, production, required configuration, and failure behavior.

Project / Capstone Connection

Use this matrix for constructing repositories, API clients, and fake implementations.


Case Study 5: Builder for Complex Export Configuration

Scenario: A data export job has optional compression, encryption, partitioning, retention policy, destination, schema version, and notification settings. Constructors are overloaded and call sites are unreadable.

Source anchor: Design Patterns Catalog - Refactoring.Guru.

Module concepts:

  • Builder
  • valid construction
  • named options
  • configuration readability

Wrong Approach

Create more constructors and rely on argument order.

Better Approach

Use a builder or typed configuration object that names every option, validates incompatible choices, and produces an immutable ExportJobConfig. Keep defaults explicit and test invalid combinations.

Tradeoff Table

ChoiceGainCost
Overloaded constructorsFamiliar syntaxAmbiguous call sites
BuilderReadable constructionMore code
Plain config objectSimple data shapeNeeds validation discipline

Failure Mode

A caller enables encryption without a key because no construction step validates the combination.

Required Artifact

Design a builder flow with required steps, optional defaults, invalid combinations, and example call sites.

Project / Capstone Connection

Use this for any project object that needs multiple optional but constrained settings.


Source Map

SourceUse it for
Design Patterns Catalog - Refactoring.GuruPattern intent and vocabulary across creational and structural choices.