Designing a Scalable Delivery Dispatch System for Chain Restaurants
The article details a three‑module architecture—capacity verification, platform routing, and status synchronization—showing how a chain restaurant can dynamically choose third‑party delivery services across multiple cities, handle asynchronous checks, prioritize platforms, and ensure reliable callbacks and resends.
Overall Flow
When a self‑service order is placed, the order service records it as a delivery order. After the kitchen finishes cooking, staff trigger the dispatch process, which validates address coverage, selects a delivery platform, and pushes the order to that platform. The rider’s status is then synchronized back to the user.
Capacity Verification
The system evaluates five conditions in order of cost: store open today, delivery enabled, straight‑line distance within the store’s max range, acceptable riding distance, and third‑party rider availability. The first three are simple DB reads; the last two call external map and platform APIs.
A filter chain processes these checks: a synchronous chain runs the cheap checks sequentially, while an asynchronous chain launches the two external calls in parallel, returning as soon as the slower of the two completes. The overall timeout is set to 3 seconds, covering most cases.
Distance calculation uses a two‑step approach: a quick straight‑line filter (meters) rejects out‑of‑range addresses without a map call, then a riding‑distance check accounts for terrain and road curvature. Each platform can have an independent coverage coefficient (e.g., 0.9) to fine‑tune the effective radius without code changes.
Platform Routing and Dispatch Strategy
After capacity verification, the system decides which platform to dispatch. Routing rules support two dimensions: city‑level defaults (e.g., Shenzhen → SF Express, Guangzhou → Meituan, Chengdu → JD) and store‑level overrides, with store settings taking precedence.
Configuration data stores each store’s platform support flags, priority list, and a reference to the city configuration. When routing, the system first reads the store’s config, falling back to the city config if absent.
Priority and Fallback
// Try platforms in priority order
for (DeliveryPlatform platform : priorityList) {
DeliveryResult result = tryDispatch(order, platform);
if (result.isSuccess()) {
return result;
}
log.warn("platform {} dispatch failed, fallback", platform.name());
}If a platform returns “address out of coverage,” the system falls back to the next platform. For timeouts or network errors, the request may be retried on the same platform or downgraded. When all candidates fail, the failure reason is logged and an SMS alerts operations for manual intervention.
Unified Abstraction for Platform Integration
Each delivery provider (SF Express, JD, Meituan, Dada, etc.) implements a common interface, isolating the dispatch layer from provider‑specific details.
public interface DeliveryPlatformService {
String platformCode();
DeliveryResult createOrder(DeliveryOrderRequest request);
DeliveryResult cancelOrder(String platformOrderNo, String reason);
RiderPosition getRiderPosition(String platformOrderNo);
}Platform credentials (keys, endpoints, merchant codes) are managed in a configuration center per environment, avoiding hard‑coded secrets.
Delivery Status Callback and Synchronization
The rider’s lifecycle follows the state machine: CONFIRMED → ARRIVED → DISPATCHED → COMPLETED → CANCELLED. Each provider pushes status updates via callbacks with differing formats; the system maps them to the internal enum and writes them to the delivery record.
// Internal unified states
CONFIRMED // rider accepted order
ARRIVED // rider arrived at store
DISPATCHED // rider picked up food
COMPLETED // delivery finished
CANCELLED // order cancelledCallbacks are idempotent: duplicate notifications are ignored to prevent repeated writes or downstream triggers. After updating the internal state, the system publishes a message to a queue so that order services and user‑facing notifications (push, mini‑program messages) are processed asynchronously.
Rider real‑time location is also abstracted; callers receive latitude/longitude without caring about the underlying provider.
Resend (补单) Mechanism
If a delivery fails after the rider has picked up the order (e.g., lost order on the provider side), a resend request is issued with a special order‑number suffix (‑1, ‑2, …) to avoid conflicts. The store UI records a resend log, and each retry increments the suffix, ensuring uniqueness for the provider.
Practical Issues in a Chain Scenario
Capacity shortage alerts : The system aggregates dispatch failures per city within a time window; exceeding a threshold triggers an alert to the operations on‑call group.
Timing of rider call : A configurable “call‑ahead” offset lets the system request a rider N minutes before the expected cooking completion, with N adjustable per store type or peak period.
Separation of pre‑check and final dispatch : A lightweight pre‑check (distance only) runs when the user selects an address, while the full platform check runs after cooking. Because coverage can change in the interim, the two results are kept separate.
Conclusion
The design is straightforward but challenging due to multi‑city, multi‑platform routing, heterogeneous external APIs, and latency control for capacity checks. Configurable routing is essential for operations to adapt to real‑time rider availability without code releases. The filter‑chain pattern simplifies adding new validation steps, and careful idempotency plus message‑queue integration ensures stable production behavior.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
samdeepthink
Knowledge Planet: Old Dock's Tech Chronicles Zhihu: SamDeepThinking A technical manager who still codes heavily on the front line. From junior developer to tech lead, then tech manager, now leading the whole front‑ and back‑end development team—leveling up along the way. I have some insights on programming, career development, and tech management.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
