TL;DR: Domain-driven design interviews test whether a candidate can draw bounded contexts, size aggregates around real invariants, and keep business logic out of services and controllers. Senior roles also probe how DDD maps to microservices, modular monoliths and event sourcing, and whether the candidate knows when DDD is not worth it.
Axon Framework 5.0, released on November 18, 2025, added event tags and append conditions for Dynamic Consistency Boundaries. That idea questions whether the aggregate must be the only consistency boundary.
Most interviews still start with bounded contexts, aggregates and value objects. The strong candidates also know where these rules bend.
- 1Vernon's aggregate rules still anchor most answers: small aggregates, reference by ID, eventual consistency between them.
- 2A bounded context is a boundary of one model and one language. It does not have to be one microservice.
- 3Spring Modulith 2.0 (November 21, 2025) and jMolecules 2.0 (November 13, 2025) bring DDD structure checks into Java builds.
- 4EventStoreDB, a common store for event-sourced aggregates, is now KurrentDB.
Strategic Design
1. What is a bounded context, and why does it matter so much?
A bounded context is the boundary inside which one domain model and its language apply consistently. Outside it, the same word can mean something else.
"Product" in a Catalog context has descriptions and images. In Inventory it has stock levels and bin locations. In Billing it is a price line. Forcing one shared Product class on all three creates a model nobody can change safely.
Bounded contexts let each team keep a model that fits its job, and make the translation between them explicit. Martin Fowler's Bounded Context note is a short summary.
2. What is the ubiquitous language, and how do you see it in code?
The ubiquitous language is the shared vocabulary that domain experts and developers use for one bounded context, in speech, docs and code alike.
You see it when class and method names match what the business says. For example, policy.renew() and ClaimRejected, rather than updateStatus(3) and RecordModified.
When a developer has to translate between the code and a meeting, the language has drifted. The language belongs to one context. Trying to make one language fit the whole company brings back the ambiguity DDD tries to remove.
3. What are core, supporting and generic subdomains?
They rank parts of the business by how much they set the company apart. The ranking decides where to spend design effort.

- Core: the reason customers choose you, such as pricing for an airline or matching for a marketplace. Build it in-house with your best people and your richest models.
- Supporting: needed and specific to you, but not a differentiator. Build it with plain code and little ceremony.
- Generic: solved problems, such as identity, payments or email. Buy it or use open source.
A strong answer adds that the ranking changes over time, and that heavy DDD patterns in a generic subdomain are wasted effort.
4. What is a context map, and which relationships does it show?
A context map shows the bounded contexts and how they depend on each other, including which team is upstream and which is downstream. The DDD Crew's context mapping reference lists the patterns:
- Partnership: two teams plan changes together and succeed or fail together.
- Shared kernel: a small piece of model shared as code. Cheap at first, costly to change.
- Customer and supplier: the upstream team takes the downstream team's needs into its planning.
- Conformist: the downstream team adopts the upstream model as is.
- Anticorruption layer: the downstream team translates the upstream model into its own.
- Open host service and published language: the upstream team offers a stable protocol and format for everyone.
- Separate ways: no integration at all, because it costs more than it returns.
The map is about teams as much as code. It shows who can ask whom for changes.
5. When do you need an anticorruption layer?
When an upstream model is messy, legacy, or owned by someone who will not change it for you, and letting it leak would damage your model. The ACL is a translation layer at the edge of your context.
public class LegacyCrmCustomerAdapter implements CustomerDirectory {
private final CrmClient crm;
public Optional<Customer> find(CustomerId id) {
CrmRecord r = crm.fetch(id.value());
if (r == null || "X".equals(r.statusCode())) return Optional.empty();
return Optional.of(new Customer(id, new Email(r.mail1()), Tier.from(r.segCd())));
}
}
The domain only sees CustomerDirectory and Customer. The CRM's status codes and field names stay inside the adapter. ACLs cost code and upkeep, so use them where the upstream model would do harm, not everywhere.
6. How do you find bounded context boundaries?
Look for where the language changes, where different people own the rules, and where data changes at different rates. Workshops such as Event Storming make these seams visible fast.
Useful heuristics: a term that means two things to two groups; a process step handed from one department to another; a group of events that only one team cares about.
Poor heuristics are the database schema, the org chart alone, or one context per entity. A context per entity usually produces tiny services that must call each other for every request.
7. Is a bounded context the same as a microservice?
No. A bounded context is a model boundary, and a microservice is a deployment unit. A context is a good upper limit for a service, and one context may be deployed as one or several services.
Going the other way is the problem. A service that spans several contexts mixes their models. Many teams now start with a modular monolith: one deployable, with each bounded context as a module and enforced boundaries.
They split a module out only when scaling or team autonomy calls for it. See our microservices interview guide for the deployment side.
Building Blocks
8. What is the difference between an entity and a value object?
An entity has an identity that lasts while its attributes change, such as a Customer with an ID. A value object has no identity and is defined only by its values, such as Money(100, "USD").
- Equal when the IDs match
- Mutable, with a lifecycle
Customer,Order,Policy
- Equal when all values match
- Immutable; a change makes a new value
Money,Email,DateRange
Two value objects with the same values are equal and can be swapped. That is why they should be immutable: a change produces a new value. Fowler's Value Object note covers the equality rules.
The same concept can be an entity in one context and a value in another. An address is a value on an order, but it may be an entity in a delivery-routing context.
9. Why use value objects instead of primitives?
They put validation and behaviour in one place, and make invalid states impossible to build. A String email can hold anything. An Email can only hold a valid address.
public record Money(BigDecimal amount, Currency currency) {
public Money {
if (amount.scale() > currency.getDefaultFractionDigits())
throw new IllegalArgumentException("too many decimals");
}
public Money plus(Money other) {
if (!currency.equals(other.currency)) throw new IllegalArgumentException("currency mismatch");
return new Money(amount.add(other.amount), currency);
}
}
Method signatures also get clearer: transfer(Money, AccountId) cannot be called with its arguments swapped, unlike transfer(BigDecimal, String). Java records, Kotlin data classes and C# records make value objects cheap to write.
10. What is the difference between a domain event and an integration event?
A domain event records something that happened inside a bounded context, in that context's own language. An integration event is the published contract that other contexts consume.
They are often kept separate on purpose. Domain events can change as the model changes. Integration events are a public API with versioning rules.
A common flow maps OrderShipped (domain) to order.shipped.v1 (integration) at the boundary, dropping internal fields. Publishing raw domain events to the whole company couples every consumer to your internal model.
11. What is the difference between a domain service and an application service?
A domain service holds domain logic that does not belong to one entity or value object, such as working out a price from several aggregates.
An application service runs a use case: it loads aggregates, calls the domain, saves, and handles transactions and security.
A quick test: if the business would describe the logic, it belongs in the domain. If only engineers care, such as "open a transaction, check permissions, send a notification", it belongs in the application layer.
Application services should stay thin. If they start holding if statements about business rules, the model is becoming anemic.
Aggregates
12. What is an aggregate, and what does the aggregate root do?
An aggregate is a cluster of objects that must stay consistent together, changed as one unit in one transaction. The root is the single entity outside code may hold and call.
The root enforces the invariants for the whole cluster. An Order root might guarantee that line totals match the order total and that a shipped order cannot gain lines. Outside code calls order.addLine(...), never order.getLines().add(...).
Fowler's DDD Aggregate note describes the idea.

