DDD on the systems analyst interview

Train for your next tech interview
1,500+ real interview questions across engineering, product, design, and data — with worked solutions.
Join the waitlist

Why interviewers ask about DDD

When a systems analyst walks into a loop at Stripe, Notion, or DoorDash, the architecture round is rarely about CAP theorem trivia. It's about whether you can split a tangled business problem into pieces ten engineers can build in parallel without stepping on each other. That is what Domain-Driven Design (DDD) was invented to do, and it is the vocabulary your interviewer uses to test how you think.

The most load-bearing concepts here are bounded contexts, aggregates, and ubiquitous language. You will hear them in scenario questions like "design a checkout for a marketplace" or "split this monolith into services." If you reach for ER diagrams immediately, you've already lost the room. If you start with "what subdomains do we have, and which one is the core?", you sound like someone who has shipped a real platform.

DDD is not a framework you install — it's a way to organize thinking, drawings, and team boundaries.

The core idea of DDD

Eric Evans introduced DDD in his 2003 book to attack one specific failure mode: software systems where the code says one thing, the business documents say another, and nobody can change anything without breaking something. His thesis was simple — when the business domain is complex, the domain itself should drive the software design, not the database schema or the latest framework.

Four principles run through everything:

  • Software must reflect the business domain, not a generic CRUD template.
  • Engineers and business stakeholders speak one shared language.
  • Code structure follows business structure, not technology layers.
  • Large domains are decomposed into independent bounded contexts with their own models.

Load-bearing trick: if you can describe the system to a business stakeholder using the same nouns and verbs that appear in the code, you are doing DDD. If you can't, you aren't — no matter how many "domain" folders you have.

Strategic design

Strategic design is the high-altitude view: how do we slice the whole business into manageable chunks? In DDD vocabulary, those chunks are subdomains, and they come in three flavors. The first task in any interview scenario is to name them.

Subdomain type What it is How to build it
Core domain The unique business value, your competitive moat Custom-built, best engineers assigned
Supporting subdomain Helps the core but is not unique Custom or off-the-shelf with light customization
Generic subdomain Commodity (auth, billing rails, notifications) Buy a SaaS, integrate, move on

For a ride-hailing company, matching riders to drivers is core domain. Driver onboarding workflows are supporting. SMS delivery is generic — use a vendor. A strong candidate names which subdomain is core in the first two minutes, because that decision drives everything else: where to invest engineers, what to outsource, what to keep in-house.

The deliverable from strategic design is a context map — a diagram showing each subdomain, its bounded context, and the relationships between them. Interviewers love when you reach for this without being prompted.

Bounded contexts

A bounded context is the boundary inside which a model and its terminology have one consistent meaning. Outside that boundary, the same word can mean something different, and that's fine — it's expected. This is the single concept that separates DDD-fluent candidates from people who memorized the buzzword.

Consider how the word Customer changes across an e-commerce platform:

Sales context:    Customer = name, purchase history, loyalty tier, discounts
Billing context:  Customer = name, payment method, credit limit, tax ID
Shipping context: Customer = name, delivery address, signature requirements
Support context:  Customer = name, ticket history, language preference

One real person, four different models, four different teams, four different services. Trying to force a single unified Customer table across all of them is the most common architectural mistake in mid-size companies — it creates a god-object that no team can change without coordinating with every other team.

How contexts relate matters as much as how they're split. The standard patterns are:

  • Shared kernel. Two contexts share a small piece of model, change it only by joint agreement.
  • Customer/Supplier. The upstream context cares about the downstream's needs.
  • Anti-corruption layer (ACL). You translate between your clean model and a partner's messy one.
  • Conformist. You accept the partner's model as-is because you have no leverage.

Sanity check: if changing one field in service A forces a coordinated deploy with services B, C, and D, you don't have bounded contexts — you have a distributed monolith.

Tactical patterns

Tactical patterns operate inside a single bounded context — the code-level building blocks. Senior interviewers will dig here to test whether you've actually used DDD or just read the Wikipedia page.

Entity. An object defined by its identity. A Customer with the same name and email is the same customer if and only if the ID matches. Two Order objects with identical line items are still different orders because they have different IDs.

Value object. No identity, immutable, defined entirely by its attributes. Money(amount: 100, currency: USD) is a value object — two instances with the same amount and currency are interchangeable. Address, DateRange, and Coordinates are typical examples. Use value objects aggressively; they kill an entire class of bugs around accidental mutation.

Aggregate. A cluster of entities and value objects treated as one transactional unit. Every aggregate has exactly one aggregate root — the only object outside code is allowed to reference. All changes go through the root.

Order aggregate:
  Order (root)
    OrderItem (entity, internal)
    DeliveryDetails (value object)
    PaymentReference (value object)

External code touches Order, never OrderItem directly. This is what enforces invariants like "the order total must equal the sum of line items" — the root validates on every change. In practice, aggregate boundaries are also your transaction boundaries — one aggregate per database transaction is the default rule.

Repository. Abstracts persistence of an aggregate. OrderRepository.findById(id) returns a fully loaded Order aggregate; you don't write SQL in the domain layer.

Domain service. Logic that doesn't belong to any single entity. Transferring money between two accounts touches two Account aggregates, so it lives in a TransferService rather than on either account.

Domain event. A statement that something important happened — OrderShipped, PaymentFailed, CustomerSignedUp. Events are the integration mechanism between bounded contexts and the input to event-driven architectures.

