Interview decision
Place each decision and state change with the smallest component that has the information and authority to protect its invariant.
Mental modelResponsibility answers who decides, who mutates, who coordinates, and who observes; class names come after those answers.
Use it to
- Keep elevator movement safety inside each car.
- Let a dispatcher choose a car without controlling its doors.
- Separate package transition rules from courier assignment coordination.
Keep out of scope
- Creating one service for every verb.
- Forcing all behavior into entities when a cross-entity coordinator is clearer.
See the responsibility boundary
ElevatorCar owns doors, direction, and legal movement; Dispatcher owns car selection; DispatchPolicy owns a replaceable selection rule only when policies vary.
interface ElevatorCar {
snapshot(): CarSnapshot;
requestStop(floor: Floor): Result<void, CarStateError>;
}
interface DispatchPolicy { choose(request: HallRequest, cars: readonly CarSnapshot[]): CarId | null; }One understandable owner for each decision
Ownership is about who has the context to make a rule true. Put validation and transitions near the state they protect; coordinate only where a rule spans collaborators.
A Reservation decides whether it may cancel from its current state. A Scheduler coordinates room availability and creates a Reservation because it sees both.
Interviewer follow-ups
- Which object knows enough to make this decision?
- Who is allowed to change this state?
Do not over-engineer: Do not split a small cohesive object into five single-method classes just to demonstrate SOLID vocabulary.
Make lifecycle visible
Creation, retention, replacement, and disposal are design decisions. Explain who constructs an object, who holds it, and whether another component may retain a reference.
A Ticket is created by ParkingFacility and is returned to the caller; ParkingFacility keeps the active-ticket registry until payment closes it.
Interviewer follow-ups
- Who creates this object?
- What ends its lifecycle?
Do not over-engineer: Do not add a repository for every object if storage behavior is not part of the prompt.
Separate policy from coordination when it varies
A policy belongs behind a small contract when different rules should be substituted independently. Coordination belongs in the component that sequences the flow.
PricingPolicy calculates a parking fee; ParkingFacility asks it for a fee while remaining responsible for releasing a spot after payment.
Interviewer follow-ups
- What variation would justify an interface here?
- Can this behavior stay in the entity for now?
Do not over-engineer: Do not confuse dependency injection with a requirement to inject every collaborator.
Validate the design, not the diagram
Walk one representative flow
- Dispatcher receives a hall request.
- Policy selects from read-only car snapshots.
- The selected ElevatorCar validates and accepts the stop.
- The car owns door-close, movement, arrival, and door-open transitions.
Tests that protect the reasoning
- Dispatcher cannot make a car move with open doors.
- A car rejects an invalid floor without changing its queue.
- A new policy can be tested against the same snapshots.
Evolve it without losing the boundary
- Follow-up change
- Add maintenance mode and show why the car owns eligibility while the dispatcher filters candidates.
- Concurrency, when relevant
- Simultaneous requests may compete for a car; address atomic acceptance at the car rather than promising a globally optimal schedule.
- System Design boundary
- Building-wide event transport, device networks, and real-time control certification sit outside this interview-scale object model.
What depth looks like by level
- Entry
- Assign clear owners for movement and selection.
- Mid
- Separate policy from coordination when variation is real.
- Senior
- Explain race, failure, and lifecycle boundaries.
- Staff+
- Frame safety, hardware, and organizational boundaries without pretending to certify a control system.