Interview decision
Keep a concept only when identity, value semantics, lifecycle, or behavior makes it necessary to the primary use case.
Mental modelThe domain model is a working language for rules; it is neither a database schema nor a list of nouns from the prompt.
Use it to
- Distinguish a parking spot from the ticket that records occupancy.
- Represent a time range as a value with overlap behavior.
- Keep delivery outcomes separate from package identity.
Keep out of scope
- Mirroring tables and foreign keys.
- Giving every input field its own class.
See the responsibility boundary
A reservation has identity and lifecycle; a TimeRange is an immutable value; a RoomCalendar owns the collection whose overlap rule must remain true.
type ReserveResult = Reservation | Conflict;
interface RoomCalendar {
reserve(request: ReservationRequest): ReserveResult;
cancel(id: ReservationId): Result<void, CancelError>;
}Find concepts that earn a place
A domain concept earns representation when it has meaningful identity, lifecycle, state, or behavior. A label, timestamp, or simple amount may stay a value in a larger object.
In a parking facility, Vehicle, Spot, and Ticket have identities and behavior. LicensePlate and Money can be small values rather than service objects.
Interviewer follow-ups
- What behavior or state would be lost if this stayed a value?
- Which concept has its own lifecycle?
Do not over-engineer: Do not introduce an abstract base class solely because two records share fields.
Model the language of the problem
Names should reflect the business behavior you are implementing. The model becomes easier to test and explain when operations read like the use case.
A Reservation owns confirm and cancel transitions; a RoomCatalog answers availability. Neither needs a generic process method.
Interviewer follow-ups
- Can a new reader infer the primary flow from these names?
- Which object should reject an invalid action?
Do not over-engineer: Do not recreate a database schema in object form when the interview asks about behavior.
Test the model with a counterexample
A model becomes credible when it can explain both a happy path and a boundary case. Use an invalid transition or duplicate action to find missing ownership.
Try assigning an occupied spot twice. If any caller can mutate Spot status directly, the model lacks a protected allocation boundary.
Interviewer follow-ups
- What input makes this object invalid?
- Who can create or retire this entity?
Do not over-engineer: Do not encode every validation rule in constructors if a rule depends on current collaboration context.
Validate the design, not the diagram
Walk one representative flow
- Create a validated TimeRange value.
- Ask RoomCalendar to reserve it.
- Check overlap against confirmed reservations.
- Return a Reservation or Conflict without exposing the internal collection.
Tests that protect the reasoning
- Invalid or inverted time ranges cannot be created.
- Adjacent reservations do not overlap.
- A conflicting reservation is rejected and the calendar remains unchanged.
Evolve it without losing the boundary
- Follow-up change
- Add equipment requirements and decide whether they belong on Room, ReservationRequest, or a selection policy.
- Concurrency, when relevant
- Two simultaneous reservations matter because they can violate the same calendar invariant; the atomic reserve operation belongs at that owner.
- System Design boundary
- Database indexes and multi-office replication can enforce or distribute the rule later; first define the domain rule and its owner.
What depth looks like by level
- Entry
- Name essential entities and values.
- Mid
- Explain identity, lifecycle, and relationships.
- Senior
- Use counterexamples to refine aggregate and invariant boundaries.
- Staff+
- Separate domain ownership from storage and organizational ownership.