Files
member-console/design/integration/interfaces.md
Christian Galo 195cd348a9 Update design to use schemas per module and revert to using industry
aligned terminology for discounts and coupons and promo codes.
2026-04-01 03:08:35 -05:00

5.0 KiB

Integration -- Interfaces

Module: Integration (Cross-cutting) Tables: 2


Provided Interfaces

Structural: NONE

Neither webhook_events nor integration_outbox is referenced by foreign key from any table in any module. No other module has a structural dependency on the integration module.

Behavioral

While no structural interfaces exist, the integration module provides two behavioral interfaces that other modules consume at runtime:

webhook_events: Sync Pipeline Entry Point

The webhook_events table is the entry point for the inbound sync pipeline that populates projection tables across the system. Under the Stripe strategy (Decision 90), Category B billing tables -- subscriptions, subscription_items, subscription_changes, invoices, invoice_line_items, payments, payment_methods, refunds, disputes, products, prices, coupons, promotion_codes, discounts -- are populated via webhook events processed by Temporal workflows.

Behavioral consumers:

Module Relationship Notes
billing Primary consumer. Webhook events drive the sync pipeline that populates Category B projection tables. Temporal workflows read from webhook_events, transform provider objects, and write to core billing tables. This is the highest-volume behavioral interface in the system.
entitlements Future consumer. Infrastructure provisioning callbacks (e.g., Nextcloud reporting instance creation complete) arrive as webhook events and update resource_provisions state. Not yet active; depends on infrastructure provider integration.

integration_outbox: Outbound Event Relay

The integration_outbox captures domain state changes from any module that require external action. Any module may write to the outbox within its domain transactions.

Behavioral consumers:

Module Relationship Notes
All modules Any module may INSERT into integration_outbox within its domain transaction to trigger external actions. The outbox is a general-purpose relay, not scoped to a specific module. Examples: billing writes outbox entries for Stripe product sync; entitlements writes entries for Nextcloud provisioning; organization could write entries for notification delivery.

Consumed Interfaces

Structural: NONE

Neither webhook_events nor integration_outbox contains a foreign key reference to any table in any module. Both tables use text fields for entity identification:

  • webhook_events.provider and webhook_events.provider_event_id are text fields identifying the source provider and its event identifier.
  • integration_outbox.aggregate_type and integration_outbox.aggregate_id are a text discriminator and UUID identifying the originating domain entity, with no FK constraint.
  • integration_outbox.target_provider is a text field identifying the destination provider.

This text-based identification follows the same rationale as the audit log's bare polymorphic association on entity references (Decision 23): these tables must accept references to any entity type in the system, and FK constraints would be structurally impractical.


Interface Contracts

Idempotency Guarantee

The UNIQUE(provider, provider_event_id) constraint on webhook_events guarantees that duplicate webhook deliveries are detected at insert time. Consumers of the sync pipeline can rely on this invariant: a given provider event will appear at most once in the table. The webhook endpoint returns HTTP 200 on constraint violation, signaling to the provider that the event was already received.

At-Least-Once Delivery Semantics

The integration_outbox guarantees at-least-once delivery for outbound integration actions. Because the outbox INSERT occurs in the same transaction as the domain state change, any committed domain change will have a corresponding outbox entry. The background polling worker may deliver the same event more than once (e.g., if it crashes after executing the external action but before marking the entry completed). Provider handlers must be idempotent.

Payload PII-Free Invariant

Both webhook_events.payload and integration_outbox.payload are governed by the JSONB Governance Policy (Decision 34): PII is categorically prohibited in all JSONB columns. Webhook payloads containing PII must be scrubbed before storage; outbox payloads must be composed without PII. This invariant enables the integration tables to participate in retention and archival policies without triggering GDPR anonymization obligations.

Isolation Guarantee

The integration module imposes no structural constraints on any other module. No module need consult the integration tables to answer a domain question. The self-sufficiency principle (applied via Decision 87 and the five boundary heuristics) ensures that every business-answerable concept has a domain module representation. The integration module's coupling to other modules is entirely behavioral and unidirectional at the schema level.