Skip to content

SaaS Aggregate Root Assignment (Canonical)

Authoritative map of the one aggregate root that each ConnectSoft.Saas.<Context>Template repo owns. Pairs with:

Rule

Every ConnectSoft.Saas.<Context>Template repo owns exactly one aggregate root. Related concepts are either (1) child entities / value objects of that root, (2) other aggregates in other repos referenced by opaque ID and integrated via events, or (3) explicitly deferred (see docs/out-of-scope.md).

The rule is enforced in CI by <Context>.ArchitectureTests.

Canonical map

flowchart LR
  subgraph saas [ConnectSoft SAAS workspace]
    T["TenantsTemplate (Tenant)"]
    P["ProductsCatalogTemplate (Product)"]
    E["EntitlementsTemplate (Entitlement)"]
    B["BillingTemplate (Subscription)"]
    M["MeteringTemplate (UsageMeter)"]
  end
  T -->|"tenant.created"| E
  P -->|"catalog.entitlements.changed"| E
  P -->|"catalog.entitlements.changed"| B
  E -->|"entitlements.updated"| B
  B -->|"subscription.changed"| E
  B -->|"subscription.period.rating-window"| M
  M -->|"quota.exceeded"| B
  T -.->|"TenantId by ID"| P
  T -.->|"TenantId by ID"| B
  T -.->|"TenantId by ID"| M
Hold "Alt" / "Option" to enable pan & zoom

ConnectSoft.Saas.TenantsTemplate — aggregate root Tenant

  • Repo path: C:\Git\ConnectSoft\SAAS\ConnectSoft.Saas.TenantsTemplate
  • Short name: connectsoft-saas-tenants
  • In-aggregate:
  • TenantProfile (VO) — display name, slug, locale.
  • TenantRegionResidency (VO) — residency tier + DataSiloId.
  • Contact (entity, local identity) — owner / billing / technical.
  • TenantConfiguration (entity) — feature overrides + config bundle.
  • LifecycleStatus (smart enum) — Pending, Active, Suspended, Deleted.
  • References by ID only: EditionRef (ProductsCatalog), BillingAccountRef (Billing).
  • Published events: tenants.domain.v1.tenant-created, …-activated, …-suspended, …-deleted, …-residency-changed.
  • Deferred: multi-tenant organization trees, tenant-directory identity federation (see docs/out-of-scope.md).

ConnectSoft.Saas.ProductsCatalogTemplate — aggregate root Product

  • Repo path: C:\Git\ConnectSoft\SAAS\ConnectSoft.Saas.ProductsCatalogTemplate
  • Short name: connectsoft-saas-productscatalog
  • In-aggregate:
  • Edition (entity) — stable EditionId; child of Product.
  • EditionFeature (entity) — feature mapping per edition.
  • Feature (entity) — catalog feature keys + descriptors.
  • ServiceLevelAgreement (entity) — SLA per product/edition.
  • BusinessModel (entity), EditionBusinessModel (entity) — monetization model per edition.
  • PricingModel (entity), EditionPricing (entity) — pricing declarations (structural only; enforcement lives in Billing).
  • BillingRule (VO), AccessRule (VO), UsageLimit (VO) — rule declarations.
  • References by ID only: TenantId, SubscriptionId (Billing).
  • Published events: catalog.domain.v1.product-created, …-edition-added, …-feature-activated, catalog.entitlements.v1.entitlements-changed.
  • ADR 0002-edition-inside-product-aggregate.md (mandatory): although Edition has a strong identity, it stays an in-aggregate child of Product for this wave; a future split into EditionsTemplate is an additive change (the EditionId identity stays stable and the published event surface is already addressed by EditionId).
  • Seed vocabulary is derived from ConnectSoft.Saas..ProductCatalogDemo — see its ConnectSoft.Saas.ProductsCatalog.json descriptor for the reference shape. The demo repo is not cloned as solution structure.

ConnectSoft.Saas.EntitlementsTemplate — aggregate root Entitlement

  • Repo path: C:\Git\ConnectSoft\SAAS\ConnectSoft.Saas.EntitlementsTemplate
  • Short name: connectsoft-saas-entitlements
  • In-aggregate:
  • EntitlementAssignment (entity) — which EditionRef is assigned to which TenantId.
  • TenantFeatureOverride (entity) — per-tenant feature on/off overrides.
  • EffectiveEntitlementDescriptor (VO) — materialized effective entitlements snapshot.
  • References by ID only: TenantId, EditionRef, FeatureKey.
  • Published events: entitlements.v1.effective-entitlements-updated, entitlements.v1.feature-overridden, entitlements.v1.assignment-changed.
  • Deferred: pay-per-use entitlements, entitlement rollouts per geo — out of scope this wave.

ConnectSoft.Saas.BillingTemplate — aggregate root Subscription

  • Repo path: C:\Git\ConnectSoft\SAAS\ConnectSoft.Saas.BillingTemplate
  • Short name: connectsoft-saas-billing
  • In-aggregate:
  • BillingCycle (smart enum) — Monthly, Annual, Custom.
  • PromotionApplication (entity) — promotion usage per subscription.
  • SeatPolicy (VO) — seats min/max/current.
  • ProrationPolicy (VO) — how upgrades / downgrades prorate.
  • References by ID only: TenantId, EditionRef, PricingModelId (ProductsCatalog).
  • Published events: billing.subscriptions.v1.subscription-created, …-upgraded, …-canceled, billing.invoices.v1.invoice-issued, billing.payments.v1.payment-captured.
  • Not roots here (and so NOT in this repo as roots):
  • Invoice — read-model projection this wave; promote to InvoicingTemplate later.
  • PricingModel / BillingRule — owned by ProductsCatalog (structural) + Billing (enforcement). Billing never writes them; it reads the ServiceModel NuGet.
  • External ACL: payments / tax providers via BillingTemplate.FlowModel.MassTransit adapters.

ConnectSoft.Saas.MeteringTemplate — aggregate root UsageMeter

  • Repo path: C:\Git\ConnectSoft\SAAS\ConnectSoft.Saas.MeteringTemplate
  • Short name: connectsoft-saas-metering
  • Grain key: {tenantId, dimension}. Orleans is the canonical runtime for this aggregate (per-tenant stateful counters).
  • In-aggregate:
  • Counter (entity) — running totals per window.
  • Window (VO) — {from, to, granularity}.
  • UsageRecord (VO) — individual event (with idempotency key + integrity hash).
  • References by ID only: TenantId, Dimension, SubscriptionId (Billing).
  • Published events: metering.usage.v1.usage-recorded, metering.counters.v1.counter-rolled, metering.quota.v1.quota-threshold-crossed, metering.quota.v1.quota-exceeded.
  • Deferred: billing-grade rating / invoicing integration (Billing consumes events; rating is Billing's).

Enforcement

  1. <Context>.ArchitectureTests includes a test asserting exactly one aggregate-root marker in <Context>.DomainModel.
  2. <Context>.json schema enforces a single entityModel.aggregateRoot property.
  3. docs/adr/0001-one-aggregate-root-per-repo.md is mandatory; ProductsCatalog additionally ships docs/adr/0002-edition-inside-product-aggregate.md.
  4. The installer CI gate (azure-pipelines-template.yml) fails when either the descriptor or architecture tests violate the rule.