A Python FastAPI backend for an IRCTC-style train ticketing system with event-driven flows, search, and background workers.
This repository includes:
app/β FastAPI application, domain logic, API routes, middleware, services, and workersDockerfileβ backend container build definitiondocker-compose.ymlβ full local infrastructure stack: MySQL, Redis, Kafka, Elasticsearch, Kibana, phpMyAdmin, backend, and workersstart.shβ startup wrapper for dependency checks, Alembic migration, and Uvicorn launchrequirements.txtβ Python dependencies.envβ environment configuration for local development and Docker Compose
- Overview
- Architecture
- Port Reference
- Tech Stack
- Getting Started
- Services
- Kafka Topics
- API Documentation
- Environment Variables
- Project Structure
- Notes
This project implements a backend for an IRCTC-like booking system using a single FastAPI application plus background workers and asynchronous integration with Kafka, Redis, MySQL, and Elasticsearch.
The repository architecture is designed to demonstrate:
- REST API with FastAPI
- asynchronous event-driven processing using Kafka topics
- database migrations with Alembic
- caching and rate-limiting support via Redis
- search and discovery using Elasticsearch
- containerized local development with Docker Compose
The backend serves API requests and also relies on asynchronous worker processes for outbox dispatch and Kafka-based integration.
Key architectural components:
- FastAPI backend β main HTTP API exposed on
/api/v1 - Database layer β MySQL for application persistence
- Redis β fast read cache, rate limiting, frequently accessed data, and distributed seat lock management for booking operations
- Kafka β event streaming and topic-driven workers for asynchronous decoupling
- Elasticsearch β search index for station/route discovery
- Outbox workers β asynchronous dispatch of domain events to Kafka
- Saga-style booking coordination β booking workflows coordinate inventory locks, payment orders, and eventual confirmation or rollback
The app combines synchronous request handling with asynchronous event flows to support features such as OTP email handling, booking inventory coordination, distributed locks, and search indexing.
This project uses the Outbox pattern to keep Kafka event publishing reliable and decoupled from HTTP request handling.
- Writes domain events to the database as part of the same transaction as business changes.
- Prevents lost events when Kafka is temporarily unavailable.
- Moves Kafka publish logic out of user-facing request handlers.
- Supports retries and eventual consistency.
- Domain code creates an event record in the
outbox_eventstable. - An outbox worker reads pending rows and marks them processing.
- The worker publishes event payloads to Kafka topics.
- The worker marks each event published, or retries/fails if publishing fails.
- A dispatcher process consumes the Kafka topic and applies side effects.
| Kafka topic | Outbox worker | Dispatcher / consumer | Container name | Consumer group |
|---|---|---|---|---|
pwdchanged-otp |
pwdchanged-otp-outbox-worker |
pwdchanged-otp-outbox-dispatcher |
irtc-pwdchanged-otp-outbox-dispatcher |
pwdchanged-otp-consumer-group |
emailverification-otp |
emailverification-otp-outbox-worker |
emailverification-otp-outbox-dispatcher |
irtc-emailverification-otp-outbox-dispatcher |
emailverification-otp-consumer-group |
emailchanged-otp |
emailchanged-otp-outbox-worker |
emailchanged-otp-outbox-dispatcher |
irtc-emailchanged-otp-outbox-dispatcher |
emailchanged-otp-consumer-group |
station |
stations-outbox-worker |
stations-outbox-dispatcher |
irtc-stations-outbox-dispatcher |
station-consumer-group |
route |
routes-outbox-worker |
routes-outbox-dispatcher |
irtc-routes-outbox-dispatcher |
route-consumer-group |
schedule |
schedules-outbox-worker |
schedules-outbox-dispatcher |
irtc-schedules-outbox-dispatcher |
schedule-consumer-group |
schedule-inventory-seat-availability-updated |
schedule-inventory-seat-availability-updated-outbox-worker |
schedule-inventory-seat-availability-updated-outbox-dispatcher |
irtc-schedule-inventory-seat-availability-updated-outbox-dispatcher |
schedule-inventory-seat-availability-updated-consumer-group |
payment-updated-status |
payment-updated-status-outbox-worker |
payment-updated-status-outbox-dispatcher |
irtc-payment-updated-status-outbox-dispatcher |
payment-updated-status-consumer-group |
booking-updated-status |
booking-updated-status-outbox-worker |
booking-updated-status-send-email-outbox-dispatcher |
irtc-booking-updated-status-send-email-outbox-dispatcher |
booking-updated-status-email-consumer-group |
The project follows a layered domain architecture:
app/api/v1/β HTTP adapters and route definitions.app/domains/β business logic and consumer services.app/infrastructure/β DB, Kafka, Elasticsearch, Redis, email, and outbox plumbing.app/workers/β background workers that publish to Kafka or consume Kafka topics.app/core/,app/common/, andapp/dependencies/β shared configuration, middleware, and FastAPI integration.
This structure keeps domain logic separated from infrastructure code and makes the event-driven flows clear and maintainable.
Booking in this project is implemented as a Saga-style workflow rather than a single monolithic transaction.
- A booking request starts in the backend and writes a local booking state to MySQL.
- Redis is used for fast access data, rate limiting, and to coordinate distributed seat locks during booking.
- The booking service creates outbox events for inventory lock, payment order, and booking status updates.
- Kafka topics carry each step as an event, so the system can resume and retry independently.
- A successful payment result triggers a confirm flow that locks the seat permanently and marks the booking confirmed.
- A failure or timeout triggers compensating actions, such as unlocking seat reservations and canceling the booking.
This Saga-style pattern helps the application maintain consistency across services without requiring distributed transactions.
| Component | Local URL | Container port | Notes |
|---|---|---|---|
| Backend API | http://localhost:8000 | 8000 | FastAPI app /docs available |
| phpMyAdmin | http://localhost:8080 | 80 | MySQL UI |
| MySQL | localhost:3307 | 3306 | Database for app |
| Redis | localhost:6380 | 6379 | Cache and rate limiting |
| Kafka | localhost:9092 | 9092 | Event broker |
| Elasticsearch | http://localhost:9200 | 9200 | Search index |
| Kibana | http://localhost:5601 | 5601 | Elasticsearch UI |
- Python 3.12
- FastAPI
- Uvicorn
- SQLAlchemy + asyncmy
- Alembic migrations
- Pydantic / Pydantic Settings
- SendGrid mail integration
- Docker Compose
- MySQL 8.4
- Redis 7.2
- Apache Kafka 3.9.0 (KRaft mode)
- Elasticsearch 8.15.1
- Kibana 8.15.1
- phpMyAdmin
- Kafka topics for event-driven asynchronous workflows
- Elasticsearch for station/route search and discovery
- Docker
- Docker Compose
- Python 3.12+ (optional for local non-container execution)
- Git
Clone the repository:
git clone https://github.com/your-user/PythonIRTCSystemBackend.git
cd PythonIRTCSystemBackendIf you just pulled the code and want to launch the full stack immediately, run:
docker compose down -v && docker compose build --no-cache && docker compose up -d --force-recreateInstall Python dependencies locally (optional):
python -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install -r requirements.txtStart the full stack:
docker compose up -dVerify services:
docker compose psView logs:
docker compose logs -fStop and remove containers:
docker compose down -vThe backend can be run directly if dependencies are installed, but it still requires the supporting services from Docker Compose.
source venv/bin/activate
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000To run Elasticsearch and Kibana alone:
docker compose -f docker-compose.elastic.yml up -d- Builds from
Dockerfile - Serves FastAPI app on port
8000 - Uses
start.shto wait for MySQL, Redis, Kafka, Elasticsearch, then run Alembic and start Uvicorn
- MySQL 8.4 database
- Exposes port
3307locally - Uses
.envvalues for credentials
- Web UI for MySQL
- Access at
http://localhost:8080
- Redis cache exposed on
6380locally
- Kafka broker on
9092
- Creates required Kafka topics automatically
- Elasticsearch single-node cluster on
9200
- Kibana UI on
http://localhost:5601
pwdchanged-otp-outbox-workerpwdchanged-otp-outbox-dispatcheremailchanged-otp-outbox-workeremailchanged-otp-outbox-dispatcheremailverification-otp-outbox-workeremailverification-otp-outbox-dispatcherstations-outbox-workerstations-outbox-dispatcherroutes-outbox-workerroutes-outbox-dispatcherschedules-outbox-workerschedules-outbox-dispatcher- and other workers defined in
docker-compose.yml
The compose stack creates these Kafka topics:
pwdchanged-otpemailverification-otpemailchanged-otpstationroutescheduleschedule-inventory-seat-availability-updatedpayment-updated-statusbooking-updated-status
The backend exposes the FastAPI docs at:
http://localhost:8000/docshttp://localhost:8000/openapi.json
GET /healthβ service healthGET /routes_es_client_readyβ Elasticsearch services health
All API routes are mounted under /api/v1.
POST /api/v1/auth/...β authentication, OTP, login, refreshGET/POST /api/v1/users/...β user operationsGET/POST /api/v1/admin/master-data/...β admin/master-data CRUDGET /api/v1/search_discovery/...β search and discoveryGET/POST /api/v1/inventory/...β inventory endpointsGET/POST /api/v1/bookings/...β booking operationsGET/POST /api/v1/payments/...β payments and refunds
Send OTP:
POST http://localhost:8000/api/v1/auth/send-otp
Content-Type: application/json
{
"email": "user@example.com"
}User signup example:
POST http://127.0.0.1:8000/api/v1/users/signup
Content-Type: application/json
{
"first_name": "Chirag",
"last_name": "Jain",
"mobile": "9975967186",
"email": "cjain9975@gmail.com",
"gender": "Male",
"password": "Test1@123456",
"confirm_password": "Test1@123456",
"profile": "User/Admin"
}Verify OTP:
POST http://localhost:8000/api/v1/auth/verify-otp
Content-Type: application/json
{
"email": "user@example.com",
"otp": "123456"
}Create booking:
POST http://localhost:8000/api/v1/bookings
Content-Type: application/json
Authorization: Bearer <access_token>Configuration is loaded from .env using Pydantic Settings.
Important variables:
APP_NAME,APP_ENV,APP_DEBUGJWT_SECRET_KEY,TOKEN_HASH_SECRET,JWT_ALGORITHMMYSQL_DB_HOST,MYSQL_DB_PORT,MYSQL_DB_NAME,MYSQL_DB_USER,MYSQL_DB_PASSWORD,MYSQL_ROOT_PASSWORDREDIS_HOST,REDIS_PORT,REDIS_DBKAFKA_BOOTSTRAP_SERVERS,KAFKA_CLIENT_IDELASTICSEARCH_URL,ELASTICSEARCH_USERNAME,ELASTICSEARCH_PASSWORD,ELASTICSEARCH_VERIFY_CERTSSENDGRID_API_KEY,SENDGRID_DRY_RUNINVENTORY_SERVICE_BASE_URL,PAYMENT_SERVICE_BASE_URL
Do not commit secrets or the
.envfile to version control.
The repository uses a layered domain architecture that separates API adapters, domain logic, infrastructure adapters, and background workers.
PythonIRTCSystemBackend/
βββ .env
βββ Dockerfile
βββ docker-compose.yml
βββ docker-compose.elastic.yml
βββ README.md
βββ requirements.txt
βββ start.sh
βββ app/
β βββ api/
β β βββ v1/ # API routers by domain
β βββ common/ # shared helpers, security, cache
β βββ core/ # application settings, exception handlers, response wrapper
β βββ dependencies/ # FastAPI dependencies
β βββ domains/ # business logic and service classes
β βββ infrastructure/ # database, kafka, email, redis, outbox integrations
β βββ middlewares/ # middleware definitions
β βββ services/ # saga and background service orchestration
β βββ workers/ # background worker entrypoints
βββ alembic/ # migration configuration and versions
βββ project_details.txt # notes and helper commands
Dockerfileinstalls dependencies, copies source code, and runsstart.sh.start.shwaits for MySQL, Redis, Kafka, and Elasticsearch, then runs Alembic migrations before launching Uvicorn.- The API is primarily served at
http://localhost:8000and the docs are available athttp://localhost:8000/docs. docker-compose.elastic.ymlis available for Elasticsearch + Kibana only.