Generated SaaS Aggregate Roots¶
Target Architecture — Final-State Design
This page documents the thirteen common platform-level aggregate roots present in every Generated SaaS Product. Each follows the naming conventions (singular PascalCase noun) and is persisted via NHibernate to Azure SQL or PostgreSQL. Products add domain-specific aggregates; these form the reusable SaaS spine. Every aggregate is the consistency boundary and the only entry point to its entities.
All aggregates below carry a TenantId value object (except Tenant itself, which is the tenant boundary), enforce their invariants within a single transaction, and emit domain events in the canonical envelope. Repositories follow the {AggregateRoot}Repository convention.
Tenant¶
- Purpose — represents a customer organization and is the root isolation boundary for all data and traffic in the product.
- Fields —
TenantId,Name,Slug,Status(Provisioning, Active, Suspended, Decommissioned),EditionCode,CreatedAt,ProvisionedAt. - Entities —
TenantOwner(the founding admin user reference),ProvisioningStep(ordered provisioning tasks). - Value Objects —
TenantId,Slug,TenantStatus,ContactEmail. - Invariants — slug is globally unique and immutable once Active; cannot transition to Active until all
ProvisioningSteps succeed; suspended tenants reject all write operations downstream. - Domain Events —
TenantRegistered,TenantProvisioned,TenantSuspended,TenantDecommissioned. - Repository —
TenantRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; tenant registry table; the source of the
tenantIddiscriminator used by every other store.
User¶
- Purpose — an authenticatable principal within a tenant, with profile and role assignments.
- Fields —
UserId,TenantId,Email,DisplayName,Status(Invited, Active, Disabled),CreatedAt,LastLoginAt. - Entities —
RoleAssignment(links user to aRole),Invitation(pending invite with expiry). - Value Objects —
UserId,TenantId,EmailAddress,UserStatus. - Invariants — email is unique within a tenant; an
Invitationcannot be accepted after expiry; a disabled user holds no effective permissions. - Domain Events —
UserInvited,UserActivated,UserDisabled,RoleAssigned,RoleUnassigned. - Repository —
UserRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; users and role-assignment tables, tenant-scoped.
Role¶
- Purpose — a named bundle of permissions assignable to users within a tenant (RBAC).
- Fields —
RoleId,TenantId,Code,Name,IsSystem,Description. - Entities —
RolePermission(grant linking role to aPermission). - Value Objects —
RoleId,RoleCode,TenantId. - Invariants — code is unique within a tenant; system roles (
IsSystem) cannot be deleted or have their core permissions removed; a role must reference only existing permissions. - Domain Events —
RoleCreated,RoleUpdated,PermissionGrantedToRole,PermissionRevokedFromRole,RoleDeleted. - Repository —
RoleRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; roles and role-permission tables.
Permission¶
- Purpose — an atomic, named capability (e.g.
subscription.manage) that can be granted to roles. - Fields —
PermissionId,Code,Name,Category,IsSystem. - Entities — none (permissions are leaf grants).
- Value Objects —
PermissionId,PermissionCode,PermissionCategory. - Invariants — code is globally unique and stable; system permissions are immutable; a permission cannot be deleted while referenced by any role.
- Domain Events —
PermissionDefined,PermissionDeprecated. - Repository —
PermissionRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; typically a platform-level (cross-tenant) catalog table seeded from the product blueprint.
Subscription¶
- Purpose — a tenant's active commercial agreement on an edition, governing entitlements and billing.
- Fields —
SubscriptionId,TenantId,EditionCode,Status(Trial, Active, PastDue, Cancelled),BillingCycle,CurrentPeriodStart,CurrentPeriodEnd,CancelledAt. - Entities —
BillingPeriod(per-cycle billing record),EntitlementOverride(per-subscription override of an edition entitlement). - Value Objects —
SubscriptionId,TenantId,BillingCycle,Money,SubscriptionStatus. - Invariants — exactly one Active subscription per tenant; cannot activate without referencing a published
Edition;CurrentPeriodEndis always afterCurrentPeriodStart; cancellation is terminal for the current period. - Domain Events —
SubscriptionActivated,SubscriptionRenewed,SubscriptionPastDue,SubscriptionCancelled,EntitlementOverridden. - Repository —
SubscriptionRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; subscription and billing-period tables, tenant-scoped.
Edition¶
- Purpose — a defined product plan (e.g. Starter, Professional, Enterprise) bundling features, limits, and price.
- Fields —
EditionId,Code,Name,Status(Draft, Published, Retired),Price,EffectiveFrom. - Entities —
EditionEntitlement(feature/limit included in the edition),PriceTier. - Value Objects —
EditionId,EditionCode,Money,Limit,EditionStatus. - Invariants — code is unique; a Published edition's entitlements are immutable (new versions require a new edition); a retired edition accepts no new subscriptions.
- Domain Events —
EditionDefined,EditionPublished,EditionRetired. - Repository —
EditionRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; platform-level edition catalog (cross-tenant), seeded from blueprint.
FeatureFlag¶
- Purpose — a toggle controlling feature availability and progressive rollout, scoped per tenant or globally.
- Fields —
FeatureFlagId,Key,TenantId(nullable for global),Enabled,RolloutPercentage,UpdatedAt. - Entities —
FlagRule(targeting rule, e.g. by tenant segment or user attribute). - Value Objects —
FeatureFlagKey,RolloutPercentage,TenantId. - Invariants — key is unique per scope;
RolloutPercentageis within 0–100; a tenant-scoped flag overrides the global flag for that tenant. - Domain Events —
FeatureFlagToggled,FeatureFlagRuleChanged. - Repository —
FeatureFlagRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; flags cached in Redis for low-latency evaluation.
AuditEntry¶
- Purpose — an immutable record of a security- or compliance-relevant action.
- Fields —
AuditEntryId,TenantId,Actor,Action,ResourceType,ResourceId,OccurredAt,TraceId,Outcome. - Entities — none (audit entries are immutable leaf records).
- Value Objects —
AuditEntryId,TenantId,Actor,ActionCode,TraceId. - Invariants — entries are append-only and never updated or deleted;
OccurredAtandTraceIdare always populated; outcome is one of Success/Denied/Failure. - Domain Events —
AuditEntryRecorded. - Repository —
AuditEntryRepository(write-once; query-only thereafter). - Persistence — append-only Azure SQL/PostgreSQL table; periodically exported to immutable Blob archive by the Audit Export Worker.
Notification¶
- Purpose — a message to be delivered to one or more recipients across one or more channels.
- Fields —
NotificationId,TenantId,TemplateCode,Status(Pending, Sent, Failed),Channel,RequestedAt,SentAt. - Entities —
NotificationRecipient(per-recipient delivery state),DeliveryAttempt(per-attempt result). - Value Objects —
NotificationId,TenantId,Channel(Email, SMS, InApp, Webhook),TemplateCode,NotificationStatus. - Invariants — a notification references a valid template; status becomes Sent only when all required recipients have a successful
DeliveryAttempt; retries are bounded. - Domain Events —
NotificationRequested,NotificationSent,NotificationFailed. - Repository —
NotificationRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; large bodies/attachments in Blob; processed by
NotificationDispatchWorker.
IntegrationConnection¶
- Purpose — a configured connection to an external system, including credentials and sync settings.
- Fields —
IntegrationConnectionId,TenantId,SystemType,Status(Configured, Connected, Error, Disabled),LastSyncAt,SyncSchedule. - Entities —
SyncRun(per-execution record),FieldMapping(external↔internal field map). - Value Objects —
IntegrationConnectionId,TenantId,SecretReference(Key Vault pointer, never the secret),SyncSchedule. - Invariants — secrets are stored only as
SecretReferenceto Key Vault; a connection cannot sync while Disabled or Error without remediation; one in-flightSyncRunper connection. - Domain Events —
IntegrationConnected,IntegrationSyncRequested,IntegrationSucceeded,IntegrationFailed,IntegrationDisabled. - Repository —
IntegrationConnectionRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; secret material referenced in Key Vault; processed by
IntegrationSyncWorker.
WebhookSubscription¶
- Purpose — an external subscriber's registration to receive product events via outbound HTTP callbacks.
- Fields —
WebhookSubscriptionId,TenantId,CallbackUrl,EventTypes,Status(Active, Paused, Disabled),SecretReference,CreatedAt. - Entities —
DeliveryRecord(per-event delivery attempt and response). - Value Objects —
WebhookSubscriptionId,TenantId,CallbackUrl,EventTypeFilter,SecretReference. - Invariants — callback URL must be HTTPS; signing secret is stored as a
SecretReference; failed deliveries retry with backoff and auto-pause the subscription after a threshold. - Domain Events —
WebhookSubscribed,WebhookDelivered,WebhookDeliveryFailed,WebhookPaused. - Repository —
WebhookSubscriptionRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; delivery handled by the Integration Service / workers.
ReportDefinition¶
- Purpose — a parameterized definition of a report or analytics view that can be run to produce artifacts.
- Fields —
ReportDefinitionId,TenantId,Name,Category,SupportedFormats,ParameterSchema,CreatedAt. - Entities —
ReportRun(per-execution record with status and artifact reference),ReportParameter. - Value Objects —
ReportDefinitionId,TenantId,ReportFormat(Pdf, Csv, Xlsx),ParameterSchema. - Invariants — a run's parameters must validate against
ParameterSchema; requested format must be inSupportedFormats; artifacts are immutable once produced. - Domain Events —
ReportRequested,ReportGenerated,ReportFailed. - Repository —
ReportDefinitionRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL for definitions/runs; artifacts in Blob; processed by
ReportGenerationWorker.
ConfigurationSetting¶
- Purpose — a tenant- or product-scoped configuration value governing runtime behavior.
- Fields —
ConfigurationSettingId,Key,TenantId(nullable for product-default),Value,ValueType,UpdatedAt,UpdatedBy. - Entities — none (settings are leaf records; history kept via audit).
- Value Objects —
ConfigurationKey,ConfigurationValue,ValueType,TenantId. - Invariants — key is unique per scope; value must conform to
ValueType; tenant-scoped settings override product defaults; secret-typed settings are stored as Key Vault references. - Domain Events —
ConfigurationChanged. - Repository —
ConfigurationSettingRepository. - Persistence — NHibernate to Azure SQL/PostgreSQL; hot settings cached in Redis; secret values referenced in Key Vault.
Aggregate relationships¶
flowchart TB
Tenant["Tenant"] --> User["User"]
Tenant --> Subscription["Subscription"]
Tenant --> FeatureFlag["FeatureFlag"]
Tenant --> ConfigurationSetting["ConfigurationSetting"]
Tenant --> IntegrationConnection["IntegrationConnection"]
Tenant --> WebhookSubscription["WebhookSubscription"]
Tenant --> Notification["Notification"]
Tenant --> ReportDefinition["ReportDefinition"]
Tenant --> AuditEntry["AuditEntry"]
User --> Role["Role"]
Role --> Permission["Permission"]
Subscription --> Edition["Edition"]
Hold "Alt" / "Option" to enable pan & zoom
How aggregates contribute to the pillars¶
- Traceability — every aggregate emits enveloped events carrying
traceId;AuditEntryexplicitly storesTraceId. - Reusability — the thirteen roots are generated identically across products from the
ConnectSoft.Saas.*andConnectSoft.MicroserviceTemplatefamilies. - Autonomy — agents generate aggregates, repositories, and migrations from the domain model in the blueprint.
- Governance — invariants (append-only audit, immutable published editions, secret references) encode policy in the domain.
- Observability — domain events are first-class telemetry; aggregate operations are traced and metered.
- Multi-tenant scale —
TenantIdis a value object on every root, andTenantis the explicit isolation boundary.