← 返回 walmartlabs 的题目列表Design Multi-Carrier Package Delivery Routing System
类型:qbank
Design a system that ships customer packages through multiple delivery partners (UPS, USPS, FedEx, last-mile contractors), choosing a partner per package based on cost, capacity, and SLA, and tracking the package through each partner's API.
Requirements
Functional: accept a package creation request (origin, destination, weight, dimensions, service-level requested); select a carrier; book the shipment with that carrier; track status updates from the carrier and surface them to the customer; handle exceptions (carrier rejects pickup, package lost, address invalid).
Non-functional: carrier API quirks (per-carrier auth, rate limits, request schemas, webhook formats) must be isolated; the order placement path cannot block on carrier API latency; tracking updates must be eventually consistent across customer-facing UI; the routing decision must be auditable.
Data model: Shipment (id, order_id, carrier_id, carrier_tracking_number, status, created_at, sla_target_at); Carrier (id, name, capability_matrix, rate_card_url); RoutingDecision (shipment_id, candidates_considered, chosen_carrier, reason); CarrierEvent (shipment_id, carrier_event_type, raw_payload, normalized_status, observed_at).
Notes
The right abstraction is a CarrierAdapter interface: book(request) → quote, ship(quote) → tracking_number, track(tracking_number) → CarrierEvent[], cancel(tracking_number) → bool. One adapter per partner, all implementing the same interface. The routing service speaks the interface only; the adapter handles per-carrier HTTP, auth, rate-limit, and schema translation. New carrier onboarding becomes "write a new adapter," not "modify the routing service."
The carrier selection is a pluggable policy layer above the adapters. Inputs: package attributes + per-carrier capability matrix + live rate quotes (optionally cached). Output: ordered list of carrier candidates with score and reason. Persist the full ordered candidate list in RoutingDecision so post-incident analysis can replay why a given shipment went to FedEx instead of UPS.
Booking with the carrier must not be synchronous on the order placement path. The order service writes the shipment intent and emits a ShipmentRequested event; a downstream worker consumes the event, runs the routing decision, calls the adapter to book, and writes back the tracking number. This keeps order placement decoupled from carrier API outages and lets the worker queue absorb rate-limit pushback.
Tracking updates flow inbound through two paths: carrier webhooks (push) and per-shipment polling (pull, fallback for carriers without webhooks or for catching missed pushes). Both paths land on a normalization layer that maps carrier-specific event vocabularies (OUT_FOR_DELIVERY, EXCEPTION, IN_TRANSIT_NEW_HUB, …) onto a unified ShipmentStatus enum. Store the raw payload alongside the normalized form for audit.
For carrier API outages, the worker queue applies a circuit breaker per carrier: when an adapter's error rate spikes, the routing policy temporarily de-prioritizes that carrier and the breaker half-opens after a cool-down. Falling all carriers behind a breaker simultaneously is the operational nightmare to watch for; surface that state in dashboards.
For scale: shard the workers by shipment_id, store carrier events in a wide column store partitioned by (shipment_id, observed_at), and serve customer tracking pages from a cache populated by the normalization layer.
Preparation
Practice diagramming the three layers — order service / routing policy + adapter pool / carrier APIs — so the adapter abstraction comes out cleanly in under a minute.
Pre-compute back-of-envelope numbers for a Black Friday-scale day: shipments per second peak, average carrier API latency, expected webhook QPS per carrier. The interviewer in this round expected at least one quantitative pass.
Prepare two follow-ups: "a new carrier (regional last-mile) needs to be onboarded in two weeks — what changes" (answer: only the adapter + capability matrix; the policy reads the new entry without code change); "a shipment's webhook says delivered but the customer claims non-delivery — how do you investigate" (raw + normalized event store, correlation with carrier id, replay tool).