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.