SaaS Template Baseline Checklist¶
Single, repo-agnostic checklist every ConnectSoft.Saas.<Context>Template repo under C:\Git\ConnectSoft\SAAS\ must satisfy. Use it as the PR gate and the template installer CI gate.
This checklist is derived from:
- Extended Template (Base Submodule) Guide — Layer-3 mechanics.
- Template Metadata Composition — composed installer pattern.
- Template Naming Guide —
connectsoft-saas-*short names. - SaaS Bounded Contexts & Templates — canonical repos.
- SaaS Platform Solution Plan — end-to-end target.
- Multitenancy guide: guides/saas/multitenancy.md.
The five SaaS bounded-context repos this checklist applies to:
| Repo | Aggregate root | Short name |
|---|---|---|
ConnectSoft.Saas.TenantsTemplate |
Tenant |
connectsoft-saas-tenants |
ConnectSoft.Saas.ProductsCatalogTemplate |
Product |
connectsoft-saas-productscatalog |
ConnectSoft.Saas.EntitlementsTemplate |
Entitlement |
connectsoft-saas-entitlements |
ConnectSoft.Saas.BillingTemplate |
Subscription |
connectsoft-saas-billing |
ConnectSoft.Saas.MeteringTemplate |
UsageMeter |
connectsoft-saas-metering |
1. Layer-3 mechanics (non-negotiable)¶
- Repo is under
C:\Git\ConnectSoft\SAAS\and has its own git remote. -
.gitmodulesmapsbase-template/toConnectSoft.BaseTemplate(relative URL../ConnectSoft.BaseTemplate, branchmaster). - Clean clone +
git submodule update --init --recursivesucceeds. - Root
Directory.Build.propsimportsConnectSoft.TemplateRepositoryDirectory.Build.propsand thenbase-template/Directory.Build.props. - Root
Directory.Packages.propsimportsbase-template/Directory.Packages.props. -
ConnectSoft.TemplateRepositoryDirectory.Build.propssetsConnectSoftBaseTemplateDirectoryPostImporttobuild/DisableMicrosoftExtensionsStackForMinimalHost.props. -
build/DisableMicrosoftExtensionsStackForMinimalHost.propsimportsExtendedHost.BaseTemplateSatelliteDefaults.propsfor satellite paths andDisableMicrosoftExtensionsStackForMinimalHost.<Context>Host.propsfor everything else. -
<Context>Host.propsexplicitly sets the canonical SaaS feature-flag matrix below. - No
build/CentralPackageVersions.MinimalHost.propsand noConnectSoftCentralPackageVersionOverridesinConnectSoft.TemplateRepositoryDirectory.Build.propsunless an ADR documents an exception; rely on ConnectSoft.BaseTemplate Option A (conditional satellitePackageReference/PackageVersionalignment). -
base-template/submodule pointer references a ConnectSoft.BaseTemplate commit that includes Option A CPM satellite fixes (or newer). -
nuget.configincludesnuget.org+ConnectSoft+ optionalCoreWCF(package source mapping respected). -
<Context>.slnxenumerates the subset ofbase-template/src/ConnectSoft.BaseTemplate.*projects this repo compiles plus the repo's own projects.
2. Canonical SaaS feature-flag matrix (<Context>Host.props)¶
Locked defaults for every SaaS repo (overridable per repo only via an ADR):
<Project>
<PropertyGroup Label="SaaS: messaging and actors">
<UseMassTransit>true</UseMassTransit>
<MessagingModelTypeNone>false</MessagingModelTypeNone>
<UseNServiceBus>false</UseNServiceBus>
<UseOrleans>true</UseOrleans>
<UseDapr>false</UseDapr>
<UseAkka>false</UseAkka>
</PropertyGroup>
<PropertyGroup Label="SaaS: service model (REST + gRPC only)">
<UseRestApi>true</UseRestApi>
<UseGrpc>true</UseGrpc>
<UseSignalR>false</UseSignalR>
<UseGraphQL>false</UseGraphQL>
<UseCoreWCF>false</UseCoreWCF>
<UseServiceFabric>false</UseServiceFabric>
<UseAzureFunction>false</UseAzureFunction>
</PropertyGroup>
<PropertyGroup Label="SaaS: persistence (NHibernate only, multi-dialect)">
<UseNHibernate>true</UseNHibernate>
<UseMongoDb>false</UseMongoDb>
<Migrations>true</Migrations>
</PropertyGroup>
<PropertyGroup Label="SaaS: scheduler / cache / telemetry">
<UseHangFire>true</UseHangFire>
<DistributedCache>Redis</DistributedCache>
<OpenTelemetry>true</OpenTelemetry>
<UseOtelCollector>true</UseOtelCollector>
<Serilog>true</Serilog>
<Log4Net>false</Log4Net>
</PropertyGroup>
<PropertyGroup Label="SaaS: AI / vector / agents OFF by default">
<UseMicrosoftExtensionsAI>false</UseMicrosoftExtensionsAI>
<UseMicrosoftAgentFramework>false</UseMicrosoftAgentFramework>
<UseVectorStore>false</UseVectorStore>
<UseAgentSkills>false</UseAgentSkills>
<UseModelContextProtocol>false</UseModelContextProtocol>
<UseBotModel>false</UseBotModel>
<UseAIModel>false</UseAIModel>
</PropertyGroup>
</Project>
- All of the above set exactly as shown (or overridden via ADR + changelog).
- The minimal-host props
DefineConstantsstrip removes all disabled preprocessor symbols sobase-template/srcprojects compile for this repo.
3. One aggregate root per repo¶
-
<Context>.jsondeclares exactly oneentityModel.aggregateRoot. -
<Context>.ArchitectureTestscontains a test that fails if more than one type in<Context>.DomainModelcarries the aggregate-root marker. -
docs/adr/0001-one-aggregate-root-per-repo.mdis present and up to date. -
docs/out-of-scope.mdlists deferred entities (if any) with a pointer to their future repo.
4. Public contracts (ServiceModel + MessagingModel + ActorModel)¶
-
<Context>.ServiceModelcontains DTOs + typed client facades. -
<Context>.ServiceModel.RestApicontains controllers / minimal endpoints; published as a NuGet package. -
<Context>.ServiceModel.Grpccontains code-firstGrpc<Name>Serviceadapter classes implementing the C#[ServiceContract]interfaces from<Context>.ServiceModel(ServiceModel.Grpc — no.proto, noGrpc.Tools, noProtos/folder); published as a NuGet package. -
<Context>.MessagingModelcontains integration event contracts (tenantId,aggregateId,aggregateVersion,schemaVersion,correlationId,causationId) and is published as a NuGet package. -
<Context>.ActorModel(grain contracts) is published as a NuGet package whenUseOrleans=true. - No external repo may reference
<Context>.DomainModel,<Context>.EntityModel, or<Context>.PersistenceModel.*. Enforced inArchitectureTests. - Events version additively (
.v1topics); breaking changes require a.v2topic.
5. Persistence and outbox¶
-
<Context>.PersistenceModel.NHibernateis the only persistence implementation; multi-dialect viaPersistence:Dialectoption (sqlserver,postgres,mysql,oracle,sqlite). -
<Context>.DatabaseModel.Migrationsuses FluentMigrator expressions; dialect branches only viaIfDatabase(...)when an expression is impossible. - No custom outbox tables in migrations. MassTransit's built-in outbox (via BaseTemplate
AddMassTransitOutboxextension) owns the schema. - Consumers are idempotent; inbox dedupe key is
(sourceContext, eventId).
6. Multitenancy (hybrid default)¶
-
ConnectSoft.Extensions.Saas.*packages referenced (not ad hoc copies);MicroserviceRegistration/ SaaS registration seam callsAddConnectSoftSaas*(or equivalent) from BaseTemplate/SaaS template. - DedicatedSingleTenant +
appsettingspath documented for single installation; matches multitenancy-configuration-schema.md. -
ITenantContextpopulated from composite resolver (config → JWTtid→ headerX-Tenant-Id→ transport); rejects missing tenant on tenant-scoped endpoints when required. - gRPC: server/client use
ConnectSoft.Extensions.Saas.AspNetCore.Grpc—metadata keytenant-idper ADR-0100. - NHibernate:
EnableFilter+ parameter binding for shared-DB tenant filter in session scope (not onlyApplyFilteron mappings). - NServiceBus: when
UseNServiceBus=true, tenant propagation behaviors match MassTransit headertenant-idsemantics. - Background jobs and MassTransit / NServiceBus consumers propagate the same tenant context (envelope/header parity).
- Optional switches documented in
docs/configuration.md: -
Multitenancy:DeploymentKind = SharedDb(default row scoping) -
Multitenancy:DeploymentKind = DatabasePerTenant -
Multitenancy:DeploymentKind = ResidencySilo(perResidency/DataSiloId).
7. Actor / runtime coordination¶
- Orleans silo co-hosted in
<Context>.ApplicationwhenUseOrleans=true. - Grain keys include
tenantId(composite primary key). - Orleans streams used only for intra-silo fan-out. Cross-service integration goes through MassTransit.
8. Observability¶
- OTel traces + metrics + logs via
UseOtelCollector=true. -
observability/otel-collector/+observability/prometheus/+observability/grafana/+observability/loki/+observability/tempo/present and shared between DockerCompose and Deployment. - Grafana dashboards provisioned for: latency (p50/p95/p99), RED, errors, Orleans grain metrics (when on), MassTransit consume/publish, NHibernate query timings.
- Prometheus alerting rules for: p99 latency SLO breach, error-ratio breach, saga dead-letter growth, outbox delivery lag.
9. Deployment assets¶
-
DockerCompose/runs the service + chosen NHibernate DB + Redis + RabbitMQ + OTel Collector + Prometheus + Grafana + Loki + Tempo (or Jaeger). -
Deployment/kubernetes/contains raw manifests (Deployment/Service/HPA/PDB/NetworkPolicy/ServiceAccount/Ingress). -
Deployment/helm/contains a Helm chart with values for every supported dialect and a Values-driven observability block (ServiceMonitor/PodMonitor/PrometheusRule). -
<Context>.InfrastructureModelprovides Bicep/Terraform/Pulumi modules validated bybicep what-if/terraform plan/pulumi previewin the pipeline.
10. Template installer composition¶
-
.template.config/template.jsonis a minimal stub; real metadata is composed frombase-template/.template.config/template.json+template/<context>.template.extend.jsonduring CI. - Short name matches the canonical matrix in the table above.
-
build/prepare-<context>-template-pack.ps1+build/template-compose.ps1are present. -
build/template-compose.ps1stripsbase-template/.template.configfrom the final composed package (single-installer model). - CI gate:
dotnet new install+dotnet new connectsoft-saas-<context> -n Smoke+dotnet buildon the generated solution.
11. Pipelines¶
-
azure-pipelines.yml— app build / test / docker. -
azure-pipelines-service-model.yml— REST + gRPC NuGet publish. -
azure-pipelines-messaging-model.yml— event contracts NuGet publish. -
azure-pipelines-actor-model.yml— grain contracts NuGet publish (Orleans). -
azure-pipelines-infrastructure-model.yml— IaC packaging + what-if. -
azure-pipelines-documentation.yml—mkdocs build --strict+ publish. -
azure-pipelines-template.yml— single-installer composition +dotnet newgate.
12. Documentation¶
-
docs/+mkdocs.yml(MkDocs Material + mermaid + plantuml). - Sections: Overview, Bounded Context, Aggregate Root, REST API, gRPC API, Events, Configuration, Multitenancy, Deployment (compose + k8s + IaC), Observability, ADRs, Getting Started, Out of Scope.
-
<Context>.DiagramAsCodeModelrenders C4 + sequence + deployment diagrams in CI and embeds them in the docs site. -
README.mdat repo root with clone +git submodule update --init --recursiveand a pointer to the docs site. -
AGENTS.mdat repo root with agent-oriented guidance aligned with the factory-generator.
13. AI Software Factory wiring¶
-
<Context>.jsonis the single source of truth for the aggregate. -
<Context>.schema.jsonand<Context>.entitymodel.schema.jsonvalidate the descriptor. -
build/regenerate.ps1invokes themicroservice-generator-agent(idempotent) to (re)generateEntityModel+PersistenceModel.NHibernate+DatabaseModel.Migrations+ServiceModelscaffolding. - Domain logic (invariants, policies, sagas) lives in
<Context>.DomainModel.Impland<Context>.FlowModel.MassTransitand is not touched by regeneration.
14. Acceptance¶
A repo is "SaaS-template baseline" only when every box above is checked. Gate both PR reviews and azure-pipelines-template.yml on this checklist.