Files
clean-architecture-backend-…/docs/notification/module-mapping.md
T
DongHyeonkaandClaude Opus 5 701ba67456 feat(notification): implement the notification delivery platform
Maps the 31-module plan onto the registry's 19 leaves as packages; the two
edges the registry forbids (provider->httpclient, inbox->messaging) are
replaced by application-owned ports. See docs/notification/module-mapping.md.

Acceptance is not delivery: ProviderSubmissionResult refuses to carry a
delivery outcome, and AMBIGUOUS is a first-class terminal state that blocks
automatic retry and fallback until reconciliation resolves it.

Providers: SES (SigV4 + SNS callback), Twilio (X-Twilio-Signature +
reconciliation), FCM (FID-primary batch), APNs, Web Push (RFC 8030/8291/8292),
SMTP and webhook. Contact points are AES-256-GCM encrypted with a separate
HMAC lookup fingerprint; nothing raw reaches a log, metric tag or exception.

Dispatch commits the attempt row, calls the provider with no transaction open,
then records the outcome; the durable queue uses FOR UPDATE SKIP LOCKED.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 13:57:27 +09:00

6.3 KiB

Notification Delivery Platform — module mapping

Source design: notification-superpowers-package/docs/superpowers/specs/2026-08-10-notification-platform-design.md

Source plan: notification-superpowers-package/docs/superpowers/plans/2026-08-10-notification-platform-implementation-plan.md

Why a mapping exists

The plan was written against a hypothetical repository (modules/notification/**, root package io.backend.skeleton.notification, 31 Gradle projects). This repository is a fail-closed 19-leaf Clean Architecture template: src/settings.gradle rejects any registry that does not contain exactly the 19 modules in src/config/architecture/modules.json, and verifyCleanArchitectureDependencies rejects any project edge outside allowed_dependencies.

Creating 31 new Gradle projects would violate HARD-STOP #5 of AGENTS.md. The package README anticipates this and instructs the implementer to map dependency catalog and package/file paths onto the host repository's rules while preserving the public contracts and reliability semantics.

Every logical module of the plan is therefore implemented as a package inside the registered leaf that owns its responsibility. No public contract, evidence rule, or reliability semantic is dropped.

Logical module → registered leaf

Plan module Registered leaf Package
notification-core-api application-core dev.caskeleton.application.notification.platform.api
notification-content-api application-core …platform.api.content
notification-contact-api application-core …platform.contact
notification-template-api application-core …platform.template
notification-policy application-core …platform.policy
notification-provider-spi application-core …platform.provider
notification-callback-api application-core …platform.callback
notification-email-api application-core …platform.email
notification-sms-api application-core …platform.sms
notification-push-api application-core …platform.push
notification-webpush (API half) application-core …platform.webpush
notification-inbox-api application-core …platform.inbox
notification-admin-api application-core …platform.admin
notification-security (ports + redaction) application-core …platform.security
notification-observability (ports) application-core …platform.observation
notification-dispatch-runtime adapter:outbound:notification dev.caskeleton.adapter.outbound.notification.platform.dispatch
notification-security (AES-GCM/HMAC impl) adapter:outbound:notification …platform.security
notification-template-thymeleaf (reference renderer) adapter:outbound:notification …platform.template
notification-email-smtp adapter:outbound:notification …platform.provider.smtp
notification-email-ses adapter:outbound:notification …platform.provider.ses
notification-sms-twilio adapter:outbound:notification …platform.provider.twilio
notification-push-fcm adapter:outbound:notification …platform.provider.fcm
notification-push-apns adapter:outbound:notification …platform.provider.apns
notification-webpush (transport + crypto) adapter:outbound:notification …platform.provider.webpush
notification-webhook-extension adapter:outbound:notification …platform.provider.webhook
notification-observability (Micrometer impl) adapter:outbound:notification …platform.observation
notification-admin-runtime adapter:outbound:notification …platform.admin
notification-reactor adapter:outbound:notification …platform.reactor
notification-spring-boot-starter adapter:outbound:notification (+ app-bootstrap wiring) …platform.autoconfigure
notification-persistence-jpa adapter:outbound:persistence-jpa dev.caskeleton.adapter.outbound.persistence.notification.platform
notification-inbox-jpa adapter:outbound:persistence-jpa …persistence.notification.platform.inbox
notification-callback-mvc adapter:inbound:web dev.caskeleton.adapter.inbound.web.notification.platform.callback
notification-callback-webflux adapter:inbound:web …callback.reactive
notification-testkit test source sets of the owning leaves …platform.testkit

Dependency-direction consequences

The plan's module DAG (*-apiprovider-spi/policy → runtime/adapters → starter) is preserved by the leaf DAG that the registry already enforces:

application-core (all *-api, provider SPI, policy, callback contracts)
        ↑                        ↑                      ↑
adapter:outbound:notification    adapter:outbound:persistence-jpa    adapter:inbound:web
        ↑                        ↑                      ↑
                          app-bootstrap

Two plan edges cannot be expressed as project edges in this repository, and are replaced by ports:

  1. notification-email-ses, notification-sms-twilio, notification-push-fcm, notification-push-apns, notification-webpush, notification-webhook-extensionhttpclient platform. adapter-outbound-notification is not allowed to depend on adapter-outbound-httpclient. The provider adapters therefore call dev.caskeleton.adapter.outbound.notification.platform.provider.http.NotificationHttpGateway, an adapter-local port with a JDK java.net.http.HttpClient default implementation. app-bootstrap sees both leaves and is the supported place to substitute an implementation backed by the HTTP Client Platform (TLS/timeout/circuit-breaker/SSRF/dynamic-target policy reuse).
  2. notification-inbox-jpaoptional messaging outbox integration. adapter-outbound-persistence-jpa may not depend on adapter-outbound-messaging; the inbox publishes through the existing persistence outbox tables plus the NotificationInboxSignalPort application port, and app-bootstrap binds the relay.

Commit policy

AGENTS.md pins commit policy to human-only. Step 5 (git add / git commit) of every plan task is therefore intentionally not executed by the agent; the working tree carries the change and the human owner commits.