A production-grade, distributed emergency coordination system built with microservice architecture, enabling real-time dispatch, resource management, and cross-region coordination for public safety operations.
Emergency coordination across large geographic regions faces critical challenges that existing monolithic systems simply cannot solve:
| Challenge | Impact |
|---|---|
| Fragmented Communication | Dispatchers, first responders, hospitals, and control centres operate on siloed systems with no unified real-time view |
| Resource Contention | Shared, limited resources (ambulances, ICU beds, hazmat units) are double-dispatched when multiple operators act simultaneously |
| Single Points of Failure | Monolithic systems go down entirely. During an emergency, this is catastrophic |
| No Audit Integrity | Conventional logging provides no tamper-evident guarantee for post-incident legal review |
| Scalability Bottlenecks | Peak disaster events (multi-vehicle accidents, natural disasters) overwhelm systems not designed for elastic scale |
| Geographic Data Locality | Incidents in different regions share a single database, creating cross-region latency and contention |
In emergency services, system downtime isn't a business inconvenience. It costs lives.
Microservice-Based Emergency Platform is a distributed, event-driven emergency coordination system purpose-built for public safety. It replaces monolithic emergency dispatch software with a resilient microservice architecture that:
- Eliminates double-dispatch via Redis distributed locks with atomic Lua-script release
- Survives partial outages through a custom circuit breaker with domain-specific fallbacks
- Scales per-service: the Incident Service can scale independently of Auth or Audit
- Guarantees audit integrity via SHA-256 hash chains and Merkle tree tamper-evident sealing
- Routes data geographically through application-level emirate-based database sharding
- Delivers real-time updates via WebSocket-powered dashboards and Kafka event streaming
- Dispatches intelligently using a haversine-geometry SmartDispatchEngine with multi-criteria scoring
- Enforces zero-trust security with JWT + MFA + RBAC + rate limiting + IP blacklisting
The platform follows a microservice architecture with the database-per-service pattern. Each service owns its data, communicates synchronously via REST for time-sensitive queries, and publishes events asynchronously through Apache Kafka. All external traffic is routed through Kong API Gateway.
The security gateway for the entire platform. Handles user registration with five role types (DISPATCHER, RESPONDER, HOSPITAL_ADMIN, OPERATOR, ADMIN), JWT authentication (HS256, 1-hour TTL), and email OTP multi-factor authentication mandatory for dispatcher and admin roles.
- Redis-backed sessions with 30-min sliding TTL for cross-instance stateless validation
- IP blacklist/whitelist enforcement and dual-layer rate limiting (Kong + bucket4j)
Manages the complete incident lifecycle (OPEN → IN_PROGRESS → RESOLVED / CANCELLED) with strict state-machine enforcement. Supports five incident types (FIRE, MEDICAL, POLICE, ACCIDENT, HAZMAT) across four severity levels.
- Geo-sharded across 3 MySQL databases by emirate hash for write distribution
- Redis-cached active incidents (5-min TTL) and dashboard summaries (1-min TTL) with write-through eviction
- Publishes
incident.createdandincident.updatedKafka events
Tracks all emergency assets including units (ambulances, fire trucks, police), hospitals with bed capacity management, and medical equipment inventory with reservation and release operations.
- Redis distributed locks prevent over-reservation of ICU beds and medical resources
- Available-unit queries cached in Redis with write-through eviction
- Fast availability count endpoints for dispatch dashboard integration
The mission-critical coordination engine. Supports both manual assignment and automated SmartDispatchEngine dispatch with proximity, severity, and SLA-based composite scoring. Dispatchers can preview ranked recommendations before committing.
- Dual Redis distributed locks (unit + incident) acquired in fixed order to prevent deadlock
- Custom hand-rolled circuit breaker protects all Resource Service calls with domain-specific fallbacks
- Assignment lifecycle:
ASSIGNED→COMPLETED/CANCELLED
Provides tamper-evident, legally admissible audit logging. Consumes events from all 5 Kafka topics into a unified log where each entry stores a SHA-256 content hash. A Merkle tree can be committed over all entries to cryptographically seal the log state.
- Full hash-chain verification detects any tampered or deleted record and identifies its position
- STOMP-over-WebSocket broadcasts every audit event to connected dashboard clients in real-time
Netflix Eureka service registry enabling dynamic service discovery and health monitoring across all microservices.
| Component | Port | Technology | Purpose |
|---|---|---|---|
| Kong API Gateway | 8000 |
Kong 3.6 | JWT verification, rate limiting (100/min auth, 200/min others), CORS, request routing |
| Apache Kafka | 9092 |
Confluent 7.5 | Asynchronous event bus with 5 topics, 3 partitions each |
| Apache Zookeeper | 2181 |
Confluent 7.5 | Kafka cluster coordination |
| Redis | 6379 |
Redis 7 Alpine | Caching, distributed locks (SETNX + Lua), session storage |
| MySQL | 3307 |
MySQL 8.0 | Persistent storage with separate database per service + 3 incident shards |
| Zipkin | 9411 |
OpenZipkin | Distributed tracing via Micrometer spans |
| Prometheus | 9090 |
Prometheus | Metrics collection from Spring Boot Actuator endpoints |
| Grafana | 3000 |
Grafana | Real-time monitoring dashboards with pre-provisioned panels |
| Kafdrop | 9000 |
Kafdrop | Kafka topic browser and consumer-group monitor |
| Spring Actuator | per-service | Spring Boot | Health probes at /actuator/health, Prometheus metrics at /actuator/prometheus |
Each microservice owns its dedicated database, ensuring loose coupling and independent deployability:
| Service | Database(s) | Strategy |
|---|---|---|
| Auth Service | auth_db |
Single database: users, roles, refresh tokens, IP lists |
| Incident Service | incident_shard_0, incident_shard_1, incident_shard_2 |
Geo-sharded by emirate hash |
| Resource Service | resource_db |
Single database: units, hospitals, medical resources |
| Dispatch Service | dispatch_db |
Single database: assignments table |
| Audit Service | audit_db |
Single database: audit_logs + merkle_roots |
The Incident Service implements application-level database sharding to distribute write load and confine queries to geographically relevant data:
- A
ShardRoutingAspectintercepts data access and selects the target datasource from a deterministic hash of theemiratefield - A
ReadReplicaRouteraspect routes read-only transactions to replica datasources when available - Distributes write load and confines incident queries to the geographically relevant shard
Apache Kafka serves as the asynchronous event bus, decoupling producers from consumers and enabling event sourcing patterns:
| Topic | Partitions | Producer | Consumer(s) |
|---|---|---|---|
incident.created |
3 | Incident Service | Dispatch Service, Audit Service |
incident.updated |
3 | Incident Service | Audit Service |
resource.assigned |
3 | Dispatch Service | Audit Service |
resource.released |
3 | Dispatch Service | Audit Service |
auth.event |
1 | Auth Service | Audit Service |
- Decoupled Services: Producers don't need to know about consumers. Adding a new analytics service just means adding a consumer group.
- Event Replay: Topic retention allows replaying events for debugging, reprocessing, or new service bootstrapping.
- Guaranteed Delivery: At-least-once semantics ensure no audit event is ever lost.
- Backpressure Handling: Consumers process at their own pace without overloading producers.
- Monitoring: Kafdrop (
:9000) provides a visual Kafka topic browser and consumer-group lag monitor.
Synchronous REST calls between microservices introduce failure propagation risk. If the Resource Service becomes unavailable, every pending call from the Dispatch Service blocks a thread until the HTTP timeout expires. Under high concurrent load, this thread exhaustion cascades into a full Dispatch Service outage, even though the failure is isolated to one downstream service.
Rather than adopting Resilience4j, the team implemented a lightweight, hand-rolled CircuitBreaker Spring component using ConcurrentHashMap for per-service state and AtomicInteger/AtomicLong for lock-free counter updates.
Redis serves three distinct roles in the platform, each addressing a different distributed systems challenge:
Purpose: Reduce database read traffic for hot-path queries under concurrent operator load.
| Cache Region | TTL | Eviction Strategy |
|---|---|---|
incidents (by ID) |
5 min | Evicted on status change |
incidents (active list) |
5 min | Evicted on create/update |
incidents-dashboard |
1 min | Evicted on any write |
resources (available units) |
5 min | Evicted on status update |
When NOT to cache: Unit availability inside the lock-protected assignment critical section is read directly from MySQL. A stale
AVAILABLEfrom cache could cause double-dispatch.
Purpose: Prevent double-dispatch of emergency units under concurrent operator requests.
// Atomic lock acquisition
redis.opsForValue().setIfAbsent(lockKey, lockValue, 10, TimeUnit.SECONDS);-- Atomic lock release (Lua script)
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
else
return 0
end| Tool | Port | What It Shows |
|---|---|---|
| Prometheus | :9090 |
JVM metrics, HTTP request rates, cache hit ratios, custom business metrics |
| Grafana | :3000 |
Pre-provisioned dashboards with service health, request latency, error rates |
| Zipkin | :9411 |
End-to-end distributed traces across Kong → Services → Kafka |
| Kafdrop | :9000 |
Kafka topic browser, consumer-group lag, partition distribution |
| Spring Actuator | per-service | /actuator/health liveness/readiness probes |
| Circuit Breaker Logs | — | State transitions logged at INFO, rejections at WARNING |
The platform includes a full-featured React 19 single-page application built with Vite and Tailwind CSS:
| # | Scenario | Expected | Result |
|---|---|---|---|
| TC01 | User registration with valid data | 201 Created | Pass |
| TC02 | Login with MFA trigger for DISPATCHER | 200, mfaRequired: true, OTP sent |
Pass |
| TC03 | MFA OTP verification within TTL | 200, full-access JWT | Pass |
| TC04 | Expired OTP (>10 min) | 401 Unauthorized | Pass |
| TC05 | Create CRITICAL incident in Dubai | 201, incident UUID | Pass |
| TC06 | Missing required field in incident body | 400, field error message | Pass |
| TC07 | Active incidents, cache miss | 200, DB query executed | Pass |
| Test | Result |
|---|---|
| JWT payload tampering | Kong returns 401, signature mismatch |
CORS from http://evil.com |
No Access-Control-Allow-Origin header returned |
| Directory traversal | 400, UUID path-parameter parsing failure |
| Brute-force login (110 attempts) | HTTP 429 on request 101 |
The Gatling load simulation (EmergencyLoadSimulation.scala) models the complete incident lifecycle:
Ramp Profile:
├── 10 users over 30 seconds (warm-up)
├── 5 users/sec sustained for 2 minutes
├── Surge to 20 users/sec over 1 minute
├── 20 users/sec sustained for 2 minutes (peak)
└── Gradual ramp-down
Assertions: p95 response time ≤ 2,000 ms · Successful request rate ≥ 95%
| Tool | Version |
|---|---|
| Java | 21+ |
| Maven | 3.9+ |
| Node.js | 18+ |
| Docker & Docker Compose | Latest |
| Git | Latest |
git clone https://github.com/your-org/microservice-emergency-platform.git
cd microservice-emergency-platformcp .env.example .env
# Edit .env with your configurationdocker compose up -dThis starts: MySQL, Redis, Kafka, Zookeeper, Zipkin, Kong, Prometheus, Grafana, and Kafdrop.
cd shared && mvn clean install -DskipTests && cd ..Start each service via Maven or your IDE in the following order:
# 1. Eureka Server (wait for it to be UP)
cd eureka-server && mvn spring-boot:run
# 2. Auth Service
cd auth-service && mvn spring-boot:run
# 3. Incident Service
cd incident-service && mvn spring-boot:run
# 4. Resource Service
cd resource-service && mvn spring-boot:run
# 5. Dispatch Service
cd dispatch-service && mvn spring-boot:run
# 6. Audit Service
cd audit-service && mvn spring-boot:runcd frontend
npm install
npm run devThe frontend will be available at http://localhost:5173.
| Service | URL |
|---|---|
| Frontend | http://localhost:5173 |
| Kong Gateway | http://localhost:8000 |
| Eureka Dashboard | http://localhost:8761 |
| Grafana | http://localhost:3000 (admin/admin) |
| Prometheus | http://localhost:9090 |
| Zipkin | http://localhost:9411 |
| Kafdrop | http://localhost:9000 |
| Kong Admin | http://localhost:8001 |
| Swagger UI | http://localhost:{service-port}/swagger-ui.html |
Production-grade Kubernetes manifests are provided in the k8s/ directory:
k8s/
├── 00-namespace.yaml # Namespace isolation
├── 01-secrets.yaml # Kubernetes Secrets for credentials
├── 01-tls-secret.yaml # TLS certificate secret
├── 02-configmap.yaml # Application configuration
├── 02-configmap-kafkasvc.yaml # Kafka service configuration
├── 03-zookeeper.yaml # Zookeeper StatefulSet
├── 04-kafka.yaml # Kafka StatefulSet
├── 05-redis.yaml # Redis Deployment
├── 06-mysql.yaml # MySQL StatefulSet
├── 08-observability.yaml # Prometheus + Grafana + Zipkin
├── 09-eureka-server.yaml # Eureka Server Deployment
├── 10-microservices.yaml # All 5 microservice Deployments
├── 11-kong.yaml # Kong API Gateway
└── 12-hpa.yaml # Horizontal Pod Autoscaler rules
kubectl apply -f k8s/ --recursive| Yohannis Adamu ID: 1093892 |
Indalu Taresa ID: 1093915 |
Biniam Negash ID: 1093887 |
Course: CSC408, Distributed Information Systems · Instructor: Prof. Mourad Elhadef · Semester: Spring 2026
Built for public safety
Because when seconds count, your architecture shouldn't be the bottleneck.