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.