SaaS Template Program — Deep Gap Analysis
Last verified: 2026-05-27
Program: Gap Implementation (saas-EPIC-*)
Companion backlog: saas-gap-implementation-backlog.md
1. Executive summary
This document is the single source of evidence for the SaaS template gap program. It compares wave-1 implementations in the five ConnectSoft.Saas.*Template repositories against the canonical DDD blueprint, JSON descriptors, cross-repo published language, and baseline checklist.
Rollup by service
| Service |
Implemented |
Partial |
Missing |
Deferred |
Linked Features |
| Cross-cutting (INTEG) |
2 |
4 |
1 |
0 |
saas-INTEG-F01..F07 |
| Tenants |
8 |
9 |
6 |
1 |
saas-TEN-F01..F08 |
| Products Catalog |
14 |
5 |
2 |
1 |
saas-CAT-F01..F07 |
| Entitlements |
10 |
7 |
5 |
1 |
saas-ENT-F01..F08 |
| Billing |
11 |
8 |
7 |
2 |
saas-BIL-F01..F11 |
| Metering |
9 |
8 |
8 |
1 |
saas-MET-F01..F09 |
P0 blockers (E2E integration)
| # |
Finding |
Evidence |
Closes with |
| 1 |
Billing inbound tenant topic uses saas.tenants.v1.tenant-activated instead of canonical tenants.domain.v1.tenant-activated |
BillingConstants.InboundEventTopics.TenantActivated |
saas-INTEG-F01, saas-BIL-F02 |
| 2 |
Billing quota topic uses metering.quotas.v1.quota-exceeded vs canonical metering.quota.v1.quota-exceeded |
BillingConstants.InboundEventTopics.MeteringQuotaExceeded vs MeteringConstants.EventTopics.QuotaExceeded |
saas-INTEG-F01, saas-BIL-F02 |
| 3 |
Quota payload field mismatch: Metering publishes Dimension; Billing expects MeterKey |
QuotaExceededIntegrationEvent.Dimension vs MeteringQuotaExceededInboundIntegrationEvent.MeterKey |
saas-INTEG-F02, saas-MET-F04 |
| 4 |
Integration events lack common envelope fields (schemaVersion, correlationId, causationId) |
Cross-repo published language §Non-negotiable rules vs event DTOs |
saas-INTEG-F03 |
| 5 |
Orleans grains exist but no service routes writes through them |
All five repos: REST/gRPC → Default*Processor directly |
saas-INTEG-F06, per-repo F08/F01/F07/F10/F05 |
Strategic decisions still required
| Decision |
Options |
Owner Feature |
| Flat tenant vs rich aggregate |
Accept flat Tenant + ADR, or implement TenantProfile, Contact, etc. |
saas-TEN-F01 |
| Orleans write-path |
Wire grains on all writes, or ADR bypass with processor-only path |
saas-INTEG-F06 |
| Invoice scope |
Minimal read-model projection vs defer (events only) |
saas-BIL-F03 |
| Payment provider ACL |
Skeleton adapter + webhook saga vs defer |
saas-BIL-F04 |
| EffectiveEntitlementDescriptor |
Materialize VO on assign/activate vs summary-only ADR |
saas-ENT-F01 |
| Catalog structural APIs |
SLA/Pricing/BusinessModel write ops vs defer ADR |
saas-CAT-F03 |
2. Methodology
Source documents
| Layer |
Path |
Role |
| Canonical DDD entities |
ConnectSoft.CompanyDocumentation/docs/saas/framework/saas-platform-ddd-entities.md |
Target aggregate shapes |
| DDD blueprint |
ConnectSoft.CompanyDocumentation/docs/saas/framework/saas-platform-ddd-blueprint.md |
Bounded contexts, collaboration |
| Bounded contexts matrix |
ConnectSoft.Documentation/Docs/starters/saas-bounded-contexts-and-templates.md |
Repo ↔ aggregate mapping |
| Cross-repo language |
ConnectSoft.Documentation/Docs/starters/saas-cross-repo-published-language.md |
Topics, envelope, ACL rules |
| Baseline checklist |
ConnectSoft.Documentation/Docs/starters/saas-template-baseline-checklist.md |
Repo compliance |
| JSON descriptors |
ConnectSoft.Saas.*Template/ConnectSoft.Saas.*.json |
Design-time model |
| Code |
ConnectSoft.Saas.*Template/src/** |
Runtime truth |
Classification taxonomy
| Status |
Meaning |
| Implemented |
Code and tests verify the capability matches canonical intent |
| Partial |
Scaffold or subset exists; gaps remain |
| Missing |
Not present in code or tests |
| Deferred |
Explicitly out of scope this wave (ADR or out-of-scope.md) |
Evidence rules
Every finding cites at least one of: source file path + symbol, test class, ADR, or doc anchor. Findings map to one or more saas-*-F## backlog Features.
3. Cross-cutting findings
3.1 Topic harmonization
Status: Partial
Closes with: saas-INTEG-F01, saas-BIL-F02
Canonical topics are defined in saas-cross-repo-published-language.md. Tenants and Products Catalog outbound topics align with constants in TenantsConstants.EventTopics and ProductsCatalogConstants.EventTopics.
Drift evidence:
| Consumer |
Constant |
Value in code |
Canonical |
| Billing |
InboundEventTopics.TenantActivated |
saas.tenants.v1.tenant-activated |
tenants.domain.v1.tenant-activated |
| Billing |
InboundEventTopics.MeteringQuotaExceeded |
metering.quotas.v1.quota-exceeded |
metering.quota.v1.quota-exceeded |
File: ConnectSoft.Saas.BillingTemplate/src/ConnectSoft.Saas.Billing/BillingConstants.cs
3.2 Quota payload alignment
Status: Missing (contract mismatch)
Closes with: saas-INTEG-F02, saas-MET-F04, saas-BIL-F02
Metering publishes QuotaExceededIntegrationEvent with property Dimension. Billing inbound DTO MeteringQuotaExceededInboundIntegrationEvent expects MeterKey. MassTransit deserialization will not map across different property names without explicit mapping or contract change.
Files:
ConnectSoft.Saas.MeteringTemplate/src/ConnectSoft.Saas.Metering.MessagingModel/Events/QuotaExceededIntegrationEvent.cs — Dimension
ConnectSoft.Saas.BillingTemplate/src/ConnectSoft.Saas.Billing.MessagingModel/Inbound/MeteringQuotaExceededInboundIntegrationEvent.cs — MeterKey
3.3 Integration event envelope
Status: Partial
Closes with: saas-INTEG-F03, saas-TEN-F05
Published language requires: tenantId, aggregateId, aggregateVersion, schemaVersion, correlationId, causationId, occurredOn. Current event DTOs implement subsets (typically TenantId, AggregateVersion, OccurredOn) without schemaVersion, correlationId, or causationId.
3.4 Redis inbox dedupe
Status: Partial
Closes with: saas-INTEG-F04
BaseTemplate provides Redis cache keys; saga idempotency patterns exist in processors but cross-repo (sourceContext, eventId) dedupe is not uniformly enforced across all inbound sagas.
3.5 MassTransit outbox + saga durability
Status: Partial
Closes with: saas-INTEG-F05, saas-CAT-F05
Tenants saga uses in-memory repository (TenantLifecycleSaga + MassTransitExtensions). Published language mandates MassTransit built-in outbox. NHibernate transactional outbox deferred per Products Catalog ADR-0003.
3.6 Orleans write-path
Status: Partial (grains implemented, not on API path)
Closes with: saas-INTEG-F06, saas-TEN-F08, saas-CAT-F01, saas-ENT-F07, saas-BIL-F10, saas-MET-F05
All five repos register Orleans, define grains implementing actor interfaces, and delegate to Default*Processor. REST/gRPC adapters call processors directly — no GetGrain usage in write paths.
Evidence (Tenants): GrpcTenantManagementService.cs, TenantsController.cs → ITenantsProcessor; grain at TenantEditorGrain.cs (TenantLifecycleGrain).
3.7 MessagingModel NuGet pin policy
Status: Partial
Closes with: saas-INTEG-F07, saas-ENT-F08
Entitlements references ConnectSoft.Saas.ProductsCatalog.MessagingModel for catalog events. Version pin strategy across repos is not documented or CI-enforced.
4. Per-service deep analysis
4.1 Tenants
Repository: ConnectSoft.Saas.TenantsTemplate
Aggregate root: Tenant
Closes with: saas-TEN-F01..F08
Entity model
| Concept |
Blueprint |
JSON descriptor |
Code |
Status |
Feature |
TenantId (string partition) |
Yes |
TenantId as guid PK |
TenantAggregateId (Guid) + TenantId (string) |
Partial |
saas-TEN-F04 |
TenantProfile VO |
Yes |
— |
— |
Missing |
saas-TEN-F01 |
TenantRegionResidency VO |
Yes |
RegionCode |
ResidencyRegion |
Partial |
saas-TEN-F01, F04 |
Contact entity |
Yes |
— |
— |
Missing |
saas-TEN-F01 |
TenantConfiguration entity |
Yes |
— |
— |
Missing |
saas-TEN-F01 |
TenantKey |
Yes |
— |
Present in entity/migration |
Partial |
saas-TEN-F04 |
AggregateVersion |
Yes |
— |
Present |
Partial |
saas-TEN-F04, F05 |
EditionRef, BillingAccountRef |
Yes |
— |
— |
Missing |
saas-TEN-F02 |
Evidence: ConnectSoft.Saas.Tenants.json, ITenant.cs, docs/adr/0001-one-aggregate-root-per-repo.md
ServiceModel
| Capability |
Status |
Evidence |
ITenantManagementService (5 ops) |
Implemented |
ITenantManagementService.cs |
| REST + gRPC adapters |
Implemented |
TenantsController.cs, GrpcTenantManagementService.cs |
| Query service |
Implemented |
ITenantQueryService.cs |
Actor / Orleans
| Capability |
Status |
Evidence |
ITenantLifecycleActor |
Implemented |
ITenantEditorActor.cs |
TenantLifecycleGrain |
Implemented |
TenantEditorGrain.cs |
| Write path via grain |
Missing |
No GetGrain in ServiceModel/DomainModel.Impl |
| Orleans tests |
Missing |
Only base-template BankAccount examples |
Messaging
| Capability |
Status |
Evidence |
| Outbound topics (5) |
Implemented |
TenantsConstants.EventTopics, TenantsMassTransitTopology.cs |
| Descriptor alignment |
Implemented |
ConnectSoft.Saas.Tenants.json publishedEvents |
| Saga orchestration |
Partial |
TenantLifecycleSaga — create-only stub, no multi-step lifecycle |
| Event envelope fields |
Partial |
Missing schemaVersion, correlationId, causationId |
Tests
| Test area |
Status |
Evidence |
| NetArch layering |
Implemented |
ServiceModelLayeringNetArchTests.cs |
| OneAggregateRootPerRepo |
Missing (placeholder) |
OneAggregateRootPerRepoTests.cs — Assert.IsTrue(true) |
| CrossRepoPublishedLanguage |
Missing (placeholder) |
CrossRepoPublishedLanguageTests.cs |
| Processor/saga coverage |
Missing |
No saga unit tests |
4.2 Products Catalog
Repository: ConnectSoft.Saas.ProductsCatalogTemplate
Aggregate root: Product
Closes with: saas-CAT-F01..F07
Entity model
| Capability |
Status |
Evidence |
Rich IProduct + editions, SLA, features |
Implemented |
IProduct.cs, ProductEntity.cs, JSON descriptor |
| Peer entities (Feature, PricingModel, BusinessModel) |
Implemented |
EntityModel, processor |
| AggregateVersion on events |
Implemented |
MessagingModel events |
| POCO + processor pattern (no domain aggregate class) |
Partial |
By design; entity tests only |
ServiceModel
| Capability |
Status |
Evidence |
| Management + query + catalog services |
Implemented |
ServiceModel, gRPC, REST controllers |
| ListEditions / get-by-id pricing/business |
Partial |
Some query gaps |
Actor / Orleans
| Capability |
Status |
Evidence |
IProductEditorGrain |
Implemented |
ProductEditorGrain.cs |
| Write path via grain |
Missing |
ServiceModel → processor directly |
| Grain tests |
Partial |
ProductEditorGrainSiloTests.cs — CreateProduct only |
Messaging
| Capability |
Status |
Evidence |
| 7 outbound topics |
Implemented |
ProductsCatalogConstants, CatalogMassTransitTopology.cs |
| Publish-only (no inbound sagas) |
Implemented |
By design |
| EntitlementsChanged fan-out on pricing/SLA bindings |
Missing |
When structural APIs ship |
Architecture tests
| Test |
Status |
Evidence |
| OneAggregateRootPerRepo |
Implemented |
Real NetArchTest |
| CrossRepoPublishedLanguage |
Implemented |
Assembly scan |
| Layering, gRPC, repository confinement |
Implemented |
ArchitectureTests project |
4.3 Entitlements
Repository: ConnectSoft.Saas.EntitlementsTemplate
Aggregate root: Entitlement
Closes with: saas-ENT-F01..F08
Entity model
| Concept |
Blueprint |
Code |
Status |
Feature |
IEntitlement + assignments/overrides |
Yes |
Yes |
Implemented |
— |
EffectiveEntitlementDescriptor VO |
Yes |
— |
Missing |
saas-ENT-F01 |
| Suspend / Decommission lifecycle |
Yes |
Partial |
Partial |
saas-ENT-F02 |
ServiceModel
| Capability |
Status |
Evidence |
| Draft/activate/assign/override + query |
Implemented |
ServiceModel, gRPC, REST |
| Catalog integrity on assign |
Missing |
No existence check |
Messaging
| Capability |
Status |
Evidence |
| 3 outbound topics |
Implemented |
EntitlementsConstants, processor publish |
| ProductRetired saga |
Implemented |
ProductCatalogReactionStateMachine.cs |
| ProductCreated/Updated topology without consumers |
Partial |
EntitlementsMassTransitTopology.cs |
| Per-row assignment-changed on catalog retirement |
Partial |
saas-ENT-F03 |
Architecture tests
| Test |
Status |
Evidence |
| Layering, gRPC, repository |
Implemented |
ArchitectureTests |
| OneAggregateRootPerRepo |
Missing (placeholder) |
OneAggregateRootPerRepoTests.cs |
| CrossRepoPublishedLanguage |
Missing (placeholder) |
CrossRepoPublishedLanguageTests.cs |
4.4 Billing
Repository: ConnectSoft.Saas.BillingTemplate
Aggregate root: Subscription
Closes with: saas-BIL-F01..F11
Entity model
| Concept |
Blueprint |
JSON |
Code |
Status |
Feature |
| Subscription lifecycle |
Yes |
Partial |
Yes |
Partial |
saas-BIL-F01 |
| Create → Draft |
Yes |
— |
Creates Active |
Missing |
saas-BIL-F01 |
| Decommissioned vs Canceled |
Yes |
Decommissioned |
Canceled |
Partial |
saas-BIL-F01, F04 |
| Invoice aggregate |
Out of scope |
— |
Events only |
Deferred |
saas-BIL-F03 |
| Payment provider domain |
Out of scope |
— |
— |
Deferred |
saas-BIL-F04 |
| PromotionApplication, ProrationPolicy, SeatPolicy |
Yes |
— |
Partial/mis-copied |
Partial |
saas-BIL-F05, F11 |
Evidence: docs/out-of-scope.md, DefaultSubscriptionsProcessor.cs, SubscriptionSeatPolicy naming drift
Inbound events
| Capability |
Status |
Evidence |
| 6 inbound DTOs |
Implemented |
MessagingModel/Inbound/ |
| 5 reaction sagas |
Implemented |
FlowModel.MassTransit/ |
| Topic name alignment |
Missing |
BillingConstants.InboundEventTopics |
| Quota payload mapping |
Missing |
Dimension vs MeterKey |
| consumedEvents in JSON descriptor |
Missing |
ConnectSoft.Saas.Billing.json |
| Suspend-on-quota outbound event |
Missing |
Processor suspends silently |
| rating-window to Metering |
Missing |
— |
ServiceModel gaps
| Operation |
Status |
Feature |
| Create/Upgrade/ChangeEdition/Cancel |
Implemented |
— |
| RecordInvoiceIssued, RecordPaymentCaptured, RequestEntitlementsSync |
Missing from public API |
saas-BIL-F08 |
4.5 Metering
Repository: ConnectSoft.Saas.MeteringTemplate
Aggregate root: UsageMeter
Closes with: saas-MET-F01..F09
Entity model
| Concept |
Blueprint |
Code |
Status |
Feature |
IUsageMeter (tenant + dimension) |
Yes |
Yes |
Implemented |
— |
UsageRecord VO + idempotency |
Yes |
— |
Missing |
saas-MET-F01 |
Window VO + roll semantics |
Yes |
Partial |
Partial |
saas-MET-F02 |
Messaging
| Capability |
Status |
Evidence |
| 4 outbound topics |
Implemented |
MeteringConstants, topology |
| 4 inbound sagas |
Implemented |
FlowModel.MassTransit/ |
| UsageReportedForQuota topology |
Missing |
Event + saga exist, no SetEntityName |
| QuotaExceeded Dimension field |
Partial |
Billing expects MeterKey |
Dead / orphan code
| Item |
Status |
Evidence |
AssignEditionInput, ActivateUsageMeterInput, OverrideTenantFeatureInput, CatalogProductRetiredReactionInput |
Missing from processor |
DomainModel — not on IUsageMetersProcessor |
| Misnamed Orleans surrogates |
Partial |
Surrogates folder — copy-paste filenames |
MeteringOrleansGrainPartitionTests |
Missing (ignored) |
ArchitectureTests |
| UsageMeterAggregateTests |
Missing (placeholder) |
UnitTests |
Actor / Orleans
| Capability |
Status |
Feature |
UsageMeterGrain + composite key |
Implemented |
— |
| Write path via grain |
Missing |
saas-MET-F05 |
5. Decision log (ADRs needed)
| ID |
Decision |
Options |
Owner |
Status |
| ADR-TEN-001 |
Flat tenant vs rich aggregate |
(A) Flat + ADR (B) Implement child VOs/entities |
saas-TEN-F01 |
Pending |
| ADR-INTEG-001 |
Orleans write-path |
(A) Wire grains (B) Processor-only + ADR |
saas-INTEG-F06 |
Pending |
| ADR-BIL-001 |
Invoice scope |
(A) Read-model projection (B) Defer events-only |
saas-BIL-F03 |
Pending |
| ADR-BIL-002 |
Payment ACL |
(A) Adapter skeleton (B) Defer |
saas-BIL-F04 |
Pending |
| ADR-ENT-001 |
EffectiveEntitlementDescriptor |
(A) Materialize VO (B) Summary-only ADR |
saas-ENT-F01 |
Pending |
| ADR-CAT-001 |
Structural write APIs |
(A) Implement SLA/pricing/business writes (B) Defer |
saas-CAT-F03 |
Pending |
| ADR-CAT-003 |
NHibernate outbox |
Future work per existing stub |
saas-CAT-F05 |
Deferred |
Per-repo ADR stub files will be authored under each template's docs/adr/ directory.
6. Priority rationale
| Priority |
Scope |
Rationale |
| P0 |
saas-INTEG-F01..F07 |
E2E demo cannot run until topics, payloads, envelope, and cross-service contracts align |
| P0 |
saas-BIL-F02, saas-MET-F04 |
Direct consumers of INTEG fixes |
| P1 |
Per-repo lifecycle, entity model, tests |
Required for production-grade templates |
| P1 |
Orleans write-path (saas-INTEG-F06 + per-repo) |
Architectural compliance vs documented actor model |
| P2 |
Query extensions, optional cross-repo edges (saas-MET-F08) |
Enhancements after core path works |
| P2 |
Documentation/governance (saas-DOCS-F01..F10) |
Ongoing; some items complete with this program |
7. Traceability matrix
| Finding ID |
Description |
Repo |
Status |
Feature(s) |
| X-001 |
Billing tenant topic drift |
Billing |
Missing |
saas-INTEG-F01, saas-BIL-F02 |
| X-002 |
Billing quota topic drift |
Billing |
Missing |
saas-INTEG-F01, saas-BIL-F02 |
| X-003 |
Quota Dimension vs MeterKey |
Metering, Billing |
Missing |
saas-INTEG-F02, saas-MET-F04 |
| X-004 |
Event envelope incomplete |
All |
Partial |
saas-INTEG-F03, saas-TEN-F05 |
| X-005 |
Inbox dedupe not uniform |
All |
Partial |
saas-INTEG-F04 |
| X-006 |
Saga outbox in-memory |
Tenants |
Partial |
saas-INTEG-F05 |
| X-007 |
Orleans not on write path |
All |
Partial |
saas-INTEG-F06, saas-*-F0* |
| X-008 |
MessagingModel pin policy |
Entitlements |
Partial |
saas-INTEG-F07, saas-ENT-F08 |
| T-001 |
Rich tenant aggregate |
Tenants |
Missing |
saas-TEN-F01 |
| T-002 |
External refs |
Tenants |
Missing |
saas-TEN-F02 |
| T-003 |
Lifecycle saga stub |
Tenants |
Partial |
saas-TEN-F03 |
| T-004 |
JSON descriptor drift |
Tenants |
Partial |
saas-TEN-F04 |
| T-005 |
Placeholder arch tests |
Tenants |
Missing |
saas-TEN-F06 |
| C-001 |
Grain not on write path |
Catalog |
Partial |
saas-CAT-F01 |
| C-002 |
Doc drift aggregate-root.md |
Catalog |
Partial |
saas-CAT-F02 |
| E-001 |
EffectiveEntitlementDescriptor |
Entitlements |
Missing |
saas-ENT-F01 |
| E-002 |
Suspend/decommission E2E |
Entitlements |
Partial |
saas-ENT-F02 |
| B-001 |
Create creates Active not Draft |
Billing |
Partial |
saas-BIL-F01 |
| B-002 |
Invoice projection |
Billing |
Deferred |
saas-BIL-F03 |
| B-003 |
Payment ACL |
Billing |
Deferred |
saas-BIL-F04 |
| M-001 |
UsageRecord idempotency |
Metering |
Missing |
saas-MET-F01 |
| M-002 |
UsageReported topology |
Metering |
Missing |
saas-MET-F03 |
| M-003 |
Dead domain inputs |
Metering |
Missing |
saas-MET-F06 |
8. Appendices
A. Canonical topic list
See saas-cross-repo-published-language.md — Canonical topic plan table.
B. Integration event envelope schema
Required fields per published language:
tenantId: string (required)
aggregateId: string (required)
aggregateVersion: long
schemaVersion: int (starts at 1)
correlationId: guid
causationId: guid (optional)
occurredOn: datetime UTC
C. Glossary
| Term |
Definition |
| Aggregate root |
Single DDD root per repo (Tenant, Product, Entitlement, Subscription, UsageMeter) |
| ACL |
Anti-corruption layer for external providers |
| Published language |
Cross-repo event and ServiceModel contracts |
| Flat tenant |
Wave-1 simplified tenant without child VOs |
D. Source document inventory
| Document |
Version / date |
| This analysis |
2026-05-27 |
| saas-cross-repo-published-language.md |
Current in Documentation repo |
| saas-platform-ddd-entities.md |
CompanyDocumentation canonical |
| Template JSON descriptors |
Per-repo at analysis date |