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.TenantActivatedequalstenants.domain.v1.tenant-activatedand matchesTenantsConstants.EventTopics.TenantActivated. - AC-2:
BillingConstants.InboundEventTopics.MeteringQuotaExceededequalsmetering.quota.v1.quota-exceededand matchesMeteringConstants.EventTopics.QuotaExceeded. - AC-3: All MassTransit topology files (
*MassTransitTopology.cs,FlowModel.MassTransitstate 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.jsondocuments the six inbound topics with canonical names oncesaas-BIL-F02consumed-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.csendpoint 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.mdtopic 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 (
DimensionvsMeterKey) — owned bysaas-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
TenantActivatedEventonTenantsConstants.EventTopics.TenantActivated, when Billing MassTransit host starts, then the tenant-activated consumer endpoint binds to the identical entity name. - AC-2: Given Metering publishes
QuotaExceededIntegrationEventonMeteringConstants.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.BillingTemplatereturns zero occurrences ofsaas.tenants.v1.tenant-activatedandmetering.quotas.v1.quota-exceeded.
Implementation notes (full):
- Primary file:
BillingConstants.cslines 65–84 (InboundEventTopicsnested 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.TenantActivatedandMeteringConstants.EventTopics.QuotaExceededcharacter-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:
MeteringQuotaExceededBillingReactionStateMachineand tenant-activated reaction state machines resolve endpoints via corrected constants. - AC-2: New or updated test in
ConnectSoft.Saas.Billing.ArchitectureTestsfails 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.csif 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— propertyDimension. - Consumer:
ConnectSoft.Saas.BillingTemplate/src/ConnectSoft.Saas.Billing.MessagingModel/Inbound/MeteringQuotaExceededInboundIntegrationEvent.cs— propertyMeterKey.
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 Dimension → MeterKey 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.mdquota-exceeded event schema. - AC-3: No duplicate conflicting properties (
DimensionandMeterKey) 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.Dimension→MeterKey(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) andsaas-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
MeteringQuotaExceededBillingReactionStateMachinemaps inbound event toQuotaExceededReactionInputwith 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:
QuotaExceededIntegrationEventexposesMeterKeyproperty 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
.Dimensionon 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
DefaultSubscriptionsProcessorquota reaction handler.
Implementation notes (full):
- Files:
MeteringQuotaExceededInboundIntegrationEvent.cs,MeteringQuotaExceededBillingReactionStateMachine.cs,QuotaExceededReactionInput.cs.
Out of scope:
- Emitting
subscription.suspended-on-quotaoutbound 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.mdappendix 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, andaggregateId(canonical JSON names documented). - AC-3: MassTransit publish pipe or processor base class sets
CorrelationIdfromActivity.Currentor explicit saga correlation when absent. - AC-4: Architecture test fails if any new outbound event type omits required envelope properties.
Implementation notes (full):
- Introduce
ISaasIntegrationEventEnvelopeor extendConnectSoft.Extensions.MessagingModel.IEventwith default envelope properties. - Files: all
MessagingModel/Events/*.csoutbound types; processor publish helpers inDefault*Processor.cs. - JSON naming: use camelCase serialization consistent with MassTransit default; document
aggregateIdmapping 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.mdenvelope 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 = 1and non-emptyCorrelationIdwhen 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.Abstractionsif 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.EventTopicshave envelope fields populated in unit test publish verification. - AC-2: Breaking change test fails if
SchemaVersionproperty removed from any event class.
Implementation notes (full):
- Files:
ConnectSoft.Saas.Tenants.MessagingModel/Events/*.cs,DefaultTenantsProcessor.csor publish site. - Coordinate naming:
tenant-deletedevent vs blueprintDecommissionedvocabulary — defer tosaas-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
MessageIdtwice; 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.Extensionsor base-template Redis helper; per-repoApplicationModel/MassTransitExtensions.csconsumer pipe configuration. - Reference:
TenantsConstants.DistributedCacheRedisConnectionStringKeypattern in each repo. - Implement
IInboxDedupeStoreinterface 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 MassTransitMessageId.
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
TryMarkProcessedreturns 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.cscalls 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.csunderConnectSoft.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
MassTransitExtensionsproduction registration removes.InMemoryRepository()in favor of NHibernate/EF saga repository wired to tenants database. - AC-2:
UseInMemoryOutboxreplaced 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:
TenantLifecycleSagaStaterows persist in database afterTenantCreatedEventconsumed. - 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.mdcross-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.mdplus stub mirrors in each templatedocs/adr/. - Review stakeholders: platform architecture, template maintainers.
- If Option A: define standard pattern
ServiceModel → IGrainFactory → Grain → IProcessor. - If Option B: update
OneAggregateRootPerRepoTestsand 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.mdactor 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.mdor 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.MessagingModelversion differs from matrix. - AC-4: Entitlements
.csprojPackageReference aligned to matrix after policy lands (implementation detail insaas-ENT-F08).
Implementation notes (full):
- Primary consumer evidence: Entitlements
FlowModel.MassTransitcatalog reaction sagas. - Consider central
Directory.Packages.propspattern for template program monorepo demos. - Coordinate with envelope
schemaVersionfromsaas-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
CrossRepoPublishedLanguageTestsin 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.EntitlementsTemplatecsproj 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:
[001]saas-INTEG-F01 — Cross-repo integration topic harmonization[002]saas-INTEG-F02 — Quota payload Dimension to MeterKey alignment[003]saas-INTEG-F03 — Common integration event envelope[004]saas-INTEG-F04 — Redis inbox dedupe[005]saas-INTEG-F05 — MassTransit outbox + durable saga persistence[006]saas-INTEG-F06 — Orleans write-path ADR + decision[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)