How chpr2011/incident-service works
1. This repository is a single Spring Boot service, not a distributed system. The core runtime is Java 21 + Spring Boot 3.3.x, with Spring MVC, Spring Data JPA, Flyway, and H2 for local/test runs or PostgreSQL 16 in the Docker “prod” profile.
2. The architecture is intentionally layered around the com.everbridge.incident package:
- web exposes REST endpoints and DTOs
- application contains orchestration/business use cases
- domain holds the pure incident model and status rules
- infrastructure contains JPA repositories and event listeners
- common provides cross-cutting request/error handling
3. The main HTTP entrypoint is IncidentController. It handles:
- POST /incidents for intake with idempotency
- GET /incidents/{id} for an incident plus history
- GET /incidents for filtered/paginated search
- PATCH /incidents/{id}/status for lifecycle transitions
- GET /incidents/{id}/history for the audit trail
4. Request flow is conventional layered MVC: controller → IncidentService → domain rules → repositories. The controller only maps request/response shapes; it does not own business logic. Mapping between entity and API DTOs is isolated in web/mapper/IncidentMapper using MapStruct.
5. The domain layer is the main business boundary. Incident, IncidentStatus, Severity, IncidentHistory, and StatusTransitionRules model the lifecycle rules and immutable audit semantics. Invalid state changes are rejected in the domain/application flow, not in the controller.
6. Persistence is handled through Spring Data JPA repositories in infrastructure/repository. IncidentRepository supports id lookup and idempotent lookup by externalReferenceId; IncidentSpecifications provides composable filters for list queries. Hibernate optimistic locking via @Version is used to prevent lost updates and surface conflicts as 409s.
7. Audit recording is decoupled from the main write path using domain events. Status changes publish IncidentStatusChangedEvent, and IncidentHistoryListener persists append-only entries into incident_history. This keeps audit history authoritative and chronologically ordered without mixing it into controller code.
8. Cross-cutting request concerns live in common:
- CorrelationIdFilter propagates or creates X-Correlation-Id
- GlobalExceptionHandler converts errors into RFC 7807 ProblemDetail responses
- custom exceptions represent not-found, invalid transition, and related business failures
9. Schema management is migration-based, not entity-auto-generated. Flyway scripts under src/main/resources/db/migration create incidents and incident_history, and the application explicitly disables ddl-auto. That makes the schema an explicit deployment boundary.
10. Observability is first-class and split across logs, metrics, and dashboards:
- structured JSON logging via logback-spring.xml
- Micrometer + Prometheus at /actuator/prometheus
- Docker observability stack with Prometheus, Grafana, Loki, and Promtail
- correlation IDs appear in logs and API error responses for traceability
11. Environment configuration is profile-driven. application.yml, application-dev.yml, and application-docker.yml switch between H2/local settings and Docker/PostgreSQL settings. The repo includes separate compose files for dev and prod, plus provisioning for Grafana datasources and dashboards.
12. The project is test-heavy and mirrors the production layers:
- pure domain tests for transition rules
- service unit tests with Mockito
- repository tests with JPA/H2
- WebMvc slice tests for controller + error handling
- full integration tests with Spring Boot and real migrations
This maps well to the architecture because each boundary is tested in isolation before the full stack.
13. The important diagram boundaries are: external HTTP clients, the web/API layer, the application service layer, the domain model, the persistence/event infrastructure, and the database. Around that core sit cross-cutting concerns for correlation IDs, error translation, logging, metrics, and environment-specific deployment via Docker Compose.
Main entry points: IncidentService.java IncidentService · IncidentController.java IncidentController · IncidentServiceApplication.java Incident App · Incident.java Incident
Components 24
Web API
REST entrypoints, DTOs, and request mapping
- IncidentController · HTTP endpoints for create, read, search, and status updates
src/main/java/com/everbridge/incident/web/IncidentController.java - IncidentMapper · MapStruct mapping between API DTOs and incident models
src/main/java/com/everbridge/incident/web/mapper/IncidentMapper.java - Create DTO · Incident create and detail API shapes
src/main/java/com/everbridge/incident/web/dto/CreateIncidentRequest.java - Status DTO · Status update and history API shapes
src/main/java/com/everbridge/incident/web/dto/UpdateStatusRequest.java - Incident DTOs · List and detail responses for incidents and history
src/main/java/com/everbridge/incident/web/dto/IncidentResponse.java - ProblemDetail · Translates business and request failures into RFC 7807 responses
src/main/java/com/everbridge/incident/common/exception/GlobalExceptionHandler.java - Correlation Filter · Propagates or creates correlation IDs for requests and logs
src/main/java/com/everbridge/incident/common/filter/CorrelationIdFilter.java
Core Logic
Application orchestration and domain rules
- Incident App · Java 21 Spring Boot entrypoint for the incident service
src/main/java/com/everbridge/incident/IncidentServiceApplication.java - IncidentService · Use case orchestration for intake, search, and lifecycle transitions
src/main/java/com/everbridge/incident/application/IncidentService.java - Incident · Core incident model and state transitions
src/main/java/com/everbridge/incident/domain/Incident.java - Transition Rules · Validates allowed status changes
src/main/java/com/everbridge/incident/domain/StatusTransitionRules.java - IncidentStatus · Incident lifecycle states
src/main/java/com/everbridge/incident/domain/IncidentStatus.java - Severity · Incident severity classification
src/main/java/com/everbridge/incident/domain/Severity.java - IncidentHistory · Append-only audit entry for incident changes
src/main/java/com/everbridge/incident/domain/IncidentHistory.java - StatusChangedEvent · Published when incident status changes
src/main/java/com/everbridge/incident/domain/events/IncidentStatusChangedEvent.java
Infrastructure
Persistence and event-driven side effects
- IncidentRepository · Incident persistence with id lookup and idempotent external reference lookup
src/main/java/com/everbridge/incident/infrastructure/repository/IncidentRepository.java - IncidentSpecifications · Composable filters for list and search queries
src/main/java/com/everbridge/incident/infrastructure/repository/IncidentSpecifications.java - HistoryRepository · Stores append-only incident history entries
src/main/java/com/everbridge/incident/infrastructure/repository/IncidentHistoryRepository.java - HistoryListener · Persists audit history from status change events
src/main/java/com/everbridge/incident/infrastructure/events/IncidentHistoryListener.java
Ops
Configuration, schema, and observability
- Flyway Migrations · Explicit database schema creation and evolution
src/main/resources/db/migration/V1__create_incidents.sql - Profiles · Environment-specific settings for local, dev, and Docker deployments
src/main/resources/application.yml - Structured Logs · JSON logging and correlation-aware log output
src/main/resources/logback-spring.xml - Docker Stack · Compose-based app and observability environment
docker-compose.yml - PostgreSQL / H2 · Primary relational store for incidents and history
Connections 24
- IncidentController → IncidentMapper: maps · Transforms API payloads to and from domain-facing shapes
- IncidentController → IncidentService: calls · Delegates request handling to the application layer
- IncidentController → ProblemDetail: errors · Business and validation failures become ProblemDetail responses
- IncidentController → Correlation Filter: correlates · Requests receive or propagate correlation IDs
- IncidentService → Incident: uses · Coordinates incident lifecycle and state changes
- IncidentService → Transition Rules: validates · Checks permitted status transitions
- IncidentService → IncidentRepository: persists · Loads and saves incidents
- IncidentService → IncidentSpecifications: queries · Builds filtered incident searches
- IncidentService → StatusChangedEvent: publishes · Emits status change events after successful transitions
- Incident → IncidentStatus: tracks · Incident lifecycle state
- Incident → Severity: classifies · Severity is part of the incident model
- Incident → IncidentHistory: records · Incident changes are represented as audit entries
- StatusChangedEvent → HistoryListener: consumed by · Status change events trigger audit persistence
- HistoryListener → HistoryRepository: writes · Appends history entries
- IncidentRepository → PostgreSQL / H2: stores · Incident entities live in the relational database
- HistoryRepository → PostgreSQL / H2: stores · Audit entries live in the relational database
- Flyway Migrations → PostgreSQL / H2: migrates · Schema is created and evolved via migrations
- Profiles → PostgreSQL / H2: configures · Profiles switch between H2 and PostgreSQL settings
- Docker Stack → Profiles: supplies · Docker profiles and compose files drive runtime settings
- Structured Logs → Correlation Filter: correlates · Correlation IDs are emitted into structured logs
- Incident App → IncidentController: starts · Bootstraps the REST API and application context
- Incident App → IncidentService: wires · Creates the use case layer and its dependencies
- Incident App → IncidentRepository: wires · Enables JPA persistence
- Incident App → HistoryListener: wires · Registers domain event listeners