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-eda Based 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 time

4.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-Key header on all write operations

4.4 Content Negotiation

The Domain API abstracts backend content types:

BackendProtocolResponseDomain API Response
exciseRESTXMLJSON (Camel transforms)
customerRESTJSONJSON (pass-through + enrich)
tax-platformRESTJSONJSON (pass-through)

5. Orchestration Patterns

5.1 Technology: Apache Camel

All orchestration is implemented in Apache Camel. Two DSLs are supported:

DSLWhen to use
Java DSLComplex orchestration, production routes, conditional logic
Groovy / KameletsReusable integration fragments, prototyping, testable units
YAML DSLSimple 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 CamelHttpMethod between 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

TypeProducerConsumerPurpose
Integration SignalBackend systemPlatform onlyRaw backend notification
Domain EventPlatform (Camel route)External consumersBusiness-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:

  1. source + vendorEventId (primary)
  2. 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

ComponentTechnologyNotes
OrchestrationApache Camel (Java DSL + Kamelets)JBang for local dev
Edge proxyEnvoyCORS, distributed tracing
API gatewayKong (future) / Envoy (current)Auth, rate limiting
Event brokerStrimzi/KafkaPlatform Kafka cluster
DeploymentKubernetes + ArgoCDGitOps, 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-Id propagated 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

  1. Producer OAS — source of truth for the domain API contract.
  2. Platform OAS — generated from producer OAS (may include platform additions).
  3. 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 typeVersion impact
Add optional fieldNone (backward compatible)
Add required fieldMinor bump or new resource version
Remove / rename fieldMajor version (/v2/)
New event field (additive)None
Remove / rename event fieldNew 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)

PrincipleImplementation
Domains are platform constructsCamel routes define the canonical model
Resources span systemsCamel sequential fan-out
Authority is field-levelAuthority mapping YAML per domain
Writes are orchestratedSagas via Camel routes
Domain events are platform-ownedCamel → Kafka, not backend → Kafka
External events are signalsIngress journal + normalisation
Reliability is enforced at boundariesOutbox + idempotency
Semantics are centralisedProducer OAS is the contract
All infrastructure is codek8s manifests, ArgoCD apps in Git