Real-time Public Transit Tracking Platform
RouteForge is a Java + Spring Boot distributed system for real time public transit tracking. It ingests live vehicle position data in the GTFS‑Realtime format, processes that stream asynchronously through Apache Kafka, and keeps two sources of truth: a Redis cache for the most recent positions and a PostgreSQL database for historical records. An API gateway exposes that data through secure REST endpoints and Server‑Sent Events streams, so clients can fetch the current location of a single bus, the list of vehicles on a route, or subscribe to continuous updates without polling.
The system is composed of three Spring‑Boot microservices. The ingestion service polls external GTFS feeds every few seconds, converts the protobuf messages into JSON events, and publishes them to Kafka with circuit breakers and retries to handle feed outages. The processing service consumes those events in batches, updates the Redis cache and writes to PostgreSQL, publishes notifications through Redis and routes unprocessable messages to a dead‑letter queue. The API gateway serves user requests, prioritising the cache and falling back to the database when necessary, enforces rate limits, and can be secured with OAuth2/JWT via Keycloak. A Prometheus–Grafana stack collects metrics such as events processed, cache updates and API latency, and structured logs include trace identifiers for easier debugging.
GTFS-RT Feed → Ingestion Service → Kafka → Processing Service → Redis (hot) + PostgreSQL (history) → API Gateway → Clients
- routeforge-common - Shared DTOs, utilities, and event schemas
- ingestion-service - GTFS-Realtime feed parser and Kafka producer
- processing-service - Kafka consumer with dual-write to Redis and PostgreSQL
- api-gateway-service - REST + WebSocket APIs with OAuth2/JWT security
- Apache Kafka (KRaft mode) - Event streaming
- Redis - Hot state cache for low-latency reads
- PostgreSQL - Historical data and analytics
- Prometheus + Grafana - Metrics and dashboards
- Keycloak - OAuth2/JWT authentication
- Java 21
- Docker & Docker Compose
- 8GB RAM minimum
# Copy the environment configuration template
cp .env.example .env
# Edit .env if needed (defaults work for local development)
# The .env.example file includes all required variables with sensible defaults📝 Note:
.env.exampleincludes all environment variables with defaults for local development. See Configuration below for details.
docker compose up -dWait for all services to be healthy (~30 seconds):
docker compose ps./gradlew build# Terminal 1: Ingestion Service
./gradlew :ingestion-service:bootRun
# Terminal 2: Processing Service
./gradlew :processing-service:bootRun
# Terminal 3: API Gateway
./gradlew :api-gateway-service:bootRun# Check API health
curl http://localhost:8082/api/health
# Get vehicles for a route
curl http://localhost:8082/api/routes/1/vehicles
# View Swagger UI
open http://localhost:8082/swagger-ui.html
# View Grafana
open http://localhost:3000 # admin/admin123- API Gateway: http://localhost:8082/actuator
- Ingestion: http://localhost:8083/actuator
- Processing: http://localhost:8084/actuator
routeforge_ingestion_events_published- Events to Kafkarouteforge_processing_events_processed- Events processedrouteforge_processing_cache_updates- Redis updatesrouteforge_processing_db_inserts- DB inserts
Interactive docs: http://localhost:8082/swagger-ui.html
Core Endpoints:
GET /api/vehicles/routes/{routeId}- Get vehicles on routeGET /api/vehicles/{vehicleId}- Get vehicle positionGET /api/stream/routes/{routeId}- SSE stream for real-time updatesGET /actuator/health- Health check
Admin Endpoints (JWT Required):
DELETE /api/admin/cache/all- Clear all cacheDELETE /api/admin/cache/routes/{routeId}- Clear route cacheGET /api/admin/dlq/metrics- Get DLQ statisticsPOST /api/admin/ingestion/replay- Replay failed messagesGET /api/admin/stats- System statistics
See API.md for complete documentation.
./gradlew test # Unit tests
./gradlew integrationTest # Integration tests with Testcontainersrouteforge/
├── routeforge-common/ # Shared DTOs
├── ingestion-service/ # GTFS-RT ingestion
├── processing-service/ # Event processing
├── api-gateway-service/ # REST APIs
├── infra/ # Infrastructure configs
│ ├── prometheus/
│ ├── grafana/
│ └── terraform/ # AWS deployment
└── docker-compose.yml
Development (default): All endpoints accessible without auth
Production: Enable OAuth2/JWT:
- Set
spring.profiles.active=prod - Configure Keycloak (see KEYCLOAK.md)
- Public endpoints have rate limiting (100 req/min)
cd infra/terraform
terraform applyKey environment variables in .env:
GTFS_RT_FEED_URL=https://api-endpoint.mta.info/...
KAFKA_BOOTSTRAP_SERVERS=localhost:9092
REDIS_HOST=localhost
POSTGRES_JDBC_URL=jdbc:postgresql://localhost:5432/routeforgeServices won't start:
docker compose ps
docker compose logs kafka redis postgres
docker compose restartCheck Kafka topics:
docker exec routeforge-kafka kafka-topics --bootstrap-server localhost:9092 --listCheck Redis cache:
docker exec routeforge-redis redis-cli KEYS "*"Query database:
docker exec -it routeforge-postgres psql -U routeforge -d routeforge
\dt
SELECT * FROM vehicle_positions_history LIMIT 10;- DESIGN.md - System architecture
- API.md - API specifications
- RUNBOOK.md - Operations guide
- CONTRIBUTING.md - Contribution guidelines
Apache License 2.0 - see LICENSE
Contributions welcome! See CONTRIBUTING.md