01 / 12 Title
โ†โ†’ navigate ยท O overview ยท F fullscreen ยท G go to
DDD for Beginners

Domain-Driven Design

Why your code should speak the language of the business.

Mir Mursalin Ankur
Lead Software Engineer @ Nerddevs Ltd
"Most software doesn't break because of syntax errors โ€” it breaks because teams lose alignment with the business problem they're supposed to solve."
01 ยท From Scratch

Meet Northwind โ€” a Small Online Retail Business

In plain English, here is everything Northwind does. No code, no database, no framework โ€” just the business, as its three founders would describe it:

The core insight
That list โ€” nothing more than what the founders would actually say โ€” is the business domain. Everything in this deck is this same list, made gradually more precise.
02 ยท Vocabulary

Five Words That Get Conflated

TermWhat it meansNorthwind example
DomainReal-world subject matter the software serves"Online retail"
Business domainThe specific domain your company operates inNorthwind's business domain is online retail
SubdomainA distinct area of responsibility inside itOrder Management, Inventory, Payments, Auth
Bounded ContextThe boundary in code where one model appliesThe order-management module
Ubiquitous LanguageShared vocabulary inside one context"Product" always means a line item, in Order Mgmt

Domain and subdomain describe the business. Bounded context and ubiquitous language describe how you organize the code and words around it. Keep those two groups separate and most DDD confusion disappears.

03 ยท The Example

The Bug: Two Teams, One Word, Two Meanings

Northwind grew. Now there's an Ordering team and a separate Inventory team, each shipping its own service. Both use a type called Product.

// InventoryService โ€” "price" is the wholesale cost Northwind pays the supplier
class Product { sku: string; price: number; stockCount: number; }

// OrderService โ€” imports the SAME Product type from a shared "models" folder
function calculateTotal(product: Product, qty: number) {
  return product.price * qty;   // customer is charged supplier COST, not retail price
}
The real-world outcome
Nobody wrote a bug. Both teams used a reasonable field named price. The system still shipped a real one: customers were charged wholesale cost for weeks before anyone noticed margins had collapsed.
04 ยท The Problem

Systems Don't Fail on Day One โ€” They Fail as They Grow

The core insight
The hardest part of a complex product was never the codebase. It's agreeing on what the business is actually doing.
05 ยท The Idea

Model the Business, Not the Database

The term traces back to Eric Evans' 2003 book Domain-Driven Design: Tackling Complexity in the Heart of Software โ€” Entity, Value Object, Aggregate, Domain Event, Repository all come from that book by name.

DDD is an approach to software design that puts the business domain โ€” not the schema, not the framework โ€” at the center of every decision. It requires engineers to collaborate deeply and continuously with domain experts, not just gather requirements once and disappear into tickets.

ByteByteGo cheatsheet on Domain-Driven Design
A cheatsheet on Domain-Driven Design โ€” ByteByteGo
06 ยท Method

A Repeatable Method โ€” Not Guesswork

  1. List what the business does, in the founders' own words.
  2. Does a shared word change meaning here? "Product" differs between stock-counting and cart-building โ€” that shift is a signal.
  3. Does a different group decide here, on a different rhythm? Pricing changes daily; login barely changes at all.
  4. Group by how much each matters:
    • Core โ€” Order Management, Pricing
    • Supporting โ€” Inventory, Fulfillment
    • Generic โ€” Payments Gateway, Auth

Legacy codebase? Same method โ€” look for a class name used two different ways, or a "simple" change needing another team's sign-off.

Online Retail business domain split into Core, Supporting, and Generic subdomains
ContextOwnsCore model
Order MgmtOrders, checkoutOrder, OrderLineItem
InventoryStock, SKUsProduct, StockCount
PaymentsCharges, refundsPaymentAttempt (via ACL)

This is a candidate, not a final answer. Northwind proposed splitting "Pricing" out of Order Management โ€” discussion revealed no real seam, and it merged back in. Propose โ†’ discuss โ†’ reject or merge โ†’ standardize.

07 ยท Fix #1

One Word, One Meaning โ€” Inside a Context

Ubiquitous language is a shared vocabulary developers and domain experts use everywhere, for one bounded context. Applied to the bug from Slide 4:

// After a 15-minute glossary conversation with the business:
// "price" the business pays a supplier is a WholesaleCost.
// "price" the customer pays is a RetailPrice. Never the same field.
class Product { sku: string; wholesaleCost: Money; retailPrice: Money; stockCount: number; }

function calculateTotal(product: Product, qty: number) {
  return product.retailPrice.multiply(qty);   // unambiguous
}

Pros: shared meaning, readable code, better decisions. Cons: upfront workshop effort; sloppy naming breaks the whole benefit.

08 ยท Common Confusion

There Is No Company-Wide Glossary

Ubiquitous language is scoped per bounded context โ€” not company-wide. Each context gets its own internally-consistent vocabulary:

The rule
Where two contexts genuinely need to talk, don't force one team's words onto the other โ€” translate explicitly at the boundary (context mapping, in the Deep Dive deck). One language per context, translated at the seams โ€” never one language forced onto everyone.
09 ยท Fix #2

Make the Confusion Structurally Impossible

Renaming one field fixes one bug. It doesn't stop the next team from importing the wrong Product again. A bounded context gives each meaning its own model.

// inventory-context/Product.ts โ€” not shared
class Product { sku: string; wholesaleCost: Money; stockCount: number; }

// order-context/Product.ts โ€” not shared
class Product { sku: string; retailPrice: Money; }

A bounded context isn't automatically a microservice โ€” it can be a module inside one monolith. It's the model-and-language boundary that counts, not the deployment.

Bounded contexts diagram โ€” Order Management and Inventory each define Product independently
Level Up Coding โ€” same word, different meanings across bounded contexts
10 ยท Building Blocks

Inside a Context: Identity vs. Attributes

// Entity โ€” has an ID that outlives any single attribute
class Customer {
  constructor(public readonly id: string, private address: Address) {}
  relocate(newAddress: Address) { this.address = newAddress; } // same customer, new address
}

// Value Object โ€” immutable, compared by value, no ID
class Money {
  constructor(public readonly amount: number, public readonly currency: string) {}
  multiply(qty: number) { return new Money(this.amount * qty, this.currency); }
}
11 ยท Wrap-Up

DDD Is a Tool, Not a Default

Reach for DDD when
The domain is complex and keeps evolving (finance, healthcare, logistics, marketplaces); multiple teams share the system; it needs to live and change safely for years.
Skip DDD when
The app is mostly CRUD; you can't get sustained access to domain experts; the project is short-lived or low-impact.

"You don't adopt DDD by adding patterns โ€” you adopt it by removing ambiguity."

Next up โ€” Deep Dive
Northwind keeps growing: Aggregates, Domain Events, Repositories (+ "one database or many?"), and the Anti-Corruption Layer.

โ†’ deep-dive.html
Sources: ByteByteGo โ€” "Domain-Driven Design (DDD) Demystified" ยท Nikki Siapno, Level Up Coding โ€” "Domain-Driven Design, Broken Down" ยท โ†— Read the blog

All slides โ€” click to jump