Skip to content

saas-EPIC-INTEG — Cross-service integration (P0)

Epic ID: saas-EPIC-INTEG
Area path: ConnectSoft\SaaS\Integration
Priority: P0
Status: Not Started
Repository scope: All five ConnectSoft.Saas.*Template repos + shared documentation
Source gap analysis: SaaS Template Program — Deep Gap Analysis

Cross-cutting integration work that must complete before an end-to-end SaaS demo can run. Canonical contracts live in ConnectSoft.Documentation/Docs/starters/saas-cross-repo-published-language.md. Current drift is documented with file-level evidence in the deep analysis §3.


[001] saas-INTEG-F01 — Cross-repo integration topic harmonization

Type: Feature
Parent: saas-EPIC-INTEG
Implementation order: 001
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, topics
Priority: P0
Effort: M
Dependencies:
Blocks: saas-BIL-F02, saas-MET-F04
Source gap analysis: Topic harmonization

Description (full):

Tenants and Products Catalog already publish integration events on canonical topic names defined in TenantsConstants.EventTopics (for example tenants.domain.v1.tenant-activated) and ProductsCatalogConstants.EventTopics. Billing inbound subscriptions, however, subscribe to non-canonical topic strings that will never receive messages from the canonical publishers.

Current drift (evidence):

Consumer constant Value in Billing code Canonical value
BillingConstants.InboundEventTopics.TenantActivated saas.tenants.v1.tenant-activated tenants.domain.v1.tenant-activated
BillingConstants.InboundEventTopics.MeteringQuotaExceeded metering.quotas.v1.quota-exceeded metering.quota.v1.quota-exceeded

File: ConnectSoft.Saas.BillingTemplate/src/ConnectSoft.Saas.Billing/BillingConstants.cs

Metering publishes on MeteringConstants.EventTopics.QuotaExceeded (metering.quota.v1.quota-exceeded). Billing listens on the plural metering.quotas.v1.quota-exceeded form. The tenant-activated mismatch uses a legacy saas.tenants.v1.* prefix instead of the tenants.domain.v1.* namespace mandated by published language.

Target state: Every producer SetEntityName, consumer subscription, MassTransit topology registration, JSON descriptor publishedEvents / consumedEvents entry, and architecture test assertion uses the same canonical topic string. Billing inbound constants and saga endpoint configuration must match Tenants outbound and Metering outbound exactly.

Why it matters: Without topic alignment, Billing never receives tenant-activated or quota-exceeded events. Subscription provisioning and suspend-on-quota reactions silently fail in integration environments.

Acceptance criteria (testable):

  • AC-1: BillingConstants.InboundEventTopics.TenantActivated equals tenants.domain.v1.tenant-activated and matches TenantsConstants.EventTopics.TenantActivated.
  • AC-2: BillingConstants.InboundEventTopics.MeteringQuotaExceeded equals metering.quota.v1.quota-exceeded and matches MeteringConstants.EventTopics.QuotaExceeded.
  • AC-3: All MassTransit topology files (*MassTransitTopology.cs, FlowModel.MassTransit state machines) reference the corrected constants — no hard-coded drift strings remain in Billing.
  • AC-4: A cross-repo architecture or contract test (or published-language scan) fails CI if any SaaS repo introduces a non-canonical inbound topic for these two events.
  • AC-5: ConnectSoft.Saas.Billing.json documents the six inbound topics with canonical names once saas-BIL-F02 consumed-events section ships (coordinate; this feature owns the canonical string decision).

