A production-grade, event-driven flight booking platform built with a microservices architecture. Designed to demonstrate real-world backend engineering patterns: outbox-guaranteed messaging, distributed observability, and cloud-native deployment on AWS.
Deployed on AWS us-east-1 with all 7 services (including frontend) running as Fargate containers behind Application Load Balancers.
Jetly is a pnpm monorepo containing 6 NestJS microservices, a Next.js frontend, and a shared package layer. Services communicate via REST (client-facing), gRPC (synchronous inter-service), and RabbitMQ (async events).
apps/
api-gateway/ — REST + WebSocket entry point (client-facing)
frontend/ — Next.js 14 (App Router)
services/
auth/ — JWT authentication, user management
booking/ — Booking lifecycle, outbox pattern
flight/ — Flight data, seat inventory, price simulation
payment/ — Stripe integration, outbox pattern
notification/ — Email delivery via event consumers
packages/
shared-types/ — DTOs, enums, interfaces
event-bus/ — RabbitMQ client abstraction
shared-utils/ — Helpers, validators
| Path | Protocol | Why |
|---|---|---|
| Client → API Gateway | REST + WebSocket | External interface; WS for real-time flight price updates |
| Booking → Flight | gRPC | Synchronous seat availability check during booking |
| Services → Services | RabbitMQ (topic exchange) | Async, decoupled, at-least-once delivery |
Async communication uses RabbitMQ topic exchanges with durable queues. Each consumer queue gets its own DLQ ({queue}.dlq) with a max of 3 retries before dead-lettering.
Booking and Payment services write domain changes and outbox events in the same database transaction, eliminating the dual-write problem. A 5-second poller publishes pending events to the exchange and marks them PROCESSED — guaranteeing at-least-once delivery even through service restarts or broker downtime.
Service TX ──► DB: [Domain Record + Outbox Event] (atomic)
│
Poller (5s)
│
RabbitMQ Exchange ──► Consumer Queues
| Exchange | Events | Consumers |
|---|---|---|
booking.events |
created, confirmed, cancelled |
Notification, Flight (cancelled), Payment |
payment.events |
completed, failed, refunded |
Booking, Notification |
auth.events |
user.registered, verified, reset.password |
Notification |
flight.events |
price.updated, availability.changed |
API Gateway → WebSocket broadcast |
Notification Service is the top consumer with 7 dedicated queues.
| Layer | Service | Purpose |
|---|---|---|
| DNS / CDN | Cloudflare + AWS ACM | TLS termination, DDoS protection |
| Compute | ECS Fargate + Service Connect | Serverless containers, service mesh |
| Container Registry | ECR (7 repos) | Docker image storage |
| Database | RDS PostgreSQL (6 instances) | One isolated DB per service |
| Cache | ElastiCache Redis | API Gateway session cache |
| Messaging | Amazon MQ (RabbitMQ) | Managed message broker |
| Secrets | Secrets Manager (17 secrets) | Per-service credentials |
| CI/CD | GitHub Actions | Build, push to ECR, deploy to ECS |
Full LGTM stack running on a dedicated instance:
- OpenTelemetry Collector — ingests traces and metrics from all services
- Tempo — distributed tracing backend
- Prometheus — metrics scraping and storage
- Loki — log aggregation (structured JSON logs from all containers)
- Grafana — unified dashboards, alerting
All services emit traces via OTLP and write structured logs forwarded by CloudWatch Logs → Loki.
Backend
- NestJS 10 · TypeScript
- Prisma ORM (one schema per service)
- RabbitMQ (topic exchanges, outbox pattern)
- gRPC (Booking ↔ Flight)
- Redis (caching)
- Stripe (payments)
- JWT (access + refresh tokens)
Frontend
- Next.js 14 (App Router)
- TanStack Query · Zustand
- Socket.io-client (real-time updates)
- Tailwind CSS · shadcn/ui
Infrastructure
- AWS ECS Fargate, RDS, ElastiCache, Amazon MQ, ECR, Secrets Manager
- Cloudflare DNS/CDN
- GitHub Actions (CI/CD)
- Docker · pnpm workspaces (monorepo)
- OpenTelemetry · Grafana LGTM stack


