Skip to content

04 · Isolate the domain

When you finish this lesson you should be able to:

  • Place a behavior in UI, application, domain, or infrastructure and defend the placement
  • Explain Evans’s shipping-destination example: five technical jobs, one business job
  • Treat Smart UI as a legitimate pattern for simple places and a trap for complex domains
  • Keep the domain from importing your ORM, HTTP types, or vendor SDKs

Only a small fraction of the code is domain, but that fraction is where you need to apply the best thinking. You must see the model as a system, not pick constellations out of a night sky of widgets, queries, and network calls.

This ground is well-trodden (layers, hexagonal, ports and adapters). Evans reviews it because without isolation, chapters 5–17 have nowhere to live.

To let a user pick a cargo destination, software must: draw a widget, query cities, interpret input, associate city with cargo, commit. All of that is one program. Only the association is shipping. If that rule is written inside the widget or the SQL, you will never get a model of Cargo.

Your analog: picking a plan during onboarding is drawing UI, loading plans, validating the POST, asserting the session may select a plan, committing. Only the assertion is domain.

Layered architecture (from a domain point of view)

Section titled “Layered architecture (from a domain point of view)”
Layer Responsibility Allowed to know
UI Render, collect intent Application API, view models
Application Use case: load, call domain, save, enqueue Domain, infrastructure ports
Domain Model, invariants, language Domain only (plus simple types, clocks as ports)
Infrastructure SQL, queues, mail, files, vendor SDKs Everything messy, hidden behind interfaces the app uses

Dependencies point inward. Application coordinates; it should not contain if (!verified && plan). That if is the domain.

There are variants (hexagon: domain in the center, adapters outside). The DDD test is the same: can you find the model without grepping controllers?

Smart UI (anti-pattern for complex domains)

Section titled “Smart UI (anti-pattern for complex domains)”

Evans is more honest than internet summaries. Smart UI puts logic in the interface. For a simple CRUD admin, it is fast, testable-enough, and honest. Teams that pretend they will “extract a model later” almost never do: every screen copies rules, tests hit the UI, there is no place to hang a ubiquitous language.

Use Smart UI on purpose for generic tools. Do not use it for the core domain (lesson 13) and then wonder why DDD “didn’t work.”

  • Separate processes / services: isolation by network, costs a context map (lesson 12).
  • Separate databases: good if they match aggregates; bad if you still join in your head.
  • Published language at the edge: JSON that is the model of a context, not the dumping of tables.

If two teams share a users table, you have not isolated anything. You have a secret unbounded context.

Rule in the wrong home:

if (req.body.planId && !user.emailVerified) return res.status(400).end();

Isolated:

POST /sessions/:id/plan
→ Application StartPlanSelection
→ session.assertCanSelectPlan() // domain
→ session.select(plan) // domain
→ repository.add(session)
→ 200

HTTP does not know what “can” means. When “verified” gains “or trusted provider,” one method changes.

flowchart TB
  ui[User interface]
  app[Application]
  domain[Domain]
  infra[Infrastructure]
  ui --> app
  app --> domain
  app --> infra

Domain defines ports (Clock, IdentityLookup) if it must. Infrastructure implements. If session.ts imports prisma or express, isolation already leaked.

  • God application service that is the old Smart UI in a hoodie.
  • Domain that logs, sends email, and commits. That is infrastructure envy.
  • “Clean architecture” folder ritual with the same if in three layers.
  1. List eight behaviors (notify, assign experiment, charge, snooze, verify, …).
  2. Mark where each rule lives today.
  3. Move two rules on paper into domain methods. Write application as load / call / save only.
  4. Find one import in a domain-ish file that is HTTP, SQL, or a vendor. Replace with a port.
  5. Pick a truly simple admin screen. Write “Smart UI: yes” and one sentence why you will not grow a model there.
  • Domain-Driven Design — Chapter 4, Isolating the Domain (layered architecture; domain layer; Smart UI; other isolation)