Implement apix-registry with IoT sunset/decommission lifecycle and full BDD suite

- REST API: register, patch, O-level, replacements, history, search endpoints
- IoT lifecycle validations: future sunset, lock-before-release, sunset-passed-before-decommission
- DB schema: Liquibase changesets 001–008 (services, versions, replacements, sunset-at column)
- @ColumnTransformer(write="?::jsonb") on bsm_payload fields to avoid JDBC varchar→jsonb rejection
- Jandex plugin on apix-common + quarkus.index-dependency so @NotBlank validators resolve at runtime
- quarkus-logging-json extension added; quarkus.log.console.json=false is now a recognised key
- Fix requireSunsetBeforeLockRelease: Boolean.TRUE.equals instead of !Boolean.FALSE.equals (null guard)
- BDD suite: 27 scenarios / 213 steps across 5 feature files (sunset-lock, decommission, replacement, discovery, anonymity)
- Test infrastructure: JDBC TRUNCATE in @Before for DB isolation, Arc.container() for clock control — no test endpoints in production code
- sunsetAt truncated to microseconds in BDD steps to match Postgres timestamptz precision
- Cucumber step fixes: singular/plural candidate(s), lastResponse propagation in replacementsReturnsNCandidates

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Carsten Rehfeld
2026-05-08 09:13:26 +02:00
commit b2a16a8be7
71 changed files with 5480 additions and 0 deletions
+135
View File
@@ -0,0 +1,135 @@
---
arc42: "5 — Building Block View"
status: stub
---
## 5.1 Level 1 — Maven Module Structure
```plantuml
@startuml modules
skinparam packageStyle rectangle
package "apix-common\n(plain Java 21 library)" as common {
component [OLevel\nLivenessStatus\nBsmPayload\nServiceSummaryDto\nVerificationResult] as dtos
}
package "apix-verification\n(plain Java 21 library)" as verification {
component [O1DnsVerifier\nO2GleifVerifier\nO2OpenCorporatesVerifier\nO3HygieneVerifier\nSanctionsScreener\nVerificationPipeline] as verifiers
}
package "apix-registry\n(Quarkus 3.x app)" as registry {
component [IndexResource\nServiceResource\nRegisterResource] as res
component [RegistryService\nVerificationOrchestrator] as svc
component [ServiceRecord\nServiceRepository] as repo
component [Liquibase\nmigrations] as lb
}
package "apix-spider\n(Quarkus 3.x app)" as spider {
component [SpiderScheduler\nLivenessFetcher\nLivenessEvaluator\nOpenApiParser\nMcpParser] as spider_core
component [SpiderServiceView\nSpiderRepository] as spider_repo
}
package "apix-portal\n(Quarkus 3.x app)" as portal {
component [PortalResource\nAdminResource\nRegistryClient] as portal_res
component [Qute templates] as templates
}
verification ..> common : depends on
registry ..> common : depends on
registry ..> verification : depends on
spider ..> common : depends on
portal ..> common : depends on
@enduml
```
## 5.2 Level 1 — Deployment View (Docker Compose)
```plantuml
@startuml deploy_l1
package "Docker Compose — Hetzner CX22" {
component [Caddy\n:80 / :443] as caddy
component [apix-registry\n:8180 (internal)] as registry
component [apix-spider\n:8082 (internal only)] as spider
component [apix-portal\n:8081 (internal)] as portal
database [PostgreSQL 16\n:5432 (internal)] as db
}
cloud Internet
Internet --> caddy : HTTPS
caddy --> registry : /api/*
caddy --> portal : /*
registry --> db : Hibernate ORM (Liquibase owner)
spider --> db : Hibernate ORM (Liquibase disabled)
portal --> registry : REST Client (HTTP internal)
spider --> [External Services] : liveness checks (HTTPS)
@enduml
```
## 5.3 Level 2 — apix-registry Internals
```plantuml
@startuml level2_registry
package "apix-registry" {
component [IndexResource] as r_index
component [ServiceResource] as r_svc
component [RegisterResource] as r_reg
component [RegistryService] as svc
component [VerificationOrchestrator] as orch
component [ServiceRepository\n(Panache)] as repo
component [Bean Validation\n(@Valid on JAX-RS)] as val
}
r_index --> svc
r_svc --> svc
r_reg --> val
r_reg --> svc
r_reg --> orch
svc --> repo
orch --> [VerificationPipeline\n(apix-verification)]
orch --> repo : persist VerificationResult
@enduml
```
## 5.4 Level 2 — apix-spider Internals
```plantuml
@startuml level2_spider
package "apix-spider" {
component [SpiderScheduler\n@Scheduled(every=15m)] as sched
component [LivenessFetcher\n@RestClient\n@RunOnVirtualThread] as fetcher
component [LivenessEvaluator\n(pure logic)] as eval
component [OpenApiParser] as oa
component [McpParser] as mcp
component [SpiderRepository\n(Panache)] as repo
}
sched --> repo : load services due for check
sched --> fetcher : dispatch per service
fetcher --> eval : HTTP status + response_ms
fetcher --> oa : spec URL
fetcher --> mcp : MCP URL
eval --> repo : write LivenessStatus
oa --> repo : write spec validation result
mcp --> repo : write spec validation result
@enduml
```
## 5.5 Component Responsibility Table
| Module / Component | Type | Responsibility |
|---|---|---|
| `apix-common` | Plain Java library | Shared enums and DTOs; no framework dependency; used by all modules |
| `apix-verification` | Plain Java library | O-level elevation pipeline; pure logic + external HTTP/DNS calls via `java.net.http`; no Quarkus context |
| `apix-registry` | Quarkus app | REST API (HATEOAS); BSM registration + validation; capability search; schema owner (Liquibase) |
| `apix-spider` | Quarkus app | Scheduled liveness checks; OpenAPI/MCP spec verification; writes liveness metrics to DB; independent lifecycle |
| `apix-portal` | Quarkus app | Human-readable web portal (HTMX + Qute); registration form; admin O-level view; calls registry via REST Client |
| `VerificationOrchestrator` | CDI bean (registry) | Bridge between Quarkus config injection and the plain-Java `VerificationPipeline`; persists results |
| `LivenessEvaluator` | Plain class (spider) | Pure function: HTTP status + response time → `LivenessStatus`; no I/O; testable without Quarkus |
| `ServiceRecord` | Panache entity (registry) | Full entity — all columns; schema owner |
| `SpiderServiceView` | Panache entity (spider) | Read/write subset of `services` table — only liveness columns; does not run migrations |
| PostgreSQL | Database | Single shared instance; registry owns schema; spider and portal are consumers |