Amazon Locker
A self-service pickup point: a courier drops a package into whichever locker fits it, the system texts the customer a code, and the customer punches that code into a keypad to get their package back. The whole design lives or dies on one question - who is allowed to open a given door, and for how long.
Requirements
Functional
- A courier drops off a package for a customer; the system picks a free locker of a suitable size and stores the package inside it.
- The customer receives a pickup code for that package.
- The customer enters the pickup code at the bank's keypad; if it matches an occupied locker, that locker opens and is freed for the next package.
- A locker bank has a fixed set of lockers in several sizes (small, medium, large).
Non-functional
- A pickup code must not unlock the wrong locker, and must not still work after the package has already been collected.
- Assigning a locker should never hand out one that's already holding another package, even if drop-offs and pickups happen back-to-back at the same bank.
Design
LockerBank is the only class that knows about physical lockers - it hands one out through
a pluggable LockerAssignmentStrategy and takes it back through verifyAndOpen. A Package
never opens its own locker; it just sits inside one until a matching code shows up at the
bank.
- 1The courier only ever talks to the bank, never picks a locker itself.
- 2The bank delegates the "which locker" decision entirely.
- 3The chosen locker is filled and marked occupied.
- 4A pickup code is minted and tied to this delivery, not to the locker.
- 5Later, the customer presents only the code - never a locker number.
- 6A matching code frees the locker for the next delivery.
The pickup code is generated once, at drop-off, and is checked against exactly the locker it
was minted for. Delivery is the record that ties a courier's drop-off to a customer's
eventual pickup, independent of which physical locker ends up being used.
Class diagram
Code
Design decisions
LockerAssignmentStrategyis its own interface, not a method onLockerBank. The obvious first cut is smallest-fit (give the package the smallest locker it fits in, so large lockers stay free for large packages); a real bank might instead balance by location within the bank to keep foot traffic spread out. Pulling it out means that swap never touches drop-off or pickup logic - the parallel toparking-lot.mdx'sFeeStrategyis intentional, both are "the one thing that changes" wrapped so nothing else has to.- The pickup code lives on the
Delivery, not theLocker. A locker is just a numbered box; it has no idea whose package is inside or what code opens it. That knowledge belongs to the delivery record, so a locker can be reused for a completely different customer the moment it's freed, with zero cleanup on the locker itself. verifyAndOpentakes a code, not a locker id. If the caller had to say "open locker 17" the system couldn't tell a legitimate pickup from someone trying doors at random. Code lookup means the only path to opening a locker is knowing the secret that was texted to the rightful customer.- What's missing for a real system: codes need an expiry (a package sitting for two weeks should trigger a return-to-sender flow, not stay claimable forever), and concurrent drop-offs at the same bank need the assignment step to be atomic - two couriers requesting a locker in the same instant must never both get handed locker 17.