Skip to main content

Module 2: Architecture Patterns & Modular Monoliths: Case Studies

These case studies compare architecture styles as cost structures. A style is not a badge. It is a set of bets about deployment, data ownership, failure, team boundaries, and change.


How To Use These Case Studies

  1. Name the forces before naming the pattern.
  2. Draw the runtime and code boundary separately.
  3. Identify the tax: network, data, deployment, observability, or team coordination.
  4. Produce the required artifact.
  5. State what evidence would trigger an evolution to another style.

Case Study 1: Monolith First For An Unproven Product

Scenario: A startup wants to launch a marketplace. The team proposes user, catalog, checkout, payment, search, and notification microservices before the domain model is stable.

Source anchor: Martin Fowler's Monolith First article argues that many successful microservice systems start as monoliths because boundaries are easier to find after the domain is understood.

Module concepts:

  • monolith first
  • modular monolith
  • microservices tax
  • domain uncertainty
  • evolutionary architecture

Wrong Approach

"We will start with microservices so we can scale later."

That optimizes for an imagined future while paying distributed-systems costs immediately.

Better Approach

Start with a modular monolith:

Single deployment:
one release path
one database transaction boundary where needed

Internal modules:
catalog
ordering
payment
fulfillment

Evolution trigger:
module has clear ownership, independent scaling pressure, and stable contract

Tradeoff Table

ChoiceGainCost
microservices earlyindependent deployment in theorynetwork, data, testing, and ops complexity
unstructured monolithfastest first weekboundary erosion
modular monolithsimple runtime with explicit boundariesdiscipline/tooling needed
extract laterbetter boundaries from evidencemigration work

Failure Mode

The team spends more time on service plumbing than product learning, then discovers the service boundaries are wrong.

Required Artifact

Write a style decision memo with three extraction triggers and three reasons to stay monolithic for now.

Project / Capstone Connection

Default capstone architecture should usually be modular monolith unless a module can justify runtime distribution.


Case Study 2: Shopify-Style Boundary Enforcement Inside A Rails Monolith

Scenario: A large Rails codebase has packages for billing, shops, checkout, and fulfillment, but any package can call any constant. Teams call it modular, yet changes leak everywhere.

Source anchor: Shopify's A Packwerk Retrospective explains how Packwerk detects dependency and privacy violations to enforce package boundaries in a large Rails monolith.

Module concepts:

  • modular monolith
  • package boundary
  • dependency rule
  • visibility/privacy
  • architecture fitness function

Wrong Approach

"We have folders, so we have modules."

Folders describe intent. Enforcement makes the intent real.

Better Approach

Define boundary rules:

Package:
Billing

Public API:
Billing::InvoiceService
Billing::Events::InvoicePaid

Forbidden:
Checkout reading Billing::Internal::TaxCalculator
Fulfillment writing billing tables directly

Then run boundary checks in CI and track violations down.

Tradeoff Table

ChoiceGainCost
folders onlylow ceremonyno real protection
package checksvisible boundary driftmigration and false-positive handling
separate serviceshard runtime boundarydistributed data and operations tax
architecture review onlynuancedinconsistent enforcement

Failure Mode

The monolith becomes a distributed monolith in waiting: tightly coupled code that will be painful to extract.

Required Artifact

Create a module-boundary map with public APIs, forbidden dependencies, and one CI rule.

Project / Capstone Connection

Capstone monoliths should still have module boundaries and at least one enforceable dependency rule.


Case Study 3: Strangler Fig Migration Instead Of Rewrite V2

Scenario: A company wants to replace a legacy order-management system. The first plan is a full rewrite and big-bang switch. Nobody can explain how old and new systems will coexist for a year.

Source anchor: Martin Fowler's Strangler Fig Application pattern describes gradually replacing an old system by building new functionality around it and routing more behavior to the new system over time.

Module concepts:

  • strangler fig
  • incremental migration
  • routing seam
  • coexistence
  • rollback

Wrong Approach

"Build the new system, then switch everything on launch day."

That creates a huge unknown integration event at the end.

Better Approach

Create an incremental path:

1. Put a routing facade in front of legacy behavior.
2. Move one low-risk capability to the new system.
3. Compare outputs with shadow traffic where possible.
4. Move higher-risk flows after evidence.
5. Retire legacy paths deliberately.

