Real-time backend system for ingesting air-quality sensor data, storing geospatial records, computing streaming analytics, and pushing live updates to clients.
- Designed event-driven architecture with Kafka topics for raw, average, and alert streams.
- Implemented geospatial persistence (
geometry(Point,4326)) with PostgreSQL/PostGIS. - Added windowed stream aggregation (Kafka Streams tumbling windows).
- Standardized a single JSON event contract across producer, stream processor, and listeners.
- Added Flyway migrations and profile-based config (
dev/prod) for reproducibility. - Added backend unit tests for core services and listener behavior.
- Java 25
- Spring Boot 3.5
- Spring Kafka + Kafka Streams
- Spring Data JPA + Hibernate Spatial
- PostgreSQL + PostGIS
- Flyway
- JUnit 5 + Mockito
DataSendingService: generates sample events and publishes to Kafka sample topic.DataStoringService: stores each event intosensor_data.PublishAverageToKafkaStreamService: computes windowed averages + emits alerts.KafkaMessageListener: consumes Kafka outputs and pushes WebSocket updates.DataRetentionScheduler: archives and prunes old records.
flowchart LR
A[SampleDataProvider] --> B[DataSendingService]
B -->|JSON event| C[(Kafka: sample topic)]
B --> D[(PostGIS: sensor_data)]
C --> E[Kafka Streams Processor]
E -->|avg per sensor| F[(Kafka: avg topic)]
E -->|threshold breach| G[(Kafka: alert topic)]
C --> H[KafkaMessageListener]
F --> H
G --> H
H --> I[/topic/sensor-data]
H --> J[/topic/sensor-data-avg]
H --> K[/topic/sensor-data-alert]
D --> L[DataRetentionScheduler]
L --> M[(sensor_data_archive)]
SampleDataProviderselects a sensor payload from sample JSON.DataSendingServiceserializes it asSensorReadingEventand publishes it toapp.kafka.sample-topic.DataStoringServicestores the same event in PostGIS for historical querying.PublishAverageToKafkaStreamServicereads sample-topic messages, extractsvalue, and:- computes tumbling-window averages by
sensorId->app.kafka.avg-topic - emits alert events when reading exceeds
app.kafka.alert-threshold->app.kafka.alert-topic
- computes tumbling-window averages by
KafkaMessageListenerconsumes sample/avg/alert topics and forwards live updates over STOMP WebSocket.DataRetentionSchedulerperiodically archives old rows and deletes expired operational/archive records.
{
"sensorId": "air-co-001",
"harmfulSubstance": "CO",
"value": 0.75,
"unit": "ppm",
"eventTimestamp": "2025-08-23T23:00:00",
"latitude": 12.9716,
"longitude": 77.5946
}- Kafka topics:
app.kafka.sample-topic(raw readings)app.kafka.avg-topic(windowed averages)app.kafka.alert-topic(threshold breaches)
- WebSocket topics:
/topic/sensor-data/topic/sensor-data-avg/topic/sensor-data-alert
- Managed via Flyway migration:
src/main/resources/db/migration/V1__init_schema.sql
- Core tables:
sensor_datasensor_data_archivesensor_statistics
Use .env.example as the template for runtime variables.
Common variables:
APP_PROFILE,APP_PORTKAFKA_BOOTSTRAP_SERVERSKAFKA_SAMPLE_TOPIC,KAFKA_AVG_TOPIC,KAFKA_ALERT_TOPICDB_URL,DB_USERNAME,DB_PASSWORDRETENTION_SENSOR_DAYS,RETENTION_ARCHIVE_DAYS
- Start infra:
docker compose up -d
- Run backend:
./mvnw spring-boot:run
- Run tests:
./mvnw test