System overview¶
Databús is structured around six functional layers. Each layer has a single authoritative owner; the boundaries are enforced by service mandates (see services.md) rather than code gates.
The six layers¶
flowchart LR
A["Ingestion\nREST API · MQTT"] --> B["Processing\nCelery realtime-engine"]
B --> C["State\nRedis"]
C --> D["Projection\nCelery schedule-engine"]
D --> E["Persistence\nPostgreSQL"]
E --> F["Learning\nPrefect"]
Ingestion¶
External signals enter the platform at two surfaces:
- REST API — the
apiDjango app accepts operator commands (create-run, confirm, complete, interrupt, short-turn). Authenticated via DRF token auth. - MQTT telemetry — vehicles publish GPS and occupancy to the
telemetry-broker(NanoMQ) on topics of the formtransit/vehicle/<id>/{position,occupancy}. Therealtime-engineCelery worker picks these up via its embedded MQTT bootstep.
Processing¶
The realtime-engine worker converts raw telemetry into domain state:
- Parses and validates each MQTT message.
- Writes the telemetry leaf to Redis (
vehicle:<id>:positionor:occupancy). - Enqueues
process_position_updateas a Celery task, which runs server-side map-matching and detection off the paho network thread. - Fires lifecycle events (
run_tracking_started,run_started,run_completed, …) viarun_lifecycle_eventtasks.
State¶
Redis (state service) holds the authoritative real-time picture of every active run and vehicle. It is the only coordination point between the processing and projection layers. The realtime-engine is the sole writer of run/vehicle state; the schedule-engine reads it as a snapshot. See state-and-persistence.md and ../data-model/redis-keys.md for the full key reference.
Projection¶
Every 15 seconds the scheduler fires build_vehicle_positions and build_trip_updates on the schedule-engine worker. That worker reads the Redis snapshot, converts it to protobuf and JSON, and writes GTFS-RT files to backend/feed/files/. Alerts are rebuilt every 10 seconds (currently a stub returning an empty feed). See ../data-flow/gtfs-rt-publishing.md.
Persistence¶
PostgreSQL (database service, PostGIS extension) stores:
- Authoritative domain records (runs, vehicles, operators, GTFS schedule).
- Run lifecycle state transitions.
- GTFS-RT blobs retained approximately one year.
The orchestrator (Django HTTP server) is the sole ORM writer for domain records. The realtime-engine writes operational traces.
Learning¶
analytics-engine (Prefect) consumes batch data from PostgreSQL for offline analysis and model training. It is operationally independent of the real-time path.
Mapping layers to services¶
| Layer | Compose service | Technology |
|---|---|---|
| Ingestion (HTTP) | orchestrator |
Django + Daphne |
| Ingestion (MQTT) | realtime-engine bootstep |
paho-mqtt inside Celery |
| Processing | realtime-engine |
Celery worker, queue realtime_engine |
| State | state |
Redis 7 |
| Projection | schedule-engine |
Celery worker, queue schedule_engine |
| Timing | scheduler |
Celery Beat |
| Persistence | database |
PostgreSQL 15 + PostGIS |
| Learning | analytics-engine |
Prefect 3 |
| Broker | message-broker |
RabbitMQ 4 |
| Telemetry broker | telemetry-broker |
NanoMQ 0.24.9 |
What AGENTS.md describes vs what exists
AGENTS.md and MODEL.md describe a publisher/ project separate from schedule_engine. That project does not exist. GTFS-RT is produced by the schedule_engine Django app running inside the schedule-engine Celery worker. When in doubt, the compose files and backend/*/apps.py are authoritative.