Go로 Uber-style rideshare 앱의 backend microservices system을 만들고, Kubernetes로 orchestration 한다.
- k8s + Tilt: YES
- Frontend/UI: NO (backend-first)
- Payments: Mock (이벤트 흐름만)
apps/: services + (optional) web appinfra/: Kubernetes/Tilt manifests and infra toolingproto/: Protobuf definitions (gRPC contracts)docs/: step-by-step notes
각 step별 상세 노트는 docs/steps/ 아래에 둔다.
추가로, Kafka/topic/consumer group 등의 기본 개념 메모는 docs/notes/ 아래에 정리한다.
개인용 workflow/conventions 메모는 로컬에서만 관리한다.
- Step 0: Repository bootstrap (README, gitignore, basic structure)
- Step 1: Local infra on k8s via Tilt (RabbitMQ, optional observability)
- Step 2: Protobuf + gRPC contracts (Trip)
- Step 3: Services skeletons (gateway, trip, driver, payment)
- Step 4: Sync flow: Gateway -> Trip (gRPC)
- Step 5: Async flow: Trip events over RabbitMQ -> Driver/Payment
- Step 6: WebSocket notifications (gateway)
- Step 7: Observability (OpenTelemetry + Jaeger)
- Step 8: Persistence (MongoDB) and repository layer
- Step 9: Hardening (timeouts, retries, DLQ), tests
- Step 10: Trace propagation over RabbitMQ
- Step 11: Replace RabbitMQ with Kafka (Sarama)
- Step 12: DLT replayer (replay
trip-events.dlt->trip-events) - Step 13: Trip lifecycle events (minimal saga)
- Infra (k8s via Tilt)
tilt up- Services
Option A) Using dev scripts (recommended)
./scripts/dev.sh start
./scripts/dev.sh logsStop
./scripts/dev.sh stopIf ports are stuck (e.g. 8080/50051 already in use)
./scripts/dev.sh clean-portsOption B) Run services manually
go run ./apps/trip
go run ./apps/gateway
go run ./apps/driver
go run ./apps/payment- Useful URLs
- Kafka UI:
http://localhost:8081 - Jaeger UI:
http://localhost:16686 - Gateway:
http://localhost:8080 - WebSocket:
ws://localhost:8080/ws
- Sync:
gatewayHTTP ->tripgRPC - Async:
trippublishes events to Kafka (trip-events) - Consumers:
driver/paymentconsume trip events - WebSocket:
gatewaybroadcasts events to connected WS clients - Observability: OpenTelemetry tracing (
gateway->trip) exported to Jaeger (OTLP) - Persistence:
tripstores started trips in MongoDB - Hardening: reconnect/backoff for dependencies + Kafka consumer groups + DLT for failed messages
- gateway: HTTP API + WebSocket, gRPC로 trip 호출
- trip: gRPC 서버, trip lifecycle 담당, 이벤트 발행
- driver: trip 이벤트 consume, driver matching 시뮬레이션
- payment: trip 이벤트 consume, payment flow 시뮬레이션
flowchart LR
Client["Client mobile web"]
subgraph Edge
Gateway["Gateway HTTP 8080"]
WS["Gateway WebSocket"]
end
subgraph Core_services
Trip["Trip gRPC 50051"]
Driver["Driver service"]
Payment["Payment service"]
DLTWorker["DLT worker"]
end
subgraph Messaging
Kafka["Kafka"]
Topic["topic: trip-events"]
DriverDLT["topic: trip-events.driver.dlt"]
PaymentDLT["topic: trip-events.payment.dlt"]
Quarantine["topic: trip-events.dlt.quarantine"]
end
subgraph Persistence
MongoDB["MongoDB"]
end
subgraph Observability
OTel["OTel Collector"]
Jaeger["Jaeger"]
end
Client -->|HTTP request| Gateway
Client -->|WebSocket connect| WS
Gateway -->|gRPC call| Trip
Trip -->|read write| MongoDB
Trip -->|publish TripStarted/TripCompleted/TripFailed| Topic
Topic -->|consume PaymentAuthorized/PaymentFailed| Trip
Topic -->|consume| Driver
Topic -->|consume| Payment
Driver -->|on failure| DriverDLT
Payment -->|on failure| PaymentDLT
DriverDLT -->|consume| DLTWorker
PaymentDLT -->|consume| DLTWorker
DLTWorker -->|replay publish| Topic
DLTWorker -->|on exhausted retries| Quarantine
Gateway -->|traces metrics| OTel
Trip -->|traces metrics| OTel
Driver -->|traces metrics| OTel
Payment -->|traces metrics| OTel
OTel -->|export| Jaeger
flowchart TB
Start["StartTrip (gRPC)"] --> PersistStarted["MongoDB: trips<br/>status=started<br/>created_at set"]
PersistStarted --> TS["Publish: TripStarted"]
TS --> DA["Driver consumes TripStarted<br/>Publish: DriverAssigned"]
DA --> Pay["Payment consumes DriverAssigned<br/>Publish: PaymentAuthorized | PaymentFailed"]
Pay -->|PaymentAuthorized| TripAuth["Trip consumes PaymentAuthorized"]
TripAuth --> PersistCompleted["MongoDB: trips<br/>status=completed<br/>completed_at set<br/>failed_at/failure_reason unset"]
PersistCompleted --> TC["Publish: TripCompleted"]
Pay -->|PaymentFailed| TripFail["Trip consumes PaymentFailed"]
TripFail --> PersistFailed["MongoDB: trips<br/>status=failed<br/>failed_at/failure_reason set<br/>completed_at unset"]
PersistFailed --> TF["Publish: TripFailed"]
TF --> DU["Publish: DriverUnassigned (compensation)"]
DU --> DriverUnassign["Driver consumes DriverUnassigned<br/>(handled as unassign)"]
- 각 step은 작고 리뷰 가능한 단위로 마무리한다.