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
- Name the forces before naming the pattern.
- Draw the runtime and code boundary separately.
- Identify the tax: network, data, deployment, observability, or team coordination.
- Produce the required artifact.
- 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
| Choice | Gain | Cost |
|---|---|---|
| microservices early | independent deployment in theory | network, data, testing, and ops complexity |
| unstructured monolith | fastest first week | boundary erosion |
| modular monolith | simple runtime with explicit boundaries | discipline/tooling needed |
| extract later | better boundaries from evidence | migration 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
| Choice | Gain | Cost |
|---|---|---|
| folders only | low ceremony | no real protection |
| package checks | visible boundary drift | migration and false-positive handling |
| separate services | hard runtime boundary | distributed data and operations tax |
| architecture review only | nuanced | inconsistent 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
| Choice | Gain | Cost |
|---|---|---|
| big-bang rewrite | clean target architecture | high cutover risk |
| strangler migration | lower risk and continuous learning | temporary duplication |
| facade/router | controlled migration path | new operational component |
| shadow comparison | evidence before switch | extra 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
| Choice | Gain | Cost |
|---|---|---|
| synchronous orchestration | clear outcome | tighter availability coupling |
| broker choreography | service autonomy | harder global reasoning |
| mediator workflow | central visibility | central coordinator |
| event sourcing | complete history | model 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
| Choice | Gain | Cost |
|---|---|---|
| shared database | easy joins and transactions | service coupling |
| database per service | autonomy and ownership | distributed consistency |
| read replica/reporting DB | query convenience | data freshness and pipelines |
| service API ownership | clear boundary | more 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
| Source | Use it for |
|---|---|
| Martin Fowler: Monolith First | when to start with a monolith and extract later |
| Shopify: A Packwerk Retrospective | modular monolith boundary enforcement and package-privacy rules |
| Martin Fowler: Strangler Fig Application | incremental legacy replacement |
| AWS event-driven architecture guidance | event-driven tradeoffs and failure handling |
| Microservices.io: Database per service | service 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.