Implementation notes (full):

  • Files to touch: ConnectSoft.Saas.BillingTemplate/src/ConnectSoft.Saas.Billing/BillingConstants.cs; ConnectSoft.Saas.Billing.FlowModel.MassTransit/*ReactionStateMachine.cs endpoint bindings; optional shared constant extraction to a documented cross-repo table in published language.
  • Code symbols: BillingConstants.InboundEventTopics.TenantActivated, BillingConstants.InboundEventTopics.MeteringQuotaExceeded.
  • Reference canonical sources: TenantsConstants.EventTopics, MeteringConstants.EventTopics, saas-cross-repo-published-language.md topic plan table.
  • Tests: extend Billing CrossRepoPublishedLanguageTests (currently placeholder) or add integration test asserting topology entity names.
  • Migration / data impact: none — topic rename only; existing RabbitMQ/ASB bindings must be recreated in demo environments.

Out of scope:

  • Quota payload field rename (Dimension vs MeterKey) — owned by saas-INTEG-F02.
  • Billing-specific saga behavior changes beyond subscription endpoint names — owned by saas-BIL-F02.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-S01.1 — Harmonize Billing inbound topics with canonical published language

Type: User Story
Parent: saas-INTEG-F01
Implementation order: 001
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, billing
Priority: P0
Effort: M
Dependencies:
Blocks: saas-INTEG-T01.1.1, saas-INTEG-T01.1.2
Source gap analysis: Topic harmonization

Description (full):

As a platform integrator running the five-repo E2E demo, I need Billing to subscribe to the same topic names that Tenants and Metering publish on, so that tenant activation triggers subscription provisioning and quota-exceeded triggers billing reactions without manual broker reconfiguration.

The story delivers corrected constants and topology wiring in Billing, verified against Tenants (tenants.domain.v1.tenant-activated) and Metering (metering.quota.v1.quota-exceeded) outbound definitions.

Acceptance criteria (testable):

  • AC-1: Given Tenants publishes TenantActivatedEvent on TenantsConstants.EventTopics.TenantActivated, when Billing MassTransit host starts, then the tenant-activated consumer endpoint binds to the identical entity name.
  • AC-2: Given Metering publishes QuotaExceededIntegrationEvent on MeteringConstants.EventTopics.QuotaExceeded, when Billing MassTransit host starts, then the quota-exceeded consumer endpoint binds to the identical entity name.
  • AC-3: Grep across ConnectSoft.Saas.BillingTemplate returns zero occurrences of saas.tenants.v1.tenant-activated and metering.quotas.v1.quota-exceeded.

Implementation notes (full):

  • Primary file: BillingConstants.cs lines 65–84 (InboundEventTopics nested class).
  • Update XML doc comments on inbound DTOs to reference corrected constant values.
  • Run Billing architecture tests after change.

Out of scope:

  • Entitlements or Catalog topic changes (already canonical).

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T01.1.1 — Update BillingConstants inbound topic strings

Type: Task
Parent: saas-INTEG-S01.1
Implementation order: 001
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, billing
Priority: P0
Effort: S
Dependencies:
Blocks: saas-INTEG-T01.1.2
Source gap analysis: Topic harmonization

Description (full):

Change BillingConstants.InboundEventTopics.TenantActivated from saas.tenants.v1.tenant-activated to tenants.domain.v1.tenant-activated. Change BillingConstants.InboundEventTopics.MeteringQuotaExceeded from metering.quotas.v1.quota-exceeded to metering.quota.v1.quota-exceeded. Update inline documentation on affected inbound event types (MeteringQuotaExceededInboundIntegrationEvent, tenant-activated inbound DTOs).

Acceptance criteria (testable):

  • AC-1: Constant values match TenantsConstants.EventTopics.TenantActivated and MeteringConstants.EventTopics.QuotaExceeded character-for-character.
  • AC-2: Solution builds with no compiler warnings on obsolete topic references.

Implementation notes (full):

  • File: ConnectSoft.Saas.BillingTemplate/src/ConnectSoft.Saas.Billing/BillingConstants.cs.
  • Cross-check: ConnectSoft.Saas.MeteringTemplate/src/ConnectSoft.Saas.Metering/MeteringConstants.cs (EventTopics.QuotaExceeded).

Out of scope:

  • MassTransit registration (next task).

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T01.1.2 — Align MassTransit topology and add topic contract test

Type: Task
Parent: saas-INTEG-S01.1
Implementation order: 001
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, billing, testing
Priority: P0
Effort: S
Dependencies: saas-INTEG-T01.1.1
Blocks: saas-BIL-F02
Source gap analysis: Topic harmonization

Description (full):

Ensure all Billing FlowModel.MassTransit state machines and topology configurators use BillingConstants.InboundEventTopics rather than string literals. Add or enable a test that asserts inbound topic constants equal canonical values from Tenants and Metering constant classes (assembly reflection or shared test data table).

Acceptance criteria (testable):

  • AC-1: MeteringQuotaExceededBillingReactionStateMachine and tenant-activated reaction state machines resolve endpoints via corrected constants.
  • AC-2: New or updated test in ConnectSoft.Saas.Billing.ArchitectureTests fails if topic constants diverge from canonical published-language table.

Implementation notes (full):

  • Files: ConnectSoft.Saas.Billing.FlowModel.MassTransit/MeteringQuotaExceededBillingReactionStateMachine.cs; tenant-activated reaction state machine(s); BillingMassTransitTopology.cs if present.
  • Pattern reference: ConnectSoft.Saas.ProductsCatalogTemplate/tests/.../CrossRepoPublishedLanguageTests.cs (real implementation in Catalog).

Out of scope:

  • End-to-end acceptance test across all five services (follow-up demo checklist).

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

[002] saas-INTEG-F02 — Quota payload Dimension to MeterKey alignment

Type: Feature
Parent: saas-EPIC-INTEG
Implementation order: 002
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, metering, billing
Priority: P0
Effort: M
Dependencies: saas-INTEG-F01
Blocks: saas-MET-F04, saas-BIL-F02
Source gap analysis: Quota payload alignment

Description (full):

Metering publishes QuotaExceededIntegrationEvent with property Dimension (the usage-meter dimension key). Billing inbound ACL DTO MeteringQuotaExceededInboundIntegrationEvent expects MeterKey. MassTransit/System.Text.Json deserialization maps by property name; Dimension values are silently dropped, leaving MeterKey empty at runtime.

Evidence:

  • Publisher: ConnectSoft.Saas.MeteringTemplate/src/ConnectSoft.Saas.Metering.MessagingModel/Events/QuotaExceededIntegrationEvent.cs — property Dimension.
  • Consumer: ConnectSoft.Saas.BillingTemplate/src/ConnectSoft.Saas.Billing.MessagingModel/Inbound/MeteringQuotaExceededInboundIntegrationEvent.cs — property MeterKey.

Target state: A single canonical field name documented in published language and implemented consistently on both sides (recommended: MeterKey as the cross-repo term, with Metering renaming DimensionMeterKey OR Billing adopting Dimension with explicit mapping — decision recorded in this feature's implementation notes). Both repos must serialize/deserialize the same JSON property name.

Why it matters: Billing suspend-on-quota and downstream subscription reactions require the meter identifier to select the correct subscription line or feature gate. Empty MeterKey causes reactions to no-op or fault.

Acceptance criteria (testable):

  • AC-1: Integration contract test serializes a sample quota-exceeded payload from Metering and deserializes into Billing inbound DTO with the meter identifier populated.
  • AC-2: Canonical JSON property name is documented in saas-cross-repo-published-language.md quota-exceeded event schema.
  • AC-3: No duplicate conflicting properties (Dimension and MeterKey) exist on the wire format after alignment.
  • AC-4: DefaultSubscriptionsProcessor / quota reaction saga receives non-empty meter key in unit test with realistic payload fixture.

Implementation notes (full):

  • Preferred approach: rename Metering QuotaExceededIntegrationEvent.DimensionMeterKey (aligns with Billing and published-language glossary term "meter key").
  • Alternate: keep Metering property, add MassTransit consume pipe or [JsonPropertyName] mapping on Billing inbound — only if rename breaks external consumers (none expected in template program).
  • Files: Metering event DTO, Billing inbound DTO, QuotaExceededReactionInput.cs, saga mapping code, JSON descriptor snippets if present.
  • Coordinate with saas-MET-F04 (Metering repo feature) and saas-BIL-F02 (Billing consumer wiring).

Out of scope:

  • Topic name correction — owned by saas-INTEG-F01.
  • Quota limit calculation semantics — Metering domain logic.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-S02.1 — Align quota-exceeded payload field names across Metering and Billing

Type: User Story
Parent: saas-INTEG-F02
Implementation order: 002
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0
Priority: P0
Effort: M
Dependencies: saas-INTEG-F01
Blocks: saas-INTEG-T02.1.1, saas-INTEG-T02.1.2
Source gap analysis: Quota payload alignment

Description (full):

As a Billing developer handling quota-exceeded reactions, I need the meter identifier from Metering events to deserialize correctly into my inbound DTO, so suspend-on-quota logic can target the right usage dimension without custom mapping hacks.

Acceptance criteria (testable):

  • AC-1: Round-trip JSON test: Metering publish shape → Billing consume shape preserves meter identifier value "api-calls".
  • AC-2: Billing saga MeteringQuotaExceededBillingReactionStateMachine maps inbound event to QuotaExceededReactionInput with populated meter key field.

Implementation notes (full):

  • Document chosen field name in published language appendix B (envelope + payload fields).
  • Update processor logging to include meter key for traceability.

Out of scope:

  • Adding new quota metadata fields (counter value, quota limit) to Billing inbound DTO unless required by saga — can be follow-up.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T02.1.1 — Rename Metering QuotaExceededIntegrationEvent.Dimension to MeterKey

Type: Task
Parent: saas-INTEG-S02.1
Implementation order: 002
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, metering
Priority: P0
Effort: S
Dependencies: saas-INTEG-F01
Blocks: saas-INTEG-T02.1.2
Source gap analysis: Quota payload alignment

Description (full):

Rename property Dimension to MeterKey on QuotaExceededIntegrationEvent. Update all Metering publishers, tests, and processor code that reference Dimension when building quota-exceeded events. Update XML documentation to state this is the canonical cross-repo meter identifier.

Acceptance criteria (testable):

  • AC-1: QuotaExceededIntegrationEvent exposes MeterKey property with [Required] validation.
  • AC-2: Metering unit tests and any saga tests referencing the old property name are updated and pass.

Implementation notes (full):

  • File: ConnectSoft.Saas.Metering.MessagingModel/Events/QuotaExceededIntegrationEvent.cs.
  • Search repo for .Dimension on quota event construction paths.

Out of scope:

  • Billing-side changes (next task).

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T02.1.2 — Verify Billing inbound deserialization and saga mapping

Type: Task
Parent: saas-INTEG-S02.1
Implementation order: 002
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, billing
Priority: P0
Effort: S
Dependencies: saas-INTEG-T02.1.1
Blocks: saas-BIL-F02
Source gap analysis: Quota payload alignment

Description (full):

Confirm MeteringQuotaExceededInboundIntegrationEvent.MeterKey receives deserialized values from aligned Metering payload. Update saga state machine mapping to QuotaExceededReactionInput. Add contract test fixture shared or duplicated between Metering.MessagingModel and Billing.MessagingModel test projects.

Acceptance criteria (testable):

  • AC-1: Contract test passes with sample JSON containing "meterKey": "storage-gb".
  • AC-2: Saga unit test asserts non-empty meter key reaches DefaultSubscriptionsProcessor quota reaction handler.

Implementation notes (full):

  • Files: MeteringQuotaExceededInboundIntegrationEvent.cs, MeteringQuotaExceededBillingReactionStateMachine.cs, QuotaExceededReactionInput.cs.

Out of scope:

  • Emitting subscription.suspended-on-quota outbound event — saas-BIL-F06.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

[003] saas-INTEG-F03 — Common integration event envelope

Type: Feature
Parent: saas-EPIC-INTEG
Implementation order: 003
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, envelope
Priority: P0
Effort: L
Dependencies: saas-INTEG-F01
Blocks: saas-TEN-F05, saas-BIL-F02
Source gap analysis: Integration event envelope

Description (full):

Published language mandates a common integration event envelope with fields: tenantId, aggregateId, aggregateVersion, schemaVersion, correlationId, causationId (optional), occurredOn. Current event DTOs across repos implement subsets — typically TenantId, aggregate-specific id (TenantAggregateId, UsageMeterId, etc.), AggregateVersion, and OccurredOn — but omit schemaVersion, correlationId, and causationId. Aggregate id naming is inconsistent (TenantAggregateId vs canonical aggregateId JSON name).

Example gap: TenantCreatedEvent in Tenants has TenantAggregateId but no SchemaVersion, CorrelationId, or CausationId (ConnectSoft.Saas.Tenants.MessagingModel/Events/TenantCreatedEvent.cs).

Target state: Shared base type or interface in ConnectSoft.Extensions (or per-repo MessagingModel copy until shared package ships) that all outbound integration events implement. Processors populate correlation from HTTP/gRPC trace context or saga correlation id. schemaVersion starts at 1 and increments only on breaking payload changes.

Why it matters: Without envelope fields, cross-service tracing, idempotent redelivery, and schema evolution governance fail. Downstream sagas cannot correlate tenant lifecycle to billing provisioning steps.

Acceptance criteria (testable):

  • AC-1: Envelope field list in saas-gap-deep-analysis.md appendix B matches implemented properties on all outbound integration events in five repos.
  • AC-2: Sample published event JSON from Tenants, Catalog, Entitlements, Billing, Metering includes schemaVersion, correlationId, and aggregateId (canonical JSON names documented).
  • AC-3: MassTransit publish pipe or processor base class sets CorrelationId from Activity.Current or explicit saga correlation when absent.
  • AC-4: Architecture test fails if any new outbound event type omits required envelope properties.

Implementation notes (full):

  • Introduce ISaasIntegrationEventEnvelope or extend ConnectSoft.Extensions.MessagingModel.IEvent with default envelope properties.
  • Files: all MessagingModel/Events/*.cs outbound types; processor publish helpers in Default*Processor.cs.
  • JSON naming: use camelCase serialization consistent with MassTransit default; document aggregateId mapping from repo-specific PK properties via [JsonPropertyName("aggregateId")].
  • Per-repo adoption tracked in saas-TEN-F05, saas-CAT-*, etc.; this feature owns the cross-repo contract and shared helper.

Out of scope:

  • Changing business payload fields beyond envelope wrapper.
  • CloudEvents full spec compliance — only published-language subset.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-S03.1 — Define and roll out common integration event envelope across SaaS repos

Type: User Story
Parent: saas-INTEG-F03
Implementation order: 003
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0
Priority: P0
Effort: L
Dependencies: saas-INTEG-F01
Blocks: saas-INTEG-T03.1.1, saas-INTEG-T03.1.2
Source gap analysis: Integration event envelope

Description (full):

As a platform operator debugging cross-service flows, I need every integration event to carry correlation and schema version metadata, so I can trace tenant activation from Tenants through Billing and Entitlements in logs and APM tools.

Acceptance criteria (testable):

  • AC-1: Envelope interface/type published and referenced by at least Tenants and Billing outbound/inbound events as pilot.
  • AC-2: Correlation id on tenant-activated event matches originating CreateDraft/Activate HTTP request trace id in acceptance test.

Implementation notes (full):

  • Start with Tenants outbound events (5 topics) as reference implementation; other repos follow in parallel repo features.
  • Update saas-cross-repo-published-language.md envelope section to match code.

Out of scope:

  • Inbound ACL DTO envelope on Billing — may only require subset; document explicitly.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T03.1.1 — Author shared envelope base type and publish helper

Type: Task
Parent: saas-INTEG-S03.1
Implementation order: 003
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0
Priority: P0
Effort: M
Dependencies: saas-INTEG-F01
Blocks: saas-INTEG-T03.1.2
Source gap analysis: Integration event envelope

Description (full):

Create shared envelope abstraction (in ConnectSoft.Extensions.Saas or documented copy pattern in base-template) with properties: SchemaVersion (int, default 1), CorrelationId (Guid), CausationId (Guid?), OccurredOn (DateTime UTC), AggregateVersion (long), canonical AggregateId (Guid or string per aggregate). Provide IntegrationEventPublishHelper.EnrichFromContext() used by processors before IPublishEndpoint.Publish.

Acceptance criteria (testable):

  • AC-1: Helper sets SchemaVersion = 1 and non-empty CorrelationId when publishing from processor unit test.
  • AC-2: XML docs on base type list all mandatory fields matching appendix B of deep analysis.

Implementation notes (full):

  • Prefer adding to ConnectSoft.Extensions.Saas.Abstractions if package already referenced by all MessagingModel projects.
  • Fallback: duplicate minimal interface in each repo MessagingModel with ADR noting eventual package extraction.

Out of scope:

  • Migrating all five repos in this task — pilot in next task.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T03.1.2 — Apply envelope to Tenants outbound events and add contract test

Type: Task
Parent: saas-INTEG-S03.1
Implementation order: 003
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, tenants
Priority: P0
Effort: M
Dependencies: saas-INTEG-T03.1.1
Blocks: saas-TEN-F05
Source gap analysis: Integration event envelope

Description (full):

Update all five Tenants outbound event types (TenantCreatedEvent, TenantActivatedEvent, etc.) to implement envelope fields. Wire DefaultTenantsProcessor (or equivalent publish path) to call enrich helper. Add serialization snapshot test asserting JSON contains schemaVersion, correlationId, aggregateId.

Acceptance criteria (testable):

  • AC-1: All five events in TenantsConstants.EventTopics have envelope fields populated in unit test publish verification.
  • AC-2: Breaking change test fails if SchemaVersion property removed from any event class.

Implementation notes (full):

  • Files: ConnectSoft.Saas.Tenants.MessagingModel/Events/*.cs, DefaultTenantsProcessor.cs or publish site.
  • Coordinate naming: tenant-deleted event vs blueprint Decommissioned vocabulary — defer to saas-TEN-F05.

Out of scope:

  • Billing inbound envelope parsing — follow-up once outbound stable.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

[004] saas-INTEG-F04 — Redis inbox dedupe

Type: Feature
Parent: saas-EPIC-INTEG
Implementation order: 004
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, redis, idempotency
Priority: P0
Effort: L
Dependencies: saas-INTEG-F03
Blocks: saas-BIL-F02, saas-ENT-F03
Source gap analysis: Redis inbox dedupe

Description (full):

Base-template provides Redis distributed cache infrastructure (DistributedCacheRedisConnectionStringKey in each repo's constants). Processor-level idempotency patterns exist in some services, but uniform inbound (sourceContext, eventId) dedupe is not enforced across all MassTransit consumer sagas. Redelivered integration events can double-apply side effects (duplicate subscription rows, double suspend commands).

Target state: Shared inbox dedupe filter or middleware applied to all inbound integration consumers. Key format: saas:inbox:{sourceContext}:{messageId} with TTL aligned to broker redelivery window. Dedupe check occurs before saga/processor mutation; successful handling records inbox entry in same Redis transaction semantics as cache write.

Why it matters: At-least-once delivery is assumed on RabbitMQ/ASB. Without inbox dedupe, E2E demos and production deployments risk duplicate writes under network retries.

Acceptance criteria (testable):

  • AC-1: Documented inbox key convention added to saas-cross-repo-published-language.md.
  • AC-2: Billing, Entitlements, and Metering each apply dedupe to all inbound integration consumers (minimum three repos with sagas).
  • AC-3: Integration test publishes same MessageId twice; processor mutation occurs exactly once (counter or DB row count assertion).
  • AC-4: Dedupe failures (Redis unavailable) follow documented fail-open vs fail-closed policy — default fail-closed for financial paths (Billing), documented per repo.

Implementation notes (full):

  • Files: ConnectSoft.Extensions or base-template Redis helper; per-repo ApplicationModel/MassTransitExtensions.cs consumer pipe configuration.
  • Reference: TenantsConstants.DistributedCacheRedisConnectionStringKey pattern in each repo.
  • Implement IInboxDedupeStore interface testable with in-memory fake for unit tests.

Out of scope:

  • Outbox dedupe (publish side) — saas-INTEG-F05.
  • NHibernate session-level idempotency keys on domain commands — repo-specific.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-S04.1 — Enforce uniform Redis inbox dedupe on inbound integration consumers

Type: User Story
Parent: saas-INTEG-F04
Implementation order: 004
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0
Priority: P0
Effort: L
Dependencies: saas-INTEG-F03
Blocks: saas-INTEG-T04.1.1, saas-INTEG-T04.1.2
Source gap analysis: Redis inbox dedupe

Description (full):

As a reliability engineer, I need every inbound saga to reject duplicate message deliveries using a shared Redis inbox pattern, so retried events do not create duplicate subscriptions or entitlement assignments.

Acceptance criteria (testable):

  • AC-1: Duplicate delivery test passes for Billing tenant-activated consumer.
  • AC-2: Inbox key includes source context constant (e.g., tenants, metering) and MassTransit MessageId.

Implementation notes (full):

  • Roll out repo-by-repo starting with Billing (highest side-effect risk).
  • Log dedupe skips at Information level with correlation id.

Out of scope:

  • Cross-region Redis replication semantics.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T04.1.1 — Implement IInboxDedupeStore and MassTransit consumer filter

Type: Task
Parent: saas-INTEG-S04.1
Implementation order: 004
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0
Priority: P0
Effort: M
Dependencies: saas-INTEG-F03
Blocks: saas-INTEG-T04.1.2
Source gap analysis: Redis inbox dedupe

Description (full):

Implement Redis-backed IInboxDedupeStore with TryMarkProcessed(sourceContext, messageId) returning false if already seen. Register MassTransit UseConsumeFilter or equivalent pipe that short-circuits duplicate consumes before saga entry. Package in base-template for reuse across all five SaaS templates.

Acceptance criteria (testable):

  • AC-1: Unit tests with fake store verify second TryMarkProcessed returns false.
  • AC-2: Filter registered in base-template sample and documented in baseline checklist.

Implementation notes (full):

  • TTL default: 7 days (configurable via options).
  • Use StackExchange.Redis SET NX pattern.

Out of scope:

  • Per-repo registration (next task).

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T04.1.2 — Register inbox dedupe on Billing, Entitlements, Metering consumers

Type: Task
Parent: saas-INTEG-S04.1
Implementation order: 004
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0
Priority: P0
Effort: M
Dependencies: saas-INTEG-T04.1.1
Blocks:
Source gap analysis: Redis inbox dedupe

Description (full):

Wire inbox dedupe filter into MassTransit bus configuration for all inbound sagas in Billing (FlowModel.MassTransit/*), Entitlements (ProductCatalogReactionStateMachine, etc.), and Metering inbound state machines. Add integration tests per repo verifying duplicate suppression.

Acceptance criteria (testable):

  • AC-1: Each repo's MassTransitExtensions.cs calls inbox dedupe registration for consumer endpoints.
  • AC-2: At least one integration test per repo demonstrates idempotent consume behavior.

Implementation notes (full):

  • Billing files: all *ReactionStateMachine.cs under ConnectSoft.Saas.Billing.FlowModel.MassTransit.
  • Source context strings: tenants, entitlements, catalog, metering — constants in each repo.

Out of scope:

  • Tenants outbound-only repo (no inbound integration sagas in wave 1).

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

[005] saas-INTEG-F05 — MassTransit outbox + durable saga persistence

Type: Feature
Parent: saas-EPIC-INTEG
Implementation order: 005
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, masstransit, outbox
Priority: P0
Effort: L
Dependencies: saas-INTEG-F04
Blocks: saas-TEN-F03, saas-CAT-F05
Source gap analysis: MassTransit outbox + saga durability

Description (full):

Published language mandates MassTransit built-in outbox for reliable publish-after-commit. Tenants currently configures saga persistence with .InMemoryRepository() and acceptance-test bus with UsingInMemory + UseInMemoryOutbox (ConnectSoft.Saas.Tenants.ApplicationModel/MassTransitExtensions.cs lines 37, 59, 64). Saga state for TenantLifecycleSaga is lost on process restart; outbox does not survive production broker restarts.

Target state: Production MassTransit configuration uses durable saga repository (NHibernate, Entity Framework, or Mongo — match repo persistence stack) and transactional outbox backed by same database as NHibernate session where applicable. Acceptance tests may retain in-memory bus with explicit #if DEBUG guard but production path must be durable.

Why it matters: Tenant lifecycle orchestration and cross-service publishes must be atomic with database commits. In-memory saga loses correlation state mid-lifecycle (create acknowledged but activate never routed).

Acceptance criteria (testable):

  • AC-1: Tenants MassTransitExtensions production registration removes .InMemoryRepository() in favor of NHibernate/EF saga repository wired to tenants database.
  • AC-2: UseInMemoryOutbox replaced with durable outbox (MassTransit.EntityFrameworkCore or NHibernate outbox per ADR) on production code path.
  • AC-3: Restart test: kill process mid-saga; on restart saga state resumes from durable store.
  • AC-4: Documented exception for Products Catalog NHibernate outbox deferred work references ADR-CAT-003 without blocking Tenants durability.

Implementation notes (full):

  • Files: ConnectSoft.Saas.Tenants.ApplicationModel/MassTransitExtensions.cs, TenantLifecycleSaga.cs, TenantLifecycleSagaState.cs.
  • Pattern reference: MassTransit docs for NHibernate saga + outbox; base-template ConnectSoft.BaseTemplate.ApplicationModel/MassTransitExtensions.cs.
  • Products Catalog ADR-0003 deferred NHibernate outbox tracked separately in saas-CAT-F05.

Out of scope:

  • Full multi-step TenantLifecycleSaga behavior — saas-TEN-F03.
  • RabbitMQ vs ASB transport selection — environment configuration.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-S05.1 — Replace in-memory MassTransit saga and outbox with durable persistence

Type: User Story
Parent: saas-INTEG-F05
Implementation order: 005
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, tenants
Priority: P0
Effort: L
Dependencies: saas-INTEG-F04
Blocks: saas-INTEG-T05.1.1, saas-INTEG-T05.1.2
Source gap analysis: MassTransit outbox + saga durability

Description (full):

As a Tenants service operator, I need saga state and outbound messages persisted durably, so tenant lifecycle orchestration survives restarts and matches published-language reliability rules.

Acceptance criteria (testable):

  • AC-1: Production configuration uses non-in-memory saga repository.
  • AC-2: Outbox defers publish until NHibernate transaction commits in processor-driven flow test.

Implementation notes (full):

  • Pilot on Tenants; document rollout checklist for other repos with sagas (Billing reactions, Entitlements, Metering).

Out of scope:

  • Catalog publish-only outbox (no inbound sagas).

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T05.1.1 — Configure durable saga repository for TenantLifecycleSaga

Type: Task
Parent: saas-INTEG-S05.1
Implementation order: 005
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, tenants
Priority: P0
Effort: M
Dependencies: saas-INTEG-F04
Blocks: saas-INTEG-T05.1.2
Source gap analysis: MassTransit outbox + saga durability

Description (full):

Replace .InMemoryRepository() saga registration with NHibernate-backed (or EF) ISagaRepository<TenantLifecycleSagaState> sharing connection with tenants ORM session. Add migration/table for saga state if required by MassTransit NHibernate integration package.

Acceptance criteria (testable):

  • AC-1: TenantLifecycleSagaState rows persist in database after TenantCreatedEvent consumed.
  • AC-2: No .InMemoryRepository() call remains in non-test production registration path.

Implementation notes (full):

  • File: MassTransitExtensions.cs — saga config section near line 37.
  • Entity: TenantLifecycleSagaState.cs — verify correlation id mapping.

Out of scope:

  • Expanding saga states beyond create-only stub.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T05.1.2 — Enable transactional outbox on Tenants publish path

Type: Task
Parent: saas-INTEG-S05.1
Implementation order: 005
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, tenants
Priority: P0
Effort: M
Dependencies: saas-INTEG-T05.1.1
Blocks: saas-TEN-F03
Source gap analysis: MassTransit outbox + saga durability

Description (full):

Replace UseInMemoryOutbox on production bus with durable outbox integrated into NHibernate unit of work. Ensure DefaultTenantsProcessor publish calls occur within outbox scope so events emit only after commit.

Acceptance criteria (testable):

  • AC-1: Integration test rolls back transaction — no message visible on bus.
  • AC-2: Successful commit — message delivered exactly once after outbox dispatch.

Implementation notes (full):

  • Coordinate with NHibernate session factory registration in ApplicationModel.
  • Keep in-memory outbox only under acceptance-test host flag (CONNECTSOFT_ACCEPTANCE_TEST_HOST).

Out of scope:

  • Billing/Entitlements outbox rollout — separate repo features after Tenants pilot.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

[006] saas-INTEG-F06 — Orleans write-path ADR + decision

Type: Feature
Parent: saas-EPIC-INTEG
Implementation order: 006
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, orleans, adr
Priority: P0
Effort: M
Dependencies: saas-INTEG-F05
Blocks: saas-TEN-F08, saas-CAT-F01, saas-ENT-F07, saas-BIL-F10, saas-MET-F05
Source gap analysis: Orleans write-path

Description (full):

All five SaaS template repos register Orleans silo, define grains implementing actor interfaces (ITenantLifecycleActor / TenantLifecycleGrain, IProductEditorGrain, etc.), and implement processors (Default*Processor) that contain actual business logic. REST/gRPC adapters and ServiceModel implementations call processors directly — no GetGrain usage on write paths.

Tenants evidence: GrpcTenantManagementService.cs and TenantsController.cs invoke ITenantsProcessor; grain exists at TenantEditorGrain.cs (TenantLifecycleGrain) but is bypassed.

Decision required (ADR-INTEG-001):

  • Option A — Wire grains: All write operations route through Orleans grains; grains delegate to processors (actor as concurrency boundary).
  • Option B — Processor-only + ADR: Formally document bypass; grains retained for future scaling or removed from write path registration.

Target state: Accepted ADR published in each repo's docs/adr/ (or central CompanyDocumentation) with explicit chosen option. If Option A, per-repo saas-*-F* grain wiring features proceed. If Option B, architecture tests updated to assert documented bypass and grains marked experimental/read-only.

Why it matters: Without a decision, parallel implementations diverge — some repos wire grains, others bypass — breaking operational assumptions in blueprint and baseline checklist.

Acceptance criteria (testable):

  • AC-1: ADR-INTEG-001 authored with status Accepted and chosen option recorded.
  • AC-2: ADR lists impact on all five repos with owner feature IDs (saas-TEN-F08, saas-CAT-F01, saas-ENT-F07, saas-BIL-F10, saas-MET-F05).
  • AC-3: Baseline checklist and saas-platform-ddd-blueprint.md cross-reference updated to match decision (no contradictory "grains required on write path" without caveat).
  • AC-4: Program backlog items for grain wiring marked Blocked or Not Started based on ADR outcome.

Implementation notes (full):

  • ADR location: ConnectSoft.CompanyDocumentation/docs/product-portfolio/platforms/saas-solution-platform/adr/ADR-INTEG-001-orleans-write-path.md plus stub mirrors in each template docs/adr/.
  • Review stakeholders: platform architecture, template maintainers.
  • If Option A: define standard pattern ServiceModel → IGrainFactory → Grain → IProcessor.
  • If Option B: update OneAggregateRootPerRepoTests and actor registration docs.

Out of scope:

  • Actual grain wiring code — per-repo features depend on this ADR outcome.
  • Orleans silo hosting configuration changes.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-S06.1 — Decide and document Orleans write-path strategy for SaaS templates

Type: User Story
Parent: saas-INTEG-F06
Implementation order: 006
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, orleans, adr
Priority: P0
Effort: M
Dependencies: saas-INTEG-F05
Blocks: saas-INTEG-T06.1.1, saas-INTEG-T06.1.2
Source gap analysis: Orleans write-path

Description (full):

As a template program architect, I need a single accepted ADR on whether API writes must route through Orleans grains, so all five bounded contexts implement the same actor model consistently.

Acceptance criteria (testable):

  • AC-1: ADR-INTEG-001 merged with Accepted status and option A or B selected.
  • AC-2: Deep analysis §3.6 and strategic decisions table updated to reflect outcome (no longer Pending).

Implementation notes (full):

  • Include cost/benefit: grain routing adds latency vs single-node processor simplicity in templates.
  • Reference Catalog grain tests (ProductEditorGrainSiloTests.cs) as partial Option A evidence.

Out of scope:

  • Implementing chosen option in code.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T06.1.1 — Draft ADR-INTEG-001 with options analysis

Type: Task
Parent: saas-INTEG-S06.1
Implementation order: 006
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, adr
Priority: P0
Effort: S
Dependencies: saas-INTEG-F05
Blocks: saas-INTEG-T06.1.2
Source gap analysis: Orleans write-path

Description (full):

Author ADR-INTEG-001 documenting current evidence (five repos bypass grains on write path), Option A (wire grains) and Option B (processor-only with formal bypass), consequences, and recommended option for wave-1 templates. Include code citations: Tenants TenantEditorGrain.cs, ITenantManagementService → processor path.

Acceptance criteria (testable):

  • AC-1: ADR file exists with sections Context, Decision, Consequences, References.
  • AC-2: All five repo grain types listed with file paths.

Implementation notes (full):

  • Path: CompanyDocumentation ADR folder under saas-solution-platform.
  • Link to saas-platform-ddd-blueprint.md actor collaboration section.

Out of scope:

  • Final acceptance sign-off (next task).

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T06.1.2 — Accept ADR and update dependent backlog items

Type: Task
Parent: saas-INTEG-S06.1
Implementation order: 006
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, adr
Priority: P0
Effort: S
Dependencies: saas-INTEG-T06.1.1
Blocks: saas-TEN-F08
Source gap analysis: Orleans write-path

Description (full):

Review ADR with program owners; mark Accepted; propagate decision to deep analysis decision log, baseline checklist, and per-repo grain wiring features (saas-TEN-F08, saas-CAT-F01, saas-ENT-F07, saas-BIL-F10, saas-MET-F05). If Option B chosen, rescope grain wiring features to "verify bypass documented" and cancel wiring tasks.

Acceptance criteria (testable):

  • AC-1: ADR status field reads Accepted with date.
  • AC-2: Each dependent feature's Dependencies field references ADR-INTEG-001 outcome explicitly.

Implementation notes (full):

  • Update saas-gap-deep-analysis.md §5 decision log row ADR-INTEG-001 from Pending to Accepted.
  • Mirror ADR stub into each template docs/adr/.

Out of scope:

  • Code changes implementing decision.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

[007] saas-INTEG-F07 — MessagingModel NuGet pin policy

Type: Feature
Parent: saas-EPIC-INTEG
Implementation order: 007
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, nuget, governance
Priority: P0
Effort: M
Dependencies: saas-INTEG-F03
Blocks: saas-ENT-F08
Source gap analysis: MessagingModel NuGet pin policy

Description (full):

Entitlements references ConnectSoft.Saas.ProductsCatalog.MessagingModel NuGet package for catalog integration event types consumed by inbound sagas. Version pin strategy across repos is not documented or CI-enforced. Silent package drift causes runtime deserialization mismatches when Catalog publishes new event shapes Entitlements has not upgraded to consume.

Target state: Documented cross-repo MessagingModel versioning policy: semver rules, allowed lag (consumer may be N versions behind publisher), CI check that fails when referenced MessagingModel version diverges from documented matrix, and process for coordinated breaking releases (schemaVersion bump + package major).

Why it matters: Cross-repo event contracts are NuGet-delivered ACL surfaces. Unpinned references break E2E demos when templates restore latest prerelease independently.

Acceptance criteria (testable):

  • AC-1: Policy document section added to saas-cross-repo-published-language.md or baseline checklist covering MessagingModel pin rules.
  • AC-2: Version matrix file checked into CompanyDocumentation listing each consumer → producer MessagingModel package with pinned version.
  • AC-3: CI script or architecture test in Entitlements fails when ConnectSoft.Saas.ProductsCatalog.MessagingModel version differs from matrix.
  • AC-4: Entitlements .csproj PackageReference aligned to matrix after policy lands (implementation detail in saas-ENT-F08).

Implementation notes (full):

  • Primary consumer evidence: Entitlements FlowModel.MassTransit catalog reaction sagas.
  • Consider central Directory.Packages.props pattern for template program monorepo demos.
  • Coordinate with envelope schemaVersion from saas-INTEG-F03.

Out of scope:

  • Publishing MessagingModel packages to NuGet.org — internal feed assumed.
  • Non-Entitlements cross-references (Billing inbound DTOs are local ACL copies by design).

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-S07.1 — Establish and enforce cross-repo MessagingModel version pin policy

Type: User Story
Parent: saas-INTEG-F07
Implementation order: 007
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, governance
Priority: P0
Effort: M
Dependencies: saas-INTEG-F03
Blocks: saas-INTEG-T07.1.1, saas-INTEG-T07.1.2
Source gap analysis: MessagingModel NuGet pin policy

Description (full):

As an Entitlements maintainer consuming Catalog events via NuGet, I need a documented and CI-enforced MessagingModel version pin, so my service does not silently drift from the event contracts Catalog publishes.

Acceptance criteria (testable):

  • AC-1: Version matrix lists Entitlements → ProductsCatalog.MessagingModel with exact version.
  • AC-2: CI validation script runs in Entitlements pipeline and passes on aligned version.

Implementation notes (full):

  • Extend placeholder CrossRepoPublishedLanguageTests in Entitlements once policy exists.

Out of scope:

  • Automating package publish pipeline.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T07.1.1 — Author MessagingModel version matrix and policy document

Type: Task
Parent: saas-INTEG-S07.1
Implementation order: 007
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, docs
Priority: P0
Effort: S
Dependencies: saas-INTEG-F03
Blocks: saas-INTEG-T07.1.2
Source gap analysis: MessagingModel NuGet pin policy

Description (full):

Create messaging-model-version-matrix.md under saas-solution-platform documenting each cross-repo PackageReference, pinned version, owner repo, and upgrade procedure. Add policy summary to published language or baseline checklist: when to bump minor vs major, coordination with schemaVersion, and PR checklist item.

Acceptance criteria (testable):

  • AC-1: Matrix includes Entitlements → ProductsCatalog.MessagingModel row with current csproj version.
  • AC-2: Policy defines maximum allowed version skew (recommend 0 for wave-1 templates).

Implementation notes (full):

  • Read version from ConnectSoft.Saas.EntitlementsTemplate csproj PackageReference.
  • Link from deep analysis finding X-008.

Out of scope:

  • Matrix entries for repos with local ACL copies only.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

saas-INTEG-T07.1.2 — Add CI/architecture test enforcing matrix compliance

Type: Task
Parent: saas-INTEG-S07.1
Implementation order: 007
Status: Not Started
Area path: ConnectSoft\SaaS\Integration
Iteration: TBD
Tags: saas-platform, gap, integration, p0, testing
Priority: P0
Effort: S
Dependencies: saas-INTEG-T07.1.1
Blocks: saas-ENT-F08
Source gap analysis: MessagingModel NuGet pin policy

Description (full):

Implement test or build script VerifyMessagingModelPins that parses consumer csproj PackageReference versions and compares to version matrix file. Integrate into Entitlements ArchitectureTests or Azure DevOps pipeline template shared across SaaS repos.

Acceptance criteria (testable):

  • AC-1: Test fails when Entitlements csproj bumps Catalog.MessagingModel without matrix update.
  • AC-2: Test passes on current aligned versions.

Implementation notes (full):

  • Place test in ConnectSoft.Saas.Entitlements.ArchitectureTests.
  • Optional: genericize for future Billing → Metering references if introduced.

Out of scope:

  • Renovate/Dependabot automation.

Definition of done:

  • All AC pass
  • Tests added/updated and green
  • Docs updated
  • Status updated in master + mirrors

Implementation ordering (P0 → P1 → P2)

P0 — E2E demo blockers (this epic)

Execute in global [NNN] order:

  1. [001] saas-INTEG-F01 — Cross-repo integration topic harmonization
  2. [002] saas-INTEG-F02 — Quota payload Dimension to MeterKey alignment
  3. [003] saas-INTEG-F03 — Common integration event envelope
  4. [004] saas-INTEG-F04 — Redis inbox dedupe
  5. [005] saas-INTEG-F05 — MassTransit outbox + durable saga persistence
  6. [006] saas-INTEG-F06 — Orleans write-path ADR + decision
  7. [007] saas-INTEG-F07 — MessagingModel NuGet pin policy

P1 — Unblocked after INTEG P0 (per-repo execution)

  • saas-BIL-F02, saas-MET-F04 — direct consumers of F01/F02
  • saas-TEN-F05 — Tenants envelope adoption (depends on F03)
  • saas-TEN-F03 — lifecycle saga expansion (depends on F05 durable saga)
  • saas-TEN-F08, saas-CAT-F01, saas-ENT-F07, saas-BIL-F10, saas-MET-F05 — grain wiring (depends on F06 ADR)
  • saas-ENT-F08 — Catalog MessagingModel pin bump (depends on F07)

P2 — Enhancements after core path works

  • saas-MET-F08 — optional Entitlements reaction to metering.quota events
  • saas-DOCS-F01..F10 — ongoing governance and nav updates
  • Query extensions and deferred ADR items (invoice projection, payment ACL, structural catalog APIs)