Neighborhood Meal Delivery
Problem
Design NeighborhoodMealService for local kitchens and couriers. It keeps menu snapshots, moves orders through preparation, lets couriers accept delivery offers, and allows cancellation only before preparation or assignment prevents it. Multiple threads share one service instance.
Requirements
- Menu and order snapshots. Publishing a menu atomically replaces that kitchen's current item set. Creating an order requires a published kitchen and a non-empty unique item list from its current menu. The order keeps those items in caller order, and later menu changes do not affect it.
- Explicit lifecycle. A new order is
DRAFT. OnlyconfirmOrdercan change it toCONFIRMED; preparation eventSTARTEDthen changes it toPREPARING, andREADYchanges it toREADY. A repeated, skipped, reversed, or post-cancellation transition fails without mutation. - Competing offers and exclusive assignment. An offer may be created for an unassigned order in
CONFIRMED,PREPARING, orREADYwhen its courier is unassigned. Multiple active offers may involve one order or courier. Accepting one active offer assigns that courier and order to each other, accepts that offer, and makes every other active offer for the order or courier stale. - State-based cancellation. Cancellation succeeds only for an unassigned order in
DRAFTorCONFIRMED. It changes the order to terminal stateCANCELLEDand makes its active offers stale. - Stable failures and views. Order and offer identifiers are never reused. A rejected operation has no side effect.
getOrderreturns a detached snapshot whose items preserve creation order. - Atomic shared use. Every public call is linearizable: it takes effect at one instant between invocation and return. Calls preserve order within each thread, never wait for a domain condition, and preserve lifecycle and assignment invariants for every allowed interleaving.
API
OrderView field | Type | Meaning |
|---|---|---|
result | string | OK, INVALID_ARGUMENT, or NOT_FOUND. |
orderId | string | The found order ID, or empty on failure. |
kitchenId | string | The captured kitchen ID, or empty on failure. |
itemIds | string[] | The captured items in creation order, or empty on failure. |
state | string | The current order state, or empty on failure. |
courierId | string | The assigned courier ID, or empty while unassigned or on failure. |
Mutation methods return one of OK, INVALID_ARGUMENT, DUPLICATE_ID, NOT_FOUND, INVALID_STATE, COURIER_UNAVAILABLE, NOT_RECIPIENT, or STALE_OFFER.
| Signature | Returns | Behavior |
|---|---|---|
NeighborhoodMealService() | New service | Creates an empty service. |
publishMenu(kitchenId: string, itemIds: string[]) | string | Creates or replaces the complete current menu and returns OK. |
createOrder(orderId: string, kitchenId: string, itemIds: string[]) | string | Returns DUPLICATE_ID for an existing order ID, then NOT_FOUND for a missing kitchen or menu item, or creates an unassigned DRAFT order and returns OK. |
confirmOrder(orderId: string) | string | Returns NOT_FOUND for a missing order, INVALID_STATE unless it is DRAFT, or changes it to CONFIRMED and returns OK. |
reportPreparation(orderId: string, event: string) | string | Returns NOT_FOUND for a missing order, INVALID_STATE unless STARTED or READY is valid for its current state, or applies the event and returns OK. |
offerCourier(offerId: string, orderId: string, courierId: string) | string | Returns DUPLICATE_ID for an existing offer ID, NOT_FOUND for a missing order, INVALID_STATE for an ineligible or assigned order, or COURIER_UNAVAILABLE for an assigned courier. Otherwise creates an active offer and returns OK; couriers need no registration. |
acceptOffer(offerId: string, courierId: string) | string | Returns NOT_FOUND for a missing offer, NOT_RECIPIENT when the supplied courier differs from its bound courier, or STALE_OFFER when it is not active. Otherwise performs the exclusive assignment and returns OK. |
cancelOrder(orderId: string) | string | Returns NOT_FOUND for a missing order, INVALID_STATE when cancellation is not allowed, or cancels the order and returns OK. |
getOrder(orderId: string) | OrderView | Returns the current detached order snapshot or the failed view defined above. |
Every argument is non-null. An identifier must contain 1 to 64 ASCII letters, digits, _, or -; identifiers are case-sensitive and are not trimmed. Arrays must meet the bounds in Constraints and contain unique valid identifiers. reportPreparation accepts only STARTED and READY. Each method validates all argument shapes, nullability, identifiers, arrays, and finite-choice strings before reading service state. Any such failure returns INVALID_ARGUMENT.
Examples
| Step | Operation | Result |
|---|---|---|
| 1 | publishMenu("k1", ["bread", "soup"]) | OK |
| 2 | createOrder("o1", "k1", ["soup", "bread"]) | OK |
| 3 | confirmOrder("o1") | OK |
| 4 | offerCourier("f1", "o1", "c1") | OK |
| 5 | reportPreparation("o1", "STARTED") | OK |
| 6 | acceptOffer("f1", "c1") | OK |
| 7 | reportPreparation("o1", "READY") | OK |
| 8 | getOrder("o1") | OrderView(OK, o1, k1, [soup, bread], READY, c1) |
If two active offers for different couriers on the same order are accepted concurrently, exactly one acceptance returns OK; the other returns STALE_OFFER, and the successful courier appears in the order view.
Constraints
- A menu contains 1 to 64 unique item IDs.
- An order contains 1 to 32 unique item IDs.
- Each testcase has at most 1,000 kitchens, 10,000 orders, 20,000 offers, and 50,000 method calls.
- Counts and collection sizes fit signed 32-bit integers and never become negative.
- Concurrent cases use coordinated thread starts and do not depend on sleeps.
Notes
Across threads, any interleaving is valid when every completed call has one atomic effect point. Concurrent creation with one order or offer ID has exactly one OK; other valid calls return DUPLICATE_ID. Competing acceptance for one order or courier, including repeated acceptance of one offer, has exactly one OK; losing offers return STALE_OFFER.
Acceptance racing cancellation produces either OK and INVALID_STATE, leaving an assigned order, or STALE_OFFER and OK, leaving a cancelled order. STARTED racing cancellation on an unassigned CONFIRMED order has one OK and one INVALID_STATE. Menu publication racing order creation exposes either the complete old menu or the complete new menu. Reads return one valid snapshot. Calls do not retry automatically, promise fairness, or deadlock.