Tradeoff Table

ChoiceGainCost
big-bang rewriteclean target architecturehigh cutover risk
strangler migrationlower risk and continuous learningtemporary duplication
facade/routercontrolled migration pathnew operational component
shadow comparisonevidence before switchextra instrumentation

Failure Mode

The rewrite reaches "almost done" and then stalls because migration, parity, and data ownership were not designed.

Required Artifact

Write a strangler migration map with first capability, routing rule, parity check, rollback, and retirement plan.

Project / Capstone Connection

Any capstone modernization story should show migration mechanics, not only target architecture.


Case Study 4: Event-Driven Architecture Chosen To Avoid Coupling

Scenario: An order service publishes OrderCreated. Inventory, email, analytics, fraud, and fulfillment subscribe. The team says this is decoupled, but now nobody knows what a successful order means or how failures are retried.

Source anchor: AWS Prescriptive Guidance describes event-driven architectures as asynchronous communication through events, with tradeoffs around loose coupling, observability, ordering, idempotency, and failure handling. See AWS event-driven architecture guidance.

Module concepts:

  • event-driven architecture
  • broker topology
  • eventual consistency
  • choreography
  • idempotent consumers
  • observability

Wrong Approach

"Events decouple everything."

Events can reduce direct call coupling while increasing temporal, semantic, and operational coupling.

Better Approach

Define the event contract and failure model:

Event:
OrderCreated

Producer guarantee:
published after order transaction commits

Consumer rule:
idempotent by event_id

Observability:
trace order_id across consumers

Failure:
retry, dead-letter, replay, manual repair

Tradeoff Table

ChoiceGainCost
synchronous orchestrationclear outcometighter availability coupling
broker choreographyservice autonomyharder global reasoning
mediator workflowcentral visibilitycentral coordinator
event sourcingcomplete historymodel and operational complexity

Failure Mode

The order is "created" but downstream effects are missing, duplicated, or unobservable.

Required Artifact

Write an event contract with producer transaction rule, schema, idempotency rule, retry policy, and dead-letter handling.

Project / Capstone Connection

If a capstone uses events, include a replay/idempotency story.


Case Study 5: Shared Database Service-Based Architecture

Scenario: A company splits a monolith into coarse services but keeps one shared database. It gets some team autonomy and simpler reporting, but every schema change still coordinates across services.

Source anchor: Chris Richardson's microservices pattern material frames database-per-service as a core microservices data ownership rule and explains why shared databases couple services. See Database per service pattern.

Module concepts:

  • service-based architecture
  • shared database
  • data ownership
  • microservices boundary
  • transactional convenience

Wrong Approach

"If code is in different deployables, we have microservices."

Runtime separation without data ownership still couples change and failure.

Better Approach

Name the style honestly:

Current style:
service-based architecture with shared database

Accept because:
fewer distributed transactions
simpler reporting
smaller ops burden

Risk:
schema coupling
unclear ownership
accidental cross-service writes

Tradeoff Table

ChoiceGainCost
shared databaseeasy joins and transactionsservice coupling
database per serviceautonomy and ownershipdistributed consistency
read replica/reporting DBquery conveniencedata freshness and pipelines
service API ownershipclear boundarymore integration work

Failure Mode

The organization pays service deployment complexity but keeps monolith-level data coupling.

Required Artifact

Create a data-ownership table: table, owning service, allowed writers, allowed readers, migration owner.

Project / Capstone Connection

When capstones split services, they must explain data ownership and cross-service consistency.


Source Map

SourceUse it for
Martin Fowler: Monolith Firstwhen to start with a monolith and extract later
Shopify: A Packwerk Retrospectivemodular monolith boundary enforcement and package-privacy rules
Martin Fowler: Strangler Fig Applicationincremental legacy replacement
AWS event-driven architecture guidanceevent-driven tradeoffs and failure handling
Microservices.io: Database per serviceservice data ownership and shared-database tradeoffs

Completion Standard

  • At least three artifacts are completed.
  • At least one artifact compares modular monolith and microservices.
  • At least one artifact includes an enforceable module boundary.
  • At least one artifact includes a migration path between styles.
  • At least one artifact names the tax of the chosen style.