Clarify the product boundary
For seat booking, ask what is reserved, how long a hold lasts, how payment works and which operations must be globally consistent. Start with a small model: Seat, Hold, Booking, PaymentAttempt and explicit state transitions. Keep a clock and persistence/payment interfaces at testable boundaries. A class diagram helps communication, but an operation walkthrough establishes whether the classes can preserve the required rules.
Concurrency belongs in the invariant
Two requests can both read an available seat before either writes. Enforce the reservation through an atomic conditional storage operation or suitable constraint and transaction. A lock inside one application instance does not coordinate the fleet. A payment provider and a database generally do not share one local transaction; use idempotency keys and explicit recovery/reconciliation for uncertain outcomes rather than assuming a failed HTTP call means no charge happened.
Test the uncomfortable transitions
Specify expiry, cancellation, confirmation, duplicate request behavior and payment success followed by local failure. Preserve an audit trail for uncertain outcomes. State whether the system retries automatically, returns pending or requires reconciliation. Test with a fake clock and deterministic failures, then verify the real persistence constraints in integration tests. Explain how a new payment provider or seat type changes the design without promising that every future requirement is free.
Worked Java example
Illustrative snippet: imports, surrounding types or collaborators may be omitted. Read the assumptions before adapting it.
interface SeatStore {
// Must atomically reserve only if no conflicting active hold exists.
Hold tryHold(SeatId seat, CustomerId customer,
Instant expiresAt, String requestKey);
}
interface PaymentGateway {
PaymentResult charge(Money amount, String idempotencyKey);
}Walkthrough
The interfaces state the critical promises but do not implement them. SeatStore needs a real storage mechanism proving conflict exclusion. PaymentGateway needs a provider-supported idempotency contract and a policy for timeouts/unknown outcomes. An application service coordinates these boundaries and records transitions so a crash can be reconciled.
Practice it yourself
The provider charged successfully, but the service crashed before marking the booking confirmed. What must the retry do?
Check your reasoning
Use the same idempotency key to recover the original payment outcome rather than initiate another charge. Reconcile the durable booking/hold state, confirm only if the seat can still be assigned under the chosen contract, and define compensation or manual resolution if it cannot. Never infer no charge solely from a timeout.
Recall checklist
- Explain the main invariant or contract without looking.
- Trace the example and test an edge case.
- State time, space, input variables and assumptions where relevant.
- Explain one alternative and when you would choose it.