MetroRide is a production-style distributed ride dispatch platform focused on event-driven backend architecture and cloud-native infrastructure engineering. It models a real-time dispatch domain with independently deployable Go services, Redis Streams for asynchronous coordination, PostgreSQL for durable ride state, and an observability stack built around Prometheus and Grafana.
The project is designed as a backend systems portfolio artifact: the emphasis is service ownership, event contracts, failure boundaries, operational visibility, and a path from local Docker Compose to Kubernetes-based deployment.
flowchart LR
RiderClient[Ride Request Client] -->|REST POST /v1/rides| Rider[rider-service]
DriverSim[Driver Simulation] --> Driver[driver-service]
Rider -->|persist ride| Postgres[(PostgreSQL)]
Rider -->|ride_requested| Redis[(Redis Streams)]
Driver -->|driver_location_updated| Redis
Traffic[traffic-service] -->|traffic_updated| Redis
Redis -->|consumer group| Dispatch[dispatch-service]
Redis -->|driver location stream| Routing[routing-service]
Dispatch -->|nearest-driver query| Routing
Dispatch -->|assignment state| Postgres
Dispatch -->|ride_assigned| Redis
Redis -->|assignment notification| Notification[notification-service]
Rider -.->|/metrics| Prometheus[Prometheus]
Dispatch -.->|/metrics| Prometheus
Routing -.->|/metrics| Prometheus
Driver -.->|/metrics| Prometheus
Notification -.->|/metrics| Prometheus
Prometheus --> Grafana[Grafana]
sequenceDiagram
participant Client as Rider Client
participant Rider as rider-service
participant DB as PostgreSQL
participant Bus as Redis Streams
participant Dispatch as dispatch-service
participant Routing as routing-service
participant Notify as notification-service
Client->>Rider: POST /v1/rides
Rider->>DB: Insert ride(status=requested)
Rider->>Bus: XADD ride_requested
Dispatch->>Bus: XREADGROUP events.ride.requests
Dispatch->>Routing: POST /v1/routes/nearest-driver
Routing-->>Dispatch: driver_id, distance_km, eta_seconds
Dispatch->>DB: Update ride(status=assigned)
Dispatch->>Bus: XADD ride_assigned
Dispatch->>Bus: XADD notification event
Notify->>Bus: XREADGROUP events.ride.notifications
flowchart TB
subgraph Local["Local Runtime"]
Compose[Docker Compose]
Compose --> GoServices[Go Microservices]
Compose --> Redis[(Redis)]
Compose --> Postgres[(PostgreSQL)]
Compose --> Prometheus[Prometheus]
Compose --> Grafana[Grafana]
end
subgraph Cloud["Cloud-Native Deployment Path"]
K8s[Kubernetes Manifests]
Helm[Helm Chart Scaffold]
K8s --> Deployments[Service Deployments]
K8s --> Services[Cluster Services]
Helm --> Templates[Parameterized Templates]
end
Prometheus --> Grafana
GoServices --> Redis
GoServices --> Postgres
| Service | Responsibility | Communication Pattern |
|---|---|---|
rider-service |
Accepts ride requests, persists rider-facing state, emits ride_requested. |
REST ingress, PostgreSQL writes, Redis Streams publish |
driver-service |
Simulates live driver coordinates and availability. | Redis Streams publish |
dispatch-service |
Consumes ride requests, coordinates driver assignment, persists assignment state. | Redis Streams consumer group, REST call to routing, PostgreSQL writes |
routing-service |
Maintains available driver state and calculates nearest-driver ETA. | REST API, Redis Streams consumer |
traffic-service |
Produces dynamic congestion updates for future route weighting. | Redis Streams publish |
notification-service |
Consumes assignment events and simulates rider/driver delivery. | Redis Streams consumer group |
- Language: Go for backend services; Python reserved for future simulation or analytics utilities.
- Event bus: Redis Streams with consumer groups for durable asynchronous workflows.
- Storage: PostgreSQL as the system of record for ride and assignment state.
- APIs: REST for synchronous service boundaries, with protobuf/gRPC scaffolding reserved in
shared/proto. - Observability: Prometheus metrics, Grafana dashboards, structured JSON logs, health and readiness probes.
- Runtime: Docker Compose for local orchestration.
- Cloud-native deployment: Kubernetes manifests and Helm chart scaffold.
MetroRide separates state mutation, assignment coordination, routing computation, and notification delivery into independently deployable services. Ride intake is synchronous only at the API boundary; downstream dispatch work is asynchronous through Redis Streams. This design keeps rider request latency decoupled from assignment processing, makes dispatch consumers horizontally scalable, and provides a foundation for replay, backpressure handling, and transport migration.
Redis Streams are used as the initial event log because they provide persistent streams, consumer groups, explicit acknowledgements, and simple local development ergonomics. The event envelope in shared/pkg/events keeps transport concerns isolated so Kafka can be introduced later without rewriting domain payloads.
PostgreSQL remains the authoritative store for ride status. Redis carries workflow events; it does not own long-term ride truth. This separation mirrors production systems where event logs coordinate distributed work while relational storage protects transactional state and queryability.
MetroRide is a production-style local distributed systems project designed to demonstrate backend infrastructure concepts: service decomposition, asynchronous workflow coordination, durable state, reliability controls, and observability. It is not presented as a real production deployment at scale; it is structured so the architecture can be explained and extended like a production backend system.
Every service exposes:
GET /healthzGET /readyzGET /metrics
Key metrics include:
metroride_ride_requests_totalmetroride_dispatch_latency_secondsmetroride_rides_assigned_totalmetroride_assignment_failures_totalmetroride_stream_consume_errors_totalmetroride_dependency_errors_totalmetroride_routing_computation_secondsmetroride_active_drivers
Prometheus scrapes service metrics, and Grafana provisions a MetroRide dashboard with ride request rate, dispatch latency, routing latency, active drivers, and assignment failures.
See docs/observability.md for the monitoring strategy.
MetroRide includes production hardening for dependency-aware readiness checks, bounded timeouts, retry behavior, idempotent ride assignment, and a Redis dead-letter stream for failed dispatch events.
See docs/reliability.md for timeout strategy, retry behavior, idempotency design, dead-letter semantics, and expected behavior during Redis, PostgreSQL, routing, and dispatch failures.
MetroRide includes a lightweight optional Kafka profile for driver location telemetry. The core Redis Streams dispatch workflow is unchanged; Kafka is used only to demonstrate topic-based streaming, producers, consumers, consumer groups, partition keys, and replay concepts.
Kafka does not start by default:
docker compose up -dStart the optional Kafka extension explicitly:
docker compose --profile kafka up -d
ENABLE_KAFKA_SMOKE=true bash scripts/smoke-test.shSee docs/kafka-lightweight-extension.md and docs/kafka-interview-notes.md.
MetroRide includes GitHub Actions CI for Go package tests, Docker Compose validation, image builds, stack startup, smoke testing, and backend integration tests against the running distributed system.
See docs/testing-and-ci.md for the CI pipeline, local test commands, smoke test coverage, integration test coverage, and future testing improvements.
.
├── docker-compose.yml
├── docs/
│ ├── api.md
│ ├── architecture.md
│ ├── architecture-decisions.md
│ ├── interview-qa-bilingual.md
│ ├── interview-talk-track.md
│ ├── kafka-interview-notes.md
│ ├── kafka-lightweight-extension.md
│ ├── observability.md
│ ├── reliability.md
│ ├── resume-bullets.md
│ ├── system-design.md
│ └── testing-and-ci.md
├── infrastructure/
│ ├── docker/
│ ├── grafana/
│ ├── helm/
│ ├── k8s/
│ └── prometheus/
├── scripts/
│ └── smoke-test.sh
├── services/
│ ├── dispatch-service/
│ ├── driver-service/
│ ├── notification-service/
│ ├── rider-service/
│ ├── routing-service/
│ └── traffic-service/
└── shared/
├── events/
├── pkg/
└── proto/
Start the platform:
docker compose up --buildCreate a ride:
curl -X POST http://localhost:8080/v1/rides \
-H 'Content-Type: application/json' \
-d '{"rider_id":"rider-42","pickup_lat":37.775,"pickup_lng":-122.419,"dropoff_lat":37.789,"dropoff_lng":-122.401}'Query the ride state:
curl http://localhost:8080/v1/rides/<ride_id>Run the smoke test:
bash scripts/smoke-test.sh| Component | Port |
|---|---|
rider-service |
8080 |
driver-service |
8081 |
dispatch-service |
8082 |
routing-service |
8083 |
traffic-service |
8084 |
notification-service |
8085 |
| Prometheus | 9090 |
| Grafana | 3000 |
| PostgreSQL | 5432 |
| Redis | 6379 |
Grafana defaults to admin / admin.
Docker Compose is the primary local runtime:
docker compose up --buildKubernetes manifests live in infrastructure/k8s:
kubectl apply -f infrastructure/k8s/namespace.yaml
kubectl apply -f infrastructure/k8s/Helm scaffold lives in infrastructure/helm/metro-ride:
helm install metro-ride infrastructure/helm/metro-rideThe Kubernetes and Helm artifacts are intentionally scaffolded for production evolution: image registries, secrets management, persistent volumes, ingress, autoscaling, and service monitors can be layered in without changing service code.
- Architecture
- System design
- Interview talk track
- Bilingual Interview Q&A
- Architecture decisions
- API
- Kafka lightweight extension
- Kafka interview notes
- Observability
- Reliability
- Testing and CI
- Resume bullets
- Kafka migration: Replace Redis Streams with Kafka while preserving the shared event envelope.
- gRPC service mesh: Move dispatch-to-routing calls to protobuf/gRPC with deadlines and retries.
- Distributed tracing: Add OpenTelemetry spans across ride intake, stream consumption, routing, and notification delivery.
- Autoscaling: Scale dispatch consumers based on stream lag and assignment latency.
- Multi-region deployment: Partition drivers and rides by region, then replicate critical events across regions.
- AI-assisted ETA prediction: Introduce an ETA model service using traffic, driver, and route features.
- Demand forecasting: Add regional demand prediction to pre-position driver supply.
- Resilience hardening: Add dead-letter streams, idempotency keys, retry budgets, and circuit breakers.