WirSchiffenDas arc42 section 9
Architecture Decisions
| ADR | Decision | Considered alternatives | Consequence |
|---|---|---|---|
| ADR-001: Choreography instead of central orchestration | The coordinator starts only the anchor Fluids; Fluids starts Drivetrain and Mechanical, both report to EMS. The coordinator does not know the flow, but knows the expected result set (read model). | central orchestration as orchestrator or saga process manager; completion report by the last step (“last step reports done”). Rejected: choreography required, coordinator otherwise a single point of failure, a reporting final step wires the order again. | Happy path decentralised, order open, EMS needs local state; the run projection is observation. |
| ADR-002: Coexistence of REST and Kafka | REST /analyze represents the transitions, Kafka transports status, results and retry, SSE the aggregated view. |
REST only with callback or polling; Kafka only, which would remove the immediate acceptance of a start; WebSocket instead of SSE. | REST edges can be protected locally by breakers, Kafka decouples the UI projection; operations needs a broker. |
| ADR-003: Decentralised retry as Kafka command | The coordinator cleans up its read model and publishes { runId, cluster } on analysis-retry; the service interprets it itself. |
executing the retry in the coordinator via REST; repeating the entire run; sending the command via REST. | The coordinator remains the entry point; a retry of Fluids continues the choreography, EMS invalidates subsequent states. |
ADR-004: Reduced AnalyzeRequest |
Only runId and optionally source for the EMS fan-in; the configuration stays in the config service. |
carrying the configuration in the request; passing on upstream results; letting every service load the configuration itself. | The contract matches the data need; the results are simulated, not computed from equipment values (TS-6). |
| ADR-005: Circuit breaker at the caller with status fallback, without service registry | Opossum breaker at the caller instead of at the target (Nygard, chapter 5); the fallback publishes failed, discovery via Compose DNS. |
Hystrix with Eureka (JVM, end of life); Resilience4j (JVM); breaker in the target; Consul as registry; dummy fallback. | Resilience emerges locally per edge (§6.4); scaling requires a registry or service mesh (TS-4), publishing another service’s status is TS-5. |
| ADR-006: Business cut into four clusters, Fluids as anchor, EMS as fan-in | Cut by physical assembly groups: Fluids (three groups), Drivetrain (two), Mechanical (four), EMS (two). | eleven services, one per equipment (Nano Service); a single analysis service as in the as-is architecture (Mega Service); anchor in the coordinator. | The call graph is a DAG; a new cluster costs one entry each in the cluster enum, in RETRY_PROJECTION_SCOPE and in Compose. |
| ADR-007: Persistence only for the configuration, runtime state volatile | Database per service only for the config service (PostgreSQL 16); run projection and EMS fan-in are held process-locally in memory. | shared database for all services (Shared Persistence); Redis for the projection; event sourcing with replay. | Coordinator and EMS are not horizontally scalable, a restart loses running runs (TS-4). |
| ADR-008: Deployment with Docker Compose from a monorepo with a shared Dockerfile | Eight containers: six NestJS applications, Kafka, PostgreSQL; all images from the same multi-stage Dockerfile via ARG APP. |
cloud platform such as SEPP or Kubernetes; separate repository and Dockerfile per service; schema registry instead of shared types. | Reproducible start with one command; independent deployability limited by the shared library (TS-1). |