13. What are the rules for designing aggregates?
Vaughn Vernon's Effective Aggregate Design gives four:
- Model true invariants inside consistency boundaries. Only rules that must hold at every commit belong in one aggregate.
- Design small aggregates. Large ones load slowly and collide under concurrent edits.
- Reference other aggregates by identity, not by object.
- Use eventual consistency outside the boundary.
The first rule does the most work. Many rules the business states as "always" can tolerate a few seconds of delay once you ask. That answer lets you split an aggregate.
14. How do you enforce a rule that spans two aggregates?
First ask whether it must hold at every instant. If a short delay is fine, use a domain event and a policy that reacts, with a compensating action if the rule is broken.
If it must hold at once, there are three options:
- Redraw the aggregate so the rule sits inside one boundary.
- Add a small aggregate whose only job is the rule, such as a
CourseCapacitycounter. - Use a database constraint for simple uniqueness.
Dynamic Consistency Boundaries (question 21) are a newer option for event-sourced systems.
15. How do you handle concurrent changes to one aggregate?
With optimistic locking. Each aggregate carries a version number. The save succeeds only if the stored version still matches the one that was loaded.
UPDATE orders SET status = 'SHIPPED', version = version + 1
WHERE id = 'o-42' AND version = 7;
-- 0 rows updated means someone else changed the order: reload and retry, or report a conflict
Frequent conflicts are a design signal. They usually mean the aggregate is too big, or that unrelated changes share one root. In event-sourced systems, the same check is an "expected version" on the append to the stream.
16. How do you publish domain events reliably?
Save the events in the same transaction as the aggregate, then publish them after commit. The usual pattern is a transactional outbox table, read by a relay that sends to the broker.
Publishing directly to the broker inside the use case risks a "dual write". The database commits and the broker call fails, or the other way round.
In an event-sourced system, the events are the saved state, so the event store's subscriptions do the publishing. Either way, consumers get at-least-once delivery and must handle duplicates.
Architecture
17. How does hexagonal architecture support DDD?
It keeps the domain at the centre with no dependency on frameworks, databases or transports. The domain defines ports (interfaces), and adapters implement them for HTTP, SQL, messaging and so on.
In a classic layered design, the domain often depends on the persistence layer below it. Hexagonal architecture, described by Alistair Cockburn, inverts that. The domain can then be tested with in-memory adapters and no database.
Onion and clean architecture apply the same dependency rule under other names.
18. What is an anemic domain model, and when is it acceptable?
An anemic model has entities with only getters and setters, while all rules live in service classes. Fowler's Anemic Domain Model note calls it an anti-pattern because nothing stops code from putting an object into an invalid state.
It is acceptable where there is little logic to protect, such as a supporting subdomain that is mostly forms and reports. Calling every data class "anemic" misses the point.
The real smell is business rules duplicated across several services because the entity cannot protect itself.
19. How do CQRS and event sourcing fit with DDD?
They are optional tools that suit some aggregates. CQRS gives commands a rich domain model and gives queries separate read models. Event sourcing stores an aggregate as its sequence of domain events.
CQRS helps when screens need data shaped differently from the aggregates. Event sourcing helps when history is part of the domain, such as ledgers or audit trails. Both add cost: projections, lag and event versioning.
A good answer applies them to one context that needs them, not to the whole system.
20. When is DDD not worth it?
When the domain is simple. CRUD apps, thin integrations and generic subdomains gain little from aggregates and domain events, and they pay for the extra layers.
The strategic parts, such as context mapping, a shared language and knowing your core domain, are cheap and help almost everywhere. The tactical patterns are the expensive part.
Candidates who say "we used DDD" should be able to name which parts, and why those parts fit that domain.
What Changed Recently
@Stereotype21. What are Dynamic Consistency Boundaries?
Dynamic Consistency Boundaries (DCB) enforce a rule across several entities in an event-sourced system by tagging events and checking a query at write time, instead of fixing the boundary in one aggregate.
The dcb.events site credits the idea to Sara Pellegrini's "Killing the Aggregate" posts. Its example is a course with limited seats and a student with a course limit. With aggregates, this needs a saga or two events for one fact.
With DCB, one StudentSubscribedToCourse event carries both a course tag and a student tag. The writer reads the events matching both tags. It then appends with a condition that fails if a matching event arrived in the meantime.
The specification defines the query, tags and append condition. DCB does not remove aggregates. It gives a second option when a rule does not fit one of them.
22. What changed in Axon Framework 5?
The API was rebuilt to be async-native, and the event store gained tags and append conditions that support DCB. Version 5.0 shipped on November 18, 2025.
Per the 5.0 release notes, command handling, repositories, the event store and event processors are async-native.
Message types use a QualifiedName instead of the Java class name, and stateful command handlers can be defined without annotations. The notes say migration tooling from version 4 would follow in 5.1.
The 5.1 notes (April 28, 2026) brought back snapshotting and split some Axon Server features into a separate Axoniq Framework.
23. How does Spring Modulith help build a DDD modular monolith?
It treats each top-level package as an application module, usually one bounded context, and verifies that modules only use each other's public APIs. Modules can talk through application events with a persistent publication registry.
The Spring Modulith 2.0 release (November 21, 2025) overhauled the event publication lifecycle across JDBC, JPA, MongoDB and Neo4j. It also added Flyway migrations per module and an option to verify the module structure at startup.
For an interview, the point is that context boundaries inside one deployable can now be tested in the build, not only drawn on a whiteboard.
24. What is jMolecules, and what changed in version 2.0?
jMolecules is a set of annotations and interfaces that mark DDD concepts in Java code, such as @AggregateRoot, @ValueObject and @Repository. Tools can then check the architecture and generate boilerplate from those markers.
jMolecules 2.0, released on November 13, 2025, raised the baseline to Java 17. It added a @Stereotype meta-annotation and a full module-info.java. Spring Modulith can read these annotations when it documents and verifies modules.
25. What happened to EventStoreDB?
It was renamed KurrentDB in version 25.0, released on March 25, 2025. Teams that event-source their aggregates on it will see the new name in configs and metrics.
Per the 25.0 release notes, the rename covers the executable, configuration prefixes, HTTP headers, default usernames and metrics. The release also added archiving of old chunks to S3.
The streams-per-aggregate model, with an expected-version check on append, works as before.
Signs of a Strong Answer
- They name the core domain of a past project and explain why it got more modelling effort than the rest.
- They size aggregates from invariants, and ask whether "always" means at every commit.
- They keep bounded contexts and deployment units separate, and can argue for a modular monolith.
- They use value objects for money, IDs and quantities without being prompted.
- They separate domain events from published integration contracts.
- They say which DDD patterns they skipped on a project, and why.
Hiring DDD Engineers
Engineers who can model a domain well are rarer than engineers who know the pattern names. Second Talent matches companies with pre-vetted back-end engineers and Java developers from Asia, screened with questions like these.
Tell us the stack and we send a shortlist within 24 hours. Start hiring, or see our event-driven architecture interview guide.






