MODULE 1/LESSON 3
🔀 Monolith to Microservices

DDD & Bounded Contexts

Using Domain-Driven Design to find natural service boundaries before you draw them

15 min
The most common microservices failure is drawing boundaries around technical layers (Controllers, Services, Repositories) instead of business domains. DDD gives you the language and tools to find where the real boundaries live in the business.

Key Concepts

Bounded Context

A Bounded Context is the boundary within which a specific domain model is consistent and valid. 'Customer' means different things in Sales (lead) vs Shipping (address) — they belong in different contexts.

Ubiquitous Language

Every Bounded Context has its own vocabulary used by both engineers and domain experts. Terms crossing context boundaries need explicit translation via Anti-Corruption Layers.

Context Mapping

Draw explicit relationships between contexts: Shared Kernel (shared code), Customer-Supplier (upstream/downstream), Conformist (accept upstream model), Anti-Corruption Layer (translate).

Aggregate Roots

An Aggregate is a cluster of domain objects treated as a single unit. Only the Aggregate Root can be referenced from outside. Order (root) + OrderItems (entities) + Address (value object).

Bounded Context Example: E-Commerce

Bounded Context Boundaries: "The Product" Catalog Context Product (Display) name, description, image_url category, SEO slug Inventory Context Product (Stock) sku, warehouse_location stock_count, reorder_level Pricing Context Product (Price) base_price, discount_rules tax_class, currency Order Context Product (Line Item) product_id (soft ref) price_at_purchase, qty ID

Why Context Boundaries Matter

The word Product appears in multiple contexts with completely different meanings. This is the tell-tale sign of a Bounded Context boundary.
ContextWhat 'Product' MeansKey Fields
Catalog ContextSomething shown to shoppersname, description, images, category, SEO slug
Inventory ContextSomething we have in stocksku, warehouseLocation, stockCount, reorderLevel
Pricing ContextSomething with a price rulebasePrice, discounts, taxClass, currency
Order ContextSomething a customer boughtproductId (reference), priceAtPurchase, quantity

Prisma Schema — Database per Bounded Context

order-service/schema.prismaprisma
1// ORDER SERVICE — its own isolated Prisma schema
2// Does NOT import Product or User models from other services.
3// Uses soft references (foreign keys are just plain IDs).
4
5model Order {
6  id          String      @id @default(uuid())
7  customerId  String      // Soft reference — NOT a FK to User service DB
8  status      OrderStatus @default(PENDING)
9  totalAmount Decimal     @db.Decimal(12, 2)
10  currency    String      @default("USD")
11  createdAt   DateTime    @default(now())
12  updatedAt   DateTime    @updatedAt
13
14  items       OrderItem[]
15  @@index([customerId])
16  @@index([status, createdAt])
17}
18
19model OrderItem {
20  id            String  @id @default(uuid())
21  orderId       String
22  productId     String  // Soft reference — price is copied at purchase time
23  productName   String  // Denormalized — independent from Catalog service
24  priceSnapshot Decimal @db.Decimal(10, 2)
25  quantity      Int
26
27  order         Order   @relation(fields: [orderId], references: [id])
28}
💡
Senior Architect Insight: If two services need to share a database table, they're not separate services — they're modules of the same service. Share data through well-defined APIs and events, never through a shared database.