← Projects

Campus Delivery

Order, courier accept, live tracking, and a dispatcher map, built to stay correct under concurrent access and flaky networks.

Stack
Java 21, Spring Boot, PostgreSQL, PostGIS, Kafka, Spark, React, TypeScript
Source
github.com/ZhuoqunWei/campus-delivery

A delivery platform for a campus: customers place orders, couriers accept them and share their location, and dispatchers watch everything on a live map. The interesting part is keeping it correct when twenty couriers tap “accept” at once, phones reconnect mid-delivery, and clients retry requests.

Architecture. Customer, dispatcher, courier, and simulator clients talk to a Spring Boot backend over REST and WebSocket. The backend writes orders, offers, couriers, the event log, and outbox rows to PostgreSQL with PostGIS. An outbox relay publishes to Kafka topics, which a Spark Structured Streaming job turns into per-minute metrics stored as date-partitioned Parquet. CLIENTSCustomer appDispatcher mapCourier consoleSimulatorSPRING BOOTREST APIWebSocket · replay + liveAssignment · nearest courierOutbox relayPOSTGRESQL + POSTGISorders, offers, couriersevent logoutboxsame transaction as stateRESTWSwriterelay publishesKAFKAorder-events.v1courier-events.v1courier-locations.v1SPARK STRUCTURED STREAMINGper-minute metricswatermarked windowsPARQUETmetrics filespartitioned by date
How the pieces connect. A state change and its outbox row commit in the same PostgreSQL transaction. A relay then publishes the outbox to Kafka, and a Spark job turns the events into per-minute metrics.

Pieces

Correctness under concurrency

Concurrent tests pin down the two races that matter most.

Left: twenty couriers accept the same offer at once and exactly one assignment results. Right: ten retries with the same idempotency key produce exactly one order. 20 SIMULTANEOUS ACCEPTS1 assignment10 RETRIES · SAME IDEMPOTENCY KEY1 order
The races, tested. Twenty couriers accept the same offer at the same moment and exactly one assignment results. Ten retries of one request with the same idempotency key create exactly one order.

CI runs 22 backend integration tests against real PostGIS and Kafka through Testcontainers, 8 analytics tests (six batch, two Kafka streaming), the web build, and simulator type-checking.

Measured

records processed replaying history (about 120k in 20 s)
~6k / s
p95 completed-order-to-query latency, 17 local test orders
5.2 s
backend and analytics tests in CI
30

Replay throughput is a batch measurement, not sustained live traffic, and the latency figure doesn’t apply to the watermarked per-minute aggregates.