Domain APIs and Events Strategy
Status: Draft v1.1 — Platform Review Category: Domain APIs / EDA Owner: Platform Architecture (Lead Platform Engineer) Repositories:
repos/domain-apis,repos/argocd-edaBased on:initial-thoughts.md, VPD Domain API POC patterns, current platform stack
1. Purpose
This strategy defines how we design Domain APIs and integrate them with the Event-Driven Architecture (EDA) layer. It is grounded in our current technology stack (Apache Camel, Strimzi/Kafka, Kubernetes, ArgoCD) and the patterns validated by the VPD Submission Returns POC.
The goal is to establish a repeatable, scalable approach for any domain integration — not just VPD.
2. Core Position
The platform is the authoritative interpreter of business truth.
Backend systems provide data. The platform provides meaning.
Our backend estate is heterogeneous: independent data models, inconsistent lifecycle semantics, varying reliability, legacy protocols (XML), modern REST, and partial functional overlaps. No single backend represents the complete business truth.
Therefore:
- Domain APIs expose canonical, business-meaningful interfaces — not backend interfaces.
- Domain Events are emitted by the platform after canonical state changes — not by individual backends.
- Backends are treated as authoritative only for explicitly delegated fields.
3. Architecture Layers
Consumers (external, internal)
↓
[ Domain API Layer ] ←— Apache Camel (orchestration, transformation, HATEOAS)
↓ Kong / Envoy (edge routing, CORS, tracing)
[ Orchestration ] ←— Camel Routes (sagas, sequential/parallel fan-out)
↓ ↓ ↓
Backend Systems ←— Legacy (XML/SOAP), REST, SaaS
↓
[ Domain State ] ←— Platform-owned canonical state (future: PostgreSQL)
↓
[ Event Layer ] ←— Strimzi/Kafka (domain event publication)
↓
Consumers (event-driven)
4. Domain API Design
4.1 Domain Model Principles
- Domains are platform constructs, not backend constructs.
- Resources are canonical business entities (e.g.,
Submission,Trader,Period). - Field-level authority — each field records which backend is authoritative.
# Example field authority mapping
Submission:
periodKey:
authority: excise
customerId:
authority: customer-service
acknowledgementReference:
authority: tax-platform
calculations:
authority: excise # computed at submission time4.2 URL Design
GET /v1/{domain}/{resource}[/{id}]
POST /v1/{domain}/{resource}
POST /v1/{domain}/{resource}/{id}/commands/{command}
Examples from VPD POC:
POST /v1/vaping-duty/submissions # Submit return
GET /v1/vaping-duty/submissions/{ackRef} # Get by acknowledgement
- Major version in URL path (
/v1/) - Additive-only changes within a major version
- Commands pattern for business operations:
POST /v1/.../commands/cancel
4.3 Response Standards
- Always JSON at the domain layer (even when backends return XML)
- HATEOAS links for navigability (producer OAS is source of truth)
- Consistent error model:
{ "code": "...", "message": "...", "correlationId": "..." } - Idempotency via
X-Idempotency-Keyheader on all write operations
4.4 Content Negotiation
The Domain API abstracts backend content types:
| Backend | Protocol | Response | Domain API Response |
|---|---|---|---|
| excise | REST | XML | JSON (Camel transforms) |
| customer | REST | JSON | JSON (pass-through + enrich) |
| tax-platform | REST | JSON | JSON (pass-through) |
5. Orchestration Patterns
5.1 Technology: Apache Camel
All orchestration is implemented in Apache Camel. Two DSLs are supported:
| DSL | When to use |
|---|---|
| Java DSL | Complex orchestration, production routes, conditional logic |
| Groovy / Kamelets | Reusable integration fragments, prototyping, testable units |
| YAML DSL | Simple routes, tooling compatibility |
The Kamelet pattern is preferred for reusable backend adapters (e.g., excise-getPeriod, customer-getCustomer). Kamelets are independently testable and composable.
5.2 Sequential Fan-out (validated pattern)
For the submit-return flow in VPD:
1. GET excise/registration → validate approvalNumber
2. GET excise/period → validate periodKey, get rates
3. GET customer → enrich with trader details
4. POST excise/validate-calc → compute duty
5. POST tax-platform/store → persist submission
→ Emit domain event: SubmissionCreated
Key rules from POC:
- Store intermediate responses in Camel exchange properties (not headers).
- Explicitly set
CamelHttpMethodbetween calls. - Remove
CamelHttp*headers between calls. - Use Groovy for JSON merge/transformation steps.
5.3 Compensation (Sagas)
For multi-step writes, implement compensation:
Step 1: Backend A write → store rollback context
Step 2: Backend B write → if fails, compensate step 1
Step 3: Emit event → transactional outbox (see §7)
Camel onException blocks handle compensation routing.
5.4 Parallel Fan-out
Use Camel multicast for independent backend reads (read-only enrichment only). Never parallelize writes — sequencing guarantees are required for consistency.
6. Event Strategy
6.1 Two Event Categories
| Type | Producer | Consumer | Purpose |
|---|---|---|---|
| Integration Signal | Backend system | Platform only | Raw backend notification |
| Domain Event | Platform (Camel route) | External consumers | Business-meaningful state change |
Backend events are never exposed to consumers directly. Only the platform can:
- Aggregate changes across systems
- Apply field authority rules
- Resolve conflicts
- Interpret lifecycle meaning
6.2 Domain Event Emission Rule
A domain event is emitted only when the effective canonical state changes.
Multiple backend updates with no canonical change → no event emitted.
6.3 Event Naming and Schema
{Domain}.{Resource}.{PastTenseVerb}
Examples:
VapingDuty.Submission.Created
VapingDuty.Submission.Amended
VapingDuty.Registration.Suspended
Event envelope (CloudEvents-aligned):
{
"specversion": "1.0",
"type": "VapingDuty.Submission.Created",
"source": "/v1/vaping-duty/submissions",
"id": "<uuid>",
"time": "2026-01-26T10:00:00Z",
"correlationid": "<original-request-correlation-id>",
"datacontenttype": "application/json",
"data": {
"acknowledgementReference": "ACK-2026-01-26-000123",
"periodKey": "24A1",
"vpdApprovalNumber": "VPD123456"
}
}6.4 Reliability: Transactional Outbox
All domain events are published via an outbox pattern:
1. Camel route: atomic write
→ Update domain state
→ Write event to outbox table (same transaction)
2. Outbox publisher (background):
→ Reads unpublished events
→ Publishes to Kafka topic
→ Marks as published
3. Kafka (Strimzi)
→ Consumer groups receive events
→ At-least-once delivery guaranteed
Until a persistent domain state store is provisioned, events are written directly from Camel to Kafka with correlation IDs for traceability. The outbox pattern is the Phase 2 target.
6.5 Kafka Topic Conventions (Strimzi)
{company}.{domain}.{resource}.{verb}
Example:
platform.vaping-duty.submission.created
platform.vaping-duty.submission.amended
- Partitioned by entity ID (e.g.,
vpdApprovalNumber) - Retention: 7 days default, 90 days for audit-critical topics
- Schema evolution: additive-only; breaking changes require new topic version
7. External Event Ingestion
7.1 Ingress Journal
All inbound external events are journaled before processing:
Receive → Journal (Kafka) → Acknowledge → Process
Benefits: replay, audit, forensics, rate-limiting independence.
7.2 Deduplication
Inbox dedup keys:
source + vendorEventId(primary)source + SHA256(payload)(fallback)
Camel Idempotent Consumer component manages this.
7.3 Normalisation
External signals are transformed to canonical internal events before any downstream processing. Consumers never see raw vendor payloads.
8. Implementation: Current Stack
8.1 Runtime
| Component | Technology | Notes |
|---|---|---|
| Orchestration | Apache Camel (Java DSL + Kamelets) | JBang for local dev |
| Edge proxy | Envoy | CORS, distributed tracing |
| API gateway | Kong (future) / Envoy (current) | Auth, rate limiting |
| Event broker | Strimzi/Kafka | Platform Kafka cluster |
| Deployment | Kubernetes + ArgoCD | GitOps, Kargo for promotions |
8.2 Observability
- Tracing: OpenTelemetry via Envoy sidecars → Tempo (LGTM stack)
- Logging: Structured JSON → Loki
- Metrics: Prometheus-compatible → Mimir
- Correlation:
X-Correlation-Idpropagated through all hops
8.3 Kubernetes Deployment Structure
k8s/
├── components/
│ ├── domain-api/ # Camel orchestrator + Envoy sidecar
│ ├── excise-mock/ # WireMock (XML)
│ ├── customer-mock/ # Prism (JSON)
│ └── tax-platform-mock/ # Prism (JSON)
└── overlays/
└── k8s-lab/ # Lab environment kustomization
ArgoCD manages all domain-api deployments. Kargo handles environment promotion (lab → staging → production).
9. OAS and Contract Governance
9.1 Spec-First Development
- Producer OAS — source of truth for the domain API contract.
- Platform OAS — generated from producer OAS (may include platform additions).
- Mock OAS — backend contracts (independent of domain API).
No domain API code is written before the producer OAS is reviewed.
9.2 Versioning Policy
| Change type | Version impact |
|---|---|
| Add optional field | None (backward compatible) |
| Add required field | Minor bump or new resource version |
| Remove / rename field | Major version (/v2/) |
| New event field (additive) | None |
| Remove / rename event field | New topic version |
9.3 Contract Testing
- Domain API consumer tests use Prism-validated mocks (not stubs).
- Camel Kamelet unit tests validate transformation logic.
- Acceptance tests cover end-to-end flows against mock backends in Kubernetes.
10. Maturity Roadmap
Phase 1 — Foundation (Current / Near-term)
- Producer OAS pattern established
- Camel orchestration with Kamelets
- Envoy for CORS and distributed tracing
- Kubernetes deployment manifests (k8s-lab)
- First production domain: VPD Submission Returns
- Direct Kafka event emission from Camel routes
- CloudEvents envelope standard adopted
Phase 2 — Reliability and Scale
- Transactional outbox (persistent domain state store)
- Field-level authority registry (initial YAML catalog)
- Idempotency enforcement (Redis or DB-backed)
- Dead Letter Queue (DLQ) handling for failed events
- Contract tests in CI pipeline
Phase 3 — Optimisation
- Domain event schema registry (Confluent Schema Registry or Apicurio)
- Automated OAS → Kamelet scaffolding
- Self-healing saga compensation
- Drift detection against backend sources
11. Governance
- Each domain has a business owner and a technical owner.
- All changes to domain schemas, authority mappings, and event schemas require lightweight RFC review.
- A central registry (initially YAML in-repo) tracks domain definitions, schemas, authority mappings, event catalogs, and owners.
12. Key Principles (Summary)
| Principle | Implementation |
|---|---|
| Domains are platform constructs | Camel routes define the canonical model |
| Resources span systems | Camel sequential fan-out |
| Authority is field-level | Authority mapping YAML per domain |
| Writes are orchestrated | Sagas via Camel routes |
| Domain events are platform-owned | Camel → Kafka, not backend → Kafka |
| External events are signals | Ingress journal + normalisation |
| Reliability is enforced at boundaries | Outbox + idempotency |
| Semantics are centralised | Producer OAS is the contract |
| All infrastructure is code | k8s manifests, ArgoCD apps in Git |