System Design - Football Field Booking System
Version: 1.0 Last Updated: 2026-01-13 Owner: Tech Lead Architecture: Microservices (NX monorepo + NestJS) + Kafka + Redis + PostgreSQL + MongoDB + Keycloak + Signoz
1) High-Level Architecture
1.1 Components
- API Gateway (3000): single entry point, auth enforcement, routing, rate limiting, request validation, observability propagation.
- user-service (3001): user data, profiles, optional player profile/rating.
- field-service (3002): field catalog, amenities, pricing, availability metadata.
- booking-service (3003): booking lifecycle, conflict prevention, booking state machine.
- payment-service (3004): payment intents, webhooks, idempotency, refunds.
- notification-service (3005): email/ZNS (Phase 2), consumes Kafka events and records logs to MongoDB.
1.2 Infrastructure
- PostgreSQL: per-service OLTP databases (user/field/booking/payment) + Keycloak DB.
- Redis: caching + distributed locks (booking conflict) + rate limiting counters (optional).
- Kafka: async event bus for decoupling services.
- Keycloak: authentication + RBAC claims.
- Signoz (OpenTelemetry): tracing/metrics/logs correlation.
1.3 Diagram
flowchart LR
Client[Web/Mobile Client] -->|HTTPS| GW[API Gateway :3000]
subgraph Services
U[user-service :3001]
F[field-service :3002]
B[booking-service :3003]
P[payment-service :3004]
N[notification-service :3005]
end
subgraph Infra
KC[Keycloak]
R[Redis]
K[Kafka]
PGU[(Postgres User)]
PGF[(Postgres Field)]
PGB[(Postgres Booking)]
PGP[(Postgres Payment)]
MG[(MongoDB Logs)]
SZ[Signoz OTEL]
end
GW --> U
GW --> F
GW --> B
GW --> P
GW --> N
GW --> KC
U --> PGU
F --> PGF
B --> PGB
P --> PGP
N --> MG
U <--> R
F <--> R
B <--> R
P <--> R
U <--> K
F <--> K
B <--> K
P <--> K
N <--> K
GW --> SZ
U --> SZ
F --> SZ
B --> SZ
P --> SZ
N --> SZ
2) Service Responsibilities & Boundaries
2.1 API Gateway
- Token verification (Keycloak OIDC)
- Role enforcement (coarse-grained)
- Request shaping: consistent response format, requestId/trace propagation
- Rate limiting
- Routing to services
2.2 user-service
- User profile CRUD
- Role mapping (as stored/derived from Keycloak)
- Zalo user mapping (
zalo_user_id) for ZNS integration - (Phase 2) Player profile + ratings + ranking
2.3 field-service
- Field CRUD + owner-specific restrictions
- Search and filters (geo radius, sport type, amenities)
- Field status workflow (PENDING_APPROVAL → ACTIVE)
2.4 booking-service
- Booking creation and lifecycle:
- PENDING → CONFIRMED → COMPLETED
- PENDING/CONFIRMED → CANCELLED
- Conflict detection:
- distributed lock (Redis) + DB checks
- Publishes booking events to Kafka
2.5 payment-service
- Payment intent creation and tracking
- Webhook ingestion (Stripe/VNPay)
- Idempotency and reconciliation
- Publishes payment events to Kafka
2.6 notification-service
- Consumes Kafka events and sends notifications
- Logs delivery attempts in MongoDB
- Retry policies for transient provider failures
3) Data Flow Diagrams
3.1 Booking Flow (Create Booking)
sequenceDiagram
autonumber
participant C as Client
participant GW as API Gateway
participant B as booking-service
participant R as Redis
participant DB as Booking DB
participant K as Kafka
C->>GW: POST /v1/bookings (token)
GW->>B: Forward request + claims + trace
B->>R: Acquire lock lock:booking:{fieldId}:{start}:{end}
alt lock acquired
B->>DB: Check overlap + create booking (transaction)
DB-->>B: bookingId, status=PENDING
B->>K: Publish BOOKING_CREATED (optional)
B->>R: Release lock
B-->>GW: 201 booking created (expiresAt)
GW-->>C: 201
else lock not acquired
B-->>GW: 409 CONFLICT (another booking in progress)
GW-->>C: 409
end
3.2 Payment Flow (Confirm Payment)
sequenceDiagram
autonumber
participant C as Client
participant GW as API Gateway
participant P as payment-service
participant DB as Payment DB
participant K as Kafka
participant N as notification-service
participant M as MongoDB Logs
participant Provider as Payment Provider
C->>GW: POST /v1/payments/create
GW->>P: create payment intent
P->>DB: upsert payment (idempotency)
P->>Provider: create checkout session
Provider-->>P: checkoutUrl + provider ids
P->>DB: update payment=PENDING
P-->>C: checkoutUrl
Provider->>P: POST /v1/webhooks/provider (signed)
P->>P: verify signature + idempotency
P->>DB: update payment=SUCCESS
P->>K: publish PAYMENT_SUCCESS (bookingId)
K->>N: consume PAYMENT_SUCCESS
N->>M: log notification attempt
N-->>Provider: send email/ZNS
3.3 Notification Flow (Event-driven)
flowchart TD
Event[Kafka Event] --> N[notification-service]
N -->|Send| Email[Email Provider]
N -->|Send| ZNS[Zalo ZNS Provider]
N -->|Write| MG[(MongoDB notification_logs)]
N -->|Retry| Queue[Background Jobs/BullMQ (optional)]
4) Critical Design Decisions & Justifications
4.1 Database-per-service
Why
- Clear ownership boundaries and independent scaling.
- Avoids tight coupling through shared schema changes.
Trade-offs
- Cross-entity constraints cannot be enforced with native DB foreign keys across DBs.
- Requires careful consistency strategy (events, validation calls, eventual consistency).
4.2 Kafka for Async Communication
Why
- Decouple payment and notification from booking hot path.
- Enable future services (analytics, fraud detection) with minimal coupling.
Trade-offs
- Requires operational maturity (topics, partitioning, lag monitoring).
- Exactly-once delivery is hard; must design for at-least-once and idempotency.
4.3 Redis for Locks & Cache
Why
- Booking conflict is a concurrency hotspot; Redis locks reduce race conditions.
- Cache reduces read load for popular fields and searches.
Trade-offs
- Redis outages degrade performance; must have fallback and resilience.
- Locks must be implemented carefully (value-based unlock, TTL).
4.4 Keycloak for Authn/Authz
Why
- Standard OIDC, centralized identity management, easy integration for web/mobile.
Trade-offs
- Additional infra and operational overhead.
- Token claim mapping and gateway enforcement must be consistent.
5) Design Patterns Used
5.1 Saga (Distributed Transaction) - Recommended
Used for booking + payment + notifications where no single DB transaction spans services.
- Booking created (PENDING) → payment created → webhook confirms payment → booking confirmed (event) → notify user.
Implementation options
- Choreography (Kafka events) for MVP.
- Orchestration (dedicated saga orchestrator) for complex flows (Phase 2).
5.2 Outbox Pattern (Recommended)
Prevent missing events when DB commit succeeds but Kafka publish fails.
- Write event to
booking_outbox/payment_outboxin same DB transaction. - Background publisher reads outbox and publishes to Kafka.
5.3 Circuit Breaker + Retry + Timeout
Required for:
- Payment providers
- Email/ZNS providers
- Cross-service calls
5.4 CQRS (Optional, Phase 2)
For high-scale search and reporting:
- Command side: OLTP Postgres
- Query side: denormalized read models (Redis/Elastic/Postgres read replica)
6) Scalability Considerations
- Hot paths:
- Field search
- Availability queries
- Booking creation
- Payment webhooks
- Techniques:
- Cache field details and search results (short TTL)
- Precompute availability windows (optional)
- Partition Kafka topics by bookingId/fieldId
- Use indexes for time-range queries in bookings
- Limit synchronous fan-out; prefer events
7) Failure Modes & Mitigations
| Failure | Impact | Mitigation |
|---|---|---|
| Redis down | slower booking conflict checks | fallback to DB checks + reduced throughput, alerts |
| Kafka down | events not delivered | outbox + retry publisher, degrade notifications |
| Payment provider down | cannot pay | show failure + retry, allow PENDING booking expiry |
| Notification provider down | no notifications | queue retries + fallback channel |
| Keycloak down | auth failures | short-term token caching, degrade to 503 for protected endpoints |
8) Deployment Topology (Production)
flowchart LR
Ingress[Ingress/Load Balancer] --> GW[api-gateway]
GW --> U[user-service]
GW --> F[field-service]
GW --> B[booking-service]
GW --> P[payment-service]
GW --> N[notification-service]
U --> PGU[(Postgres User)]
F --> PGF[(Postgres Field)]
B --> PGB[(Postgres Booking)]
P --> PGP[(Postgres Payment)]
N --> MG[(MongoDB)]
GW --> KC[Keycloak]
subgraph Shared
R[Redis]
K[Kafka]
SZ[Signoz]
end
U --> R
F --> R
B --> R
P --> R
U --> K
F --> K
B --> K
P --> K
N --> K
GW --> SZ
U --> SZ
F --> SZ
B --> SZ
P --> SZ
N --> SZ
Document Version: 1.0 Last Updated: 2026-01-13 Owner: Tech Lead