Train for your next tech interview
1,500+ real interview questions across engineering, product, design, and data — with worked solutions.
Join the waitlist

Ubiquitous language

The single deliverable that distinguishes DDD teams from everyone else is the ubiquitous language — one vocabulary shared by developers, analysts, product managers, and business stakeholders. The class names in code match the words in PRDs match the labels on whiteboard diagrams.

When the sales team says "a customer registers an account," the code reads Customer.register(), not User.signup(). When billing says "we issue an invoice," the code is Invoice.issue(), not BillingRecord.create(). This sounds trivial until you realize most production codebases have at least three names for the same business concept — user, account, and member all referring to the same thing, but with subtly different fields in each table.

The ubiquitous language must live somewhere visible. A team glossary in the repo, a Notion page, a wiki — pick one, but everyone references the same source. The fastest red flag in a codebase review is when the engineers and the PM use different words for the same thing in the same Slack thread.

When to apply DDD

DDD is heavy. It pays off when the domain complexity is high enough to justify the upfront investment in modeling, glossaries, and team coordination. It is a tax on small teams shipping simple products.

Situation DDD fit
Complex domain (insurance, banking, supply chain, healthcare) Strong fit
Multi-team product, 20+ engineers Strong fit
Multi-year roadmap, expected to evolve Strong fit
Simple CRUD app, single team of 3 Overkill
Proof-of-concept, weeks-to-throwaway Overkill
Team has zero DDD experience and no senior architect Risky — will collapse into folder-name theater

A common failure mode is partial DDD — teams adopt the vocabulary but skip the discipline. You end up with folders called domain that contain anemic data classes, no aggregates, no ubiquitous language, no context boundaries. That is worse than not doing DDD at all. Minimum viable adoption: bounded contexts + ubiquitous language + aggregates. If you can't commit to those three, skip it.

Common pitfalls

The most damaging mistake is treating every entity as an aggregate root. Junior teams expose every table as its own service and end up with hundreds of services that all have to talk to each other to do anything useful. Start coarse — model the few aggregates that enforce real business invariants, let everything else be a value object or internal entity. You can always split later; merging back is much harder.

Another common trap is shared databases across bounded contexts. Two teams agree to "just use the same Customer table for now," and within a year every schema change requires four meetings and a steering committee. The fix is to commit to physical separation early — each context owns its data, and integration happens through events or APIs, never through SQL joins across context boundaries.

Teams also frequently confuse bounded context with microservice. A bounded context is a modeling boundary; a microservice is a deployment artifact. You can run a DDD-correct system as a modular monolith with clear context boundaries inside one process. Going microservices-first without bounded contexts gives you a distributed monolith — all the operational pain and none of the team-autonomy benefit.

A subtler pitfall is ubiquitous language drift. The glossary is written on day one, then never updated. Six months later the code uses one word, PRDs use another, nobody owns reconciling them. Make glossary maintenance someone's responsibility and review it quarterly. To drill scenarios like this daily, NAILDD ships a question bank built around these patterns.

Finally, watch out for anemic domain models — classes with no behavior, just bags of fields with getters and setters, all the logic sitting in a service layer outside. This is DDD in name only. Push behavior back into the domain objects so that an Order knows how to add a line item, validate itself, and emit a LineItemAdded event.

FAQ

Is DDD the same as microservices?

No, although they are often discussed together. DDD is a modeling philosophy — how you carve the problem into bounded contexts with consistent language and aggregates. Microservices are a deployment strategy — how you package and ship the resulting code. You can do DDD inside a single modular monolith, and many teams should, especially before they have the operational maturity to run dozens of services. The link between them is that a well-designed bounded context often maps cleanly to a microservice when you do decide to split, but the modeling work comes first.

What's the difference between a domain event and a regular event?

A domain event is meaningful in the business — OrderShipped, SubscriptionRenewed, RefundIssued. It uses the ubiquitous language and is something a business stakeholder would recognize. A technical event like RowInserted is useful for plumbing, but not part of the domain. The distinction matters because domain events are what other bounded contexts subscribe to, and they need to be stable, named in business terms, and versioned carefully.

How small should an aggregate be?

As small as possible while still enforcing the invariants that matter. The default rule is one aggregate per transaction. If you find yourself wanting to update three aggregates in one database transaction, that's a signal you've drawn the boundary wrong — either merge them into one aggregate, or accept eventual consistency between them and use domain events. A common heuristic is that aggregates with more than five or six internal entities are usually too big and should be re-examined.

When does DDD actually fail in practice?

It fails when the domain isn't actually complex. If you're building a CRUD app with eight tables and no interesting business rules, DDD overhead — glossaries, aggregates, context maps — buys you nothing. It also fails when adoption is partial: the team uses the vocabulary but skips the discipline. The third failure mode is a team without senior architects to maintain the model, where context boundaries erode within a year as deadline pressure pushes shortcuts.

How do I bring up DDD in an interview without sounding rehearsed?

Don't lead with the term. Lead with the behavior — start by asking about the business domain, identify what's core versus generic, and propose boundaries before you talk patterns. The interviewer will recognize you're doing DDD without you having to say the words. Reciting the patterns list as your opening move signals memorization rather than judgment.

Which book should I read first?

Start with Vaughn Vernon's "Implementing Domain-Driven Design" — more practical and code-oriented than Evans's original. Evans is the seminal text and worth reading second, but denser. For interview prep, Vernon gives you concrete patterns you can describe in a whiteboard session within a couple of weeks.