Skip to content

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:

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.
  • .gitmodules maps base-template/ to ConnectSoft.BaseTemplate (relative URL ../ConnectSoft.BaseTemplate, branch master).
  • Clean clone + git submodule update --init --recursive succeeds.
  • Root Directory.Build.props imports ConnectSoft.TemplateRepositoryDirectory.Build.props and then base-template/Directory.Build.props.
  • Root Directory.Packages.props imports base-template/Directory.Packages.props.
  • ConnectSoft.TemplateRepositoryDirectory.Build.props sets ConnectSoftBaseTemplateDirectoryPostImport to build/DisableMicrosoftExtensionsStackForMinimalHost.props.
  • build/DisableMicrosoftExtensionsStackForMinimalHost.props imports ExtendedHost.BaseTemplateSatelliteDefaults.props for satellite paths and DisableMicrosoftExtensionsStackForMinimalHost.<Context>Host.props for everything else.
  • <Context>Host.props explicitly sets the canonical SaaS feature-flag matrix below.
  • No build/CentralPackageVersions.MinimalHost.props and no ConnectSoftCentralPackageVersionOverrides in ConnectSoft.TemplateRepositoryDirectory.Build.props unless an ADR documents an exception; rely on ConnectSoft.BaseTemplate Option A (conditional satellite PackageReference / PackageVersion alignment).
  • base-template/ submodule pointer references a ConnectSoft.BaseTemplate commit that includes Option A CPM satellite fixes (or newer).
  • nuget.config includes nuget.org + ConnectSoft + optional CoreWCF (package source mapping respected).
  • <Context>.slnx enumerates the subset of base-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 DefineConstants strip removes all disabled preprocessor symbols so base-template/src projects compile for this repo.

3. One aggregate root per repo

  • <Context>.json declares exactly one entityModel.aggregateRoot.
  • <Context>.ArchitectureTests contains a test that fails if more than one type in <Context>.DomainModel carries the aggregate-root marker.
  • docs/adr/0001-one-aggregate-root-per-repo.md is present and up to date.
  • docs/out-of-scope.md lists deferred entities (if any) with a pointer to their future repo.

4. Public contracts (ServiceModel + MessagingModel + ActorModel)

  • <Context>.ServiceModel contains DTOs + typed client facades.
  • <Context>.ServiceModel.RestApi contains controllers / minimal endpoints; published as a NuGet package.
  • <Context>.ServiceModel.Grpc contains code-first Grpc<Name>Service adapter classes implementing the C# [ServiceContract] interfaces from <Context>.ServiceModel (ServiceModel.Grpc — no .proto, no Grpc.Tools, no Protos/ folder); published as a NuGet package.
  • <Context>.MessagingModel contains 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 when UseOrleans=true.
  • No external repo may reference <Context>.DomainModel, <Context>.EntityModel, or <Context>.PersistenceModel.*. Enforced in ArchitectureTests.
  • Events version additively (.v1 topics); breaking changes require a .v2 topic.

5. Persistence and outbox

  • <Context>.PersistenceModel.NHibernate is the only persistence implementation; multi-dialect via Persistence:Dialect option (sqlserver, postgres, mysql, oracle, sqlite).
  • <Context>.DatabaseModel.Migrations uses FluentMigrator expressions; dialect branches only via IfDatabase(...) when an expression is impossible.
  • No custom outbox tables in migrations. MassTransit's built-in outbox (via BaseTemplate AddMassTransitOutbox extension) 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 calls AddConnectSoftSaas* (or equivalent) from BaseTemplate/SaaS template.
  • DedicatedSingleTenant + appsettings path documented for single installation; matches multitenancy-configuration-schema.md.
  • ITenantContext populated from composite resolver (config → JWT tid → header X-Tenant-Id → transport); rejects missing tenant on tenant-scoped endpoints when required.
  • gRPC: server/client use ConnectSoft.Extensions.Saas.AspNetCore.Grpc—metadata key tenant-id per ADR-0100.
  • NHibernate: EnableFilter + parameter binding for shared-DB tenant filter in session scope (not only ApplyFilter on mappings).
  • NServiceBus: when UseNServiceBus=true, tenant propagation behaviors match MassTransit header tenant-id semantics.
  • 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 (per Residency / DataSiloId).

7. Actor / runtime coordination

  • Orleans silo co-hosted in <Context>.Application when UseOrleans=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>.InfrastructureModel provides Bicep/Terraform/Pulumi modules validated by bicep what-if / terraform plan / pulumi preview in the pipeline.

10. Template installer composition

  • .template.config/template.json is a minimal stub; real metadata is composed from base-template/.template.config/template.json + template/<context>.template.extend.json during CI.
  • Short name matches the canonical matrix in the table above.
  • build/prepare-<context>-template-pack.ps1 + build/template-compose.ps1 are present.
  • build/template-compose.ps1 strips base-template/.template.config from the final composed package (single-installer model).
  • CI gate: dotnet new install + dotnet new connectsoft-saas-<context> -n Smoke + dotnet build on 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.ymlmkdocs build --strict + publish.
  • azure-pipelines-template.yml — single-installer composition + dotnet new gate.

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>.DiagramAsCodeModel renders C4 + sequence + deployment diagrams in CI and embeds them in the docs site.
  • README.md at repo root with clone + git submodule update --init --recursive and a pointer to the docs site.
  • AGENTS.md at repo root with agent-oriented guidance aligned with the factory-generator.

13. AI Software Factory wiring

  • <Context>.json is the single source of truth for the aggregate.
  • <Context>.schema.json and <Context>.entitymodel.schema.json validate the descriptor.
  • build/regenerate.ps1 invokes the microservice-generator-agent (idempotent) to (re)generate EntityModel + PersistenceModel.NHibernate + DatabaseModel.Migrations + ServiceModel scaffolding.
  • Domain logic (invariants, policies, sagas) lives in <Context>.DomainModel.Impl and <Context>.FlowModel.MassTransit and 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.