Files
clean-architecture-backend-…/docs/superpowers/plans/2026-09-18-messaging-platform-bridge.md
T

94 lines
4.9 KiB
Markdown

# Messaging Platform Bridge Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Connect the canonical application integration-event publish boundary to the messaging platform without re-encoding bytes or introducing broker-specific ownership in app-bootstrap.
**Architecture:** Add an application-owned publish port, implement it in `adapter/outbound/messaging/platformbridge`, and publish canonical pre-encoded envelopes through `EncodedMessagePublisher`. The bridge preserves identity/routing/evidence and maps platform publish evidence explicitly into application outcomes.
**Tech Stack:** Java 21, Gradle 9, Spring Boot auto-configuration, JUnit 5, AssertJ.
**Spec:** `docs/superpowers/specs/2026-09-18-messaging-platform-bridge-design.md`
## Global Constraints
- Preserve existing uncommitted changes; do not reset, stage, commit, amend, or push.
- Never invent missing canonical identity, timestamp, trace, tenant, schema, or routing values.
- Never bypass `DefaultMessagePublisher` through transport SPI or native Kafka clients.
- Preserve exact `ValidatedIntegrationEvent.envelopeBytes()`.
- Fail closed before send when event or causation identity is not UUIDv7.
- Legacy outbox storage/relay migration is outside this plan.
---
### Task 1: Application-owned canonical publish port
**Files:**
- Create: `application-core/src/main/java/dev/caskeleton/application/messaging/event/IntegrationEventPublishPort.java`
- Test: existing bridge test compile contract
**Interfaces:**
- Produces: `CompletionStage<OutboxPublishOutcome> publish(ValidatedIntegrationEvent event)`
- [x] Create the minimal application-owned interface.
- [x] Run the focused bridge test and verify remaining failures are platform dependencies/adapter implementation, not the port.
### Task 2: Outbound messaging platform API dependency
**Files:**
- Modify: `adapter/outbound/messaging/build.gradle`
- Modify: `adapter/outbound/messaging/gradle.lockfile` through Gradle lock writing
**Interfaces:**
- Consumes: `:messaging:messaging-core-api`, `:messaging:messaging-schema-api`.
- [x] Add only the platform API dependencies required by the bridge.
- [x] Refresh this module's locks.
- [x] Re-run focused bridge test and verify the missing type set is reduced to bridge production code.
### Task 3: Canonical platform bridge
**Files:**
- Create: `adapter/outbound/messaging/src/main/java/dev/caskeleton/adapter/outbound/messaging/platformbridge/PlatformIntegrationEventPublishAdapter.java`
- Test: `adapter/outbound/messaging/src/test/java/dev/caskeleton/adapter/outbound/messaging/platformbridge/PlatformIntegrationEventPublishAdapterTest.java`
**Interfaces:**
- Consumes: `IntegrationEventPublishPort`, `EncodedMessagePublisher`.
- Produces: canonical application-to-platform anti-corruption bridge.
- [x] Implement UUIDv7 parsing that rejects incompatible identity before publisher invocation.
- [x] Map canonical metadata and exact bytes into `MessageEnvelope<EncodedMessage>`.
- [x] Preserve non-first-class evidence in bounded `x-ca-*` headers.
- [x] Map `PublishResult` using transmission evidence.
- [x] Run all three focused bridge tests to GREEN.
### Task 4: Spring ownership while preserving the legacy seam
**Files:**
- Modify: `adapter/outbound/messaging/src/main/java/dev/caskeleton/adapter/outbound/messaging/MessagingSettings.java`
- Modify: `adapter/outbound/messaging/src/main/java/dev/caskeleton/adapter/outbound/messaging/autoconfigure/MessagingBridgeRootAutoConfiguration.java`
- Test: focused auto-configuration ownership test
**Interfaces:**
- Consumes: Spring-provided `EncodedMessagePublisher`, explicit `app.messaging.producer-id`.
- Produces: `IntegrationEventPublishPort` bean for canonical events.
- [x] Add explicit `producerId` to the existing `app.messaging` adapter settings.
- [x] Register the canonical bridge only when `app.messaging.producer-id` is explicitly present.
- [x] Keep `KafkaSender` / `KafkaMessageBroker` as a documented transitional dependency of legacy outbox/realtime only.
- [x] Add a Spring test proving producer-id present => one canonical bridge bean, absent => no canonical bridge bean.
- [x] Run outbound messaging tests.
### Task 5: Platform and composition regression verification
**Files:** no new production files unless a test exposes a real defect.
- [x] Run `:messaging:messaging-runtime-core:test`.
- [x] Run `:messaging:messaging-spring-boot-starter:test`.
- [x] Run `:adapter:outbound:messaging:check --warning-mode=fail`.
- [x] Run `:app-bootstrap:architectureTest :app-bootstrap:systemTest --warning-mode=fail`.
- [x] Search for direct app-bootstrap Kafka producer ownership.
- [x] Run `git diff --check`.
- [x] Report any remaining legacy outbox cutover blocker explicitly rather than inventing a migration.