Skip to content

title: Template Layering and Reuse description: Comprehensive guide to ConnectSoft's three-layer template architecture: shared libraries, base service template, and specialized templates with overlays and submodules. tags: - templates - architecture - layering - reuse - submodules - overlays


Template Layering and Reuse

This document provides a comprehensive guide to ConnectSoft's template layering architecture. It is written for architects and engineers who need to understand how templates are structured, how they reuse code and documentation, and how the build-time and generation-time processes differ.

ConnectSoft avoids copy-pasting and "template inheritance" by splitting responsibilities across three layers: shared libraries, base service template, and specialized templates. This architecture enables maximum reuse while maintaining flexibility and avoiding duplication.

Important

The three-layer model is fundamental to ConnectSoft's template architecture. Understanding this model is essential for working with templates, extending them, or creating new specialized templates.

Goals

The template layering architecture is designed to achieve:

  • Zero Duplication - No code, docs, or configuration is duplicated across templates
  • Maximum Reuse - Common infrastructure is shared via libraries and base template
  • Build-Time Development - Templates remain fully buildable as normal .NET solutions
  • Generation-Time Composition - Final templates are composed from base + overlays at generation time
  • Domain Flexibility - Specialized templates add domain-specific logic without modifying base
  • Maintainability - Changes to base infrastructure propagate to all templates automatically

The Three-Layer Model

ConnectSoft templates are organized into three distinct layers, each with specific responsibilities:

flowchart TB
    subgraph Layer1["Layer 1: Shared Libraries"]
        LIB1[ConnectSoft.Extensions.Logging]
        LIB2[ConnectSoft.Extensions.Messaging]
        LIB3[ConnectSoft.Extensions.Persistence]
        LIB4[ConnectSoft.Extensions.Options]
        LIB5[ConnectSoft.Extensions.Metrics]
    end

    subgraph Layer2["Layer 2: Base Service Template"]
        BASE[MicroserviceTemplate.Base<br/>Solution Layout<br/>Bootstrapping<br/>Common Infrastructure]
    end

    subgraph Layer3["Layer 3: Specialized Templates"]
        IDENTITY[Identity Template<br/>+ Identity Domain Logic]
        AUTH[Auth Template<br/>+ Auth Domain Logic]
        AUDIT[Audit Template<br/>+ Audit Domain Logic]
        WORKER[Worker Template<br/>+ Worker Domain Logic]
    end

    Layer1 -->|NuGet Packages| Layer2
    Layer1 -->|NuGet Packages| Layer3
    Layer2 -->|Git Submodule| Layer3

    style Layer1 fill:#E3F2FD
    style Layer2 fill:#BBDEFB
    style Layer3 fill:#90CAF9
Hold "Alt" / "Option" to enable pan & zoom

Layer 1: Shared Libraries (ConnectSoft.Extensions.*)

Purpose: All generic, cross-cutting infrastructure delivered as NuGet packages.

Responsibilities:

  • Observability (logging, tracing, metrics infrastructure)
  • Messaging (MassTransit, bus conventions)
  • Persistence (NHibernate, base repositories)
  • Options/config infrastructure
  • HTTP clients, retry policies, circuit breakers
  • Testing utilities and helpers

Characteristics:

  • Delivered as NuGet packages
  • Used by all templates and services
  • Versioned independently
  • No domain-specific logic

Example Libraries:

  • ConnectSoft.Extensions.Logging - Structured logging, correlation IDs
  • ConnectSoft.Extensions.Messaging - MassTransit abstractions
  • ConnectSoft.Extensions.Persistence - NHibernate base repositories
  • ConnectSoft.Extensions.Options - Configuration infrastructure
  • ConnectSoft.Extensions.Metrics - OpenTelemetry metrics setup
  • ConnectSoft.Extensions.Testing - Test utilities and BDD helpers

Layer 2: Base Service Template (MicroserviceTemplate.Base)

Purpose: One canonical repository containing the microservice "kernel" - all common structure and bootstrapping without domain-specific logic.

Responsibilities:

  • Solution layout (Host, Domain, Application, Infrastructure, Tests)
  • Program/Host bootstrapping
  • Common health checks, resilience patterns
  • Base CI/CD pipelines
  • Base testing infrastructure
  • Base documentation structure
  • Base template metadata (/template/template.json)

Characteristics:

  • Single canonical repository
  • No domain-specific logic (no Identity/Audit/etc.)
  • Fully buildable as a normal .NET solution
  • Used as git submodule by specialized templates

Structure:

MicroserviceTemplate.Base/
├── src/
│   ├── Host/              # Application host
│   ├── Domain/            # Base domain abstractions
│   ├── Application/      # Base application layer
│   └── Infrastructure/    # Base infrastructure
├── tests/
│   ├── Base.Testing.Infrastructure/  # Test helpers
│   └── Base.AcceptanceTests/         # Base acceptance tests
├── docs/
│   ├── overview.md
│   ├── architecture.md
│   └── testing.md
└── template/
    └── template.json      # Base template metadata

Layer 3: Specialized Templates (Identity, Auth, Audit, Worker, etc.)

Purpose: Each specialized template adds domain-specific functionality on top of the base template.

Responsibilities:

  • Domain-specific projects (Identity, Auth, Worker, etc.)
  • Domain-specific tests
  • Domain-specific documentation
  • Overlay metadata for template.json and docs

Characteristics:

  • Each template is its own repository
  • Includes base as git submodule (base-template/)
  • Adds domain-specific projects
  • Fully buildable as normal .NET solution without Factory
  • Defines overlay metadata for generation-time composition

Structure Example (Identity Template):

ConnectSoft.IdentityBackendTemplate/
├── base-template/                    # Git submodule -> MicroserviceTemplate.Base
├── src/
│   ├── Identity.Api/                 # Identity-specific API
│   ├── Identity.Domain/               # Identity domain logic
│   ├── Identity.Infrastructure/      # Identity infrastructure
│   └── Identity.Worker/               # Optional: Identity worker
├── tests/
│   ├── Identity.UnitTests/
│   └── Identity.AcceptanceTests/
├── docs/
│   ├── identity-overview.md
│   ├── identity-auth-flows.md
│   └── identity-metrics.md
├── template/
│   ├── identity.template.extend.json # Overlay metadata
│   └── worker.template.extend.json    # Optional: Worker overlay
└── IdentityBackend.sln                # Includes base + identity projects

Base Service Template and Specialized Templates

Base Template Responsibilities

The base template provides:

  1. Solution Structure - Standard layered architecture (Domain, Application, Infrastructure, API)
  2. Bootstrapping - Program.cs, Host setup, DI configuration
  3. Common Infrastructure - Health checks, resilience, observability wiring
  4. Base Testing - Test infrastructure, base test classes, test helpers
  5. Base Documentation - Architecture docs, testing guides, common patterns
  6. Template Metadata - Base template.json with common parameters

Specialized Template Responsibilities

Specialized templates add:

  1. Domain Logic - Domain-specific entities, aggregates, domain services
  2. Domain Infrastructure - Domain-specific repositories, external service clients
  3. Domain APIs - Domain-specific controllers, endpoints, request/response models
  4. Domain Tests - Domain-specific unit and acceptance tests
  5. Domain Documentation - Domain-specific flows, endpoints, metrics
  6. Overlay Metadata - Extend files that add to base template.json

Relationship: Submodules at Build Time

At build time, specialized templates include the base template as a git submodule:

flowchart LR
    subgraph IdentityRepo["Identity Template Repo"]
        BASE[base-template/<br/>Git Submodule]
        IDENTITY[src/Identity.*]
        TESTS[tests/Identity.*]
        DOCS[docs/identity-*.md]
    end

    subgraph BaseRepo["Base Template Repo"]
        BASESRC[src/]
        BASETESTS[tests/]
        BASEDOCS[docs/]
    end

    BASE -.->|Points to| BaseRepo

    style BASE fill:#FFE0B2
    style BaseRepo fill:#BBDEFB
Hold "Alt" / "Option" to enable pan & zoom

Benefits:

  • Base template changes propagate to all specialized templates
  • No code duplication
  • Each template repo is self-contained and buildable
  • Clear separation of concerns

Build Time vs Generation Time

ConnectSoft distinguishes between two modes of operation:

Build Time (Developer Experience)

Goal: Open solution, hit dotnet build / dotnet test, everything just works.

Characteristics:

  • Specialized template repo includes base as git submodule
  • Solution file includes projects from both base and specialized template
  • Developer works on a real, concrete application
  • No Factory required to build or test
  • Documentation references both base docs (via submodule) and specialized docs

Example Structure:

IdentityBackendTemplate/
├── base-template/              # Git submodule
│   ├── src/
│   │   ├── Host/
│   │   ├── Domain/
│   │   └── Infrastructure/
│   └── tests/
│       └── Base.Testing.Infrastructure/
├── src/
│   ├── Identity.Api/          # References base projects
│   ├── Identity.Domain/       # References base.Domain
│   └── Identity.Infrastructure/
├── tests/
│   └── Identity.AcceptanceTests/  # References Base.Testing.Infrastructure
└── IdentityBackend.sln        # Includes all projects

Workflow:

  1. Developer clones Identity template repo
  2. Git submodule automatically includes base template
  3. Open IdentityBackend.sln
  4. Build and test work immediately
  5. All base infrastructure is available

Generation Time (Factory / Template Packaging)

Goal: Produce a final, flattened project template that the AI Factory or dotnet new uses.

Characteristics:

  • Start from base template (code + docs + metadata)
  • Apply one or more overlays (Identity, Worker, etc.) in a recipe
  • Resolve tokens (ServiceName, Namespace, etc.)
  • Output flattened template artifact
  • Push to Azure DevOps / Git with pipelines/work items

Generation Pipeline:

flowchart TD
    BASE[Base Template<br/>Code + Docs + Metadata]
    OVERLAY1[Identity Overlay<br/>Domain Code + Docs + Metadata]
    OVERLAY2[Worker Overlay<br/>Worker Code + Docs + Metadata]

    RECIPE[Recipe:<br/>base + identity + worker]

    GENERATOR[Template Generator]

    OUTPUT[Final Template Artifact<br/>Flattened Structure]

    BASE --> GENERATOR
    OVERLAY1 --> GENERATOR
    OVERLAY2 --> GENERATOR
    RECIPE --> GENERATOR
    GENERATOR --> OUTPUT

    style BASE fill:#BBDEFB
    style OVERLAY1 fill:#C8E6C9
    style OVERLAY2 fill:#C8E6C9
    style OUTPUT fill:#A5D6A7
Hold "Alt" / "Option" to enable pan & zoom

Overlay Operations:

  • Add files - Domain code, hosted workers, docs
  • Patch existing files - Program.cs, pipelines
  • Insert between markers - // @@MODULE_REGISTRATION_BEGIN/END
  • Token replacements - ServiceName, Namespace, etc.
  • Patch YAML - mkdocs.yml, config files

Submodules at Build Time vs Overlays at Generation Time

Build Time: Git Submodules

What: Base template is included as a git submodule in specialized template repos.

Why:

  • Enables building specialized templates as normal .NET solutions
  • Base changes propagate automatically
  • No duplication of base code

How:

# In Identity template repo
git submodule add <base-repo-url> base-template

Result: Specialized template repo contains both base and specialized code, fully buildable.

Generation Time: Overlays

What: Overlays are applied on top of base template to create final template artifact.

Why:

  • Enables composition of multiple overlays (Identity + Worker)
  • Produces flattened template for dotnet new or Factory
  • Allows recipe-based template generation

How:

# Recipe example
templateId: identity-worker-service
displayName: "Identity Backend with Worker"
layers:
  - base: microservice/base
  - overlay: microservice/overlays/identity-backend
  - overlay: microservice/overlays/worker

Result: Final template artifact with all overlays applied, ready for generation.

Multi-Overlay Scenarios (Identity + Worker)

Specialized templates can combine multiple overlays. For example, an Identity Backend template might support both Identity domain logic and Worker functionality.

Overlay Stacking

flowchart TB
    BASE[Base Template]
    IDENTITY[Identity Overlay]
    WORKER[Worker Overlay]

    BASE --> IDENTITY
    IDENTITY --> WORKER
    WORKER --> FINAL[Final Template:<br/>Base + Identity + Worker]

    style BASE fill:#BBDEFB
    style IDENTITY fill:#C8E6C9
    style WORKER fill:#FFF9C4
    style FINAL fill:#A5D6A7
Hold "Alt" / "Option" to enable pan & zoom

Example: Identity + Worker Template

Build Time Structure:

IdentityBackendTemplate/
├── base-template/              # Base microservice kernel
├── src/
│   ├── Identity.Api/          # Identity API
│   ├── Identity.Domain/       # Identity domain
│   ├── Identity.Infrastructure/
│   └── Identity.Worker/       # Worker functionality
├── template/
│   ├── identity.template.extend.json
│   └── worker.template.extend.json
└── IdentityBackend.sln        # All projects included

Generation Time Recipe:

templateId: identity-worker-service
layers:
  - base: microservice/base
  - overlay: microservice/overlays/identity-backend
  - overlay: microservice/overlays/worker

Result: Final template supports both Identity domain logic and Worker patterns.

Docs Reuse (Base + Specialized)

Documentation follows the same layering pattern as code.

Build-Time Docs (Template Repos)

Base Repo:

MicroserviceTemplate.Base/
└── docs/
    ├── overview.md
    ├── architecture.md
    └── testing.md

Identity Repo:

IdentityBackendTemplate/
├── base-template/              # Submodule includes base docs
└── docs/
    ├── identity-overview.md
    ├── identity-auth-flows.md
    └── identity-metrics.md

mkdocs.yml in Identity Repo:

nav:
  - Home: docs/index.md
  - Base Template:
      - Overview: base-template/docs/overview.md
      - Architecture: base-template/docs/architecture.md
  - Identity Backend:
      - Overview: docs/identity-overview.md
      - Auth Flows: docs/identity-auth-flows.md

Result: At build time, docs reference both base (via submodule) and specialized docs. No physical merge needed.

Generation-Time Docs (Final Service)

When generating a final service repo, docs are physically merged:

Overlay Operations:

  1. Copy base docs → docs/base/ (or merge flat)
  2. Copy identity docs → docs/identity/
  3. Copy worker docs (if overlay present) → docs/worker/
  4. Patch mkdocs.yml to include all sections

Result: Generated service has all docs physically present in its repo with unified navigation.

Template Metadata Composition – Overview

Template metadata (/template/template.json) follows the same composition pattern. See Template Metadata Composition for detailed information.

Key Points:

  • Base template defines /template/template.json with common parameters
  • Specialized templates define extend files (e.g., identity.template.extend.json)
  • At generation time, extend files are merged with base template.json
  • Final template.json includes all parameters from base + overlays

Practical Example: Identity Backend Template

Let's walk through a complete example of how the Identity Backend template is structured.

Repository Structure

ConnectSoft.IdentityBackendTemplate/
├── .gitmodules                    # Git submodule configuration
├── base-template/                 # Git submodule -> MicroserviceTemplate.Base
│   ├── src/
│   │   ├── Host/
│   │   ├── Domain/
│   │   ├── Application/
│   │   └── Infrastructure/
│   ├── tests/
│   │   └── Base.Testing.Infrastructure/
│   ├── docs/
│   │   ├── overview.md
│   │   └── architecture.md
│   └── template/
│       └── template.json
├── src/
│   ├── Identity.Api/              # Identity-specific API
│   ├── Identity.Domain/           # Identity domain logic
│   ├── Identity.Infrastructure/   # Identity infrastructure
│   └── Identity.Worker/            # Optional: Identity worker
├── tests/
│   ├── Identity.UnitTests/        # Identity unit tests
│   └── Identity.AcceptanceTests/   # Identity acceptance tests
├── docs/
│   ├── identity-overview.md
│   ├── identity-auth-flows.md
│   └── identity-metrics.md
├── template/
│   ├── identity.template.extend.json
│   └── worker.template.extend.json
├── IdentityBackend.sln            # Includes base + identity projects
└── pack-template.ps1              # Script to build final template artifacts

Solution File Structure

IdentityBackend.sln includes:

From Base (submodule):

  • base-template/src/Host/Host.csproj
  • base-template/src/Domain/Domain.csproj
  • base-template/src/Application/Application.csproj
  • base-template/src/Infrastructure/Infrastructure.csproj
  • base-template/tests/Base.Testing.Infrastructure/Base.Testing.Infrastructure.csproj

From Identity Template:

  • src/Identity.Api/Identity.Api.csproj
  • src/Identity.Domain/Identity.Domain.csproj
  • src/Identity.Infrastructure/Identity.Infrastructure.csproj
  • tests/Identity.UnitTests/Identity.UnitTests.csproj
  • tests/Identity.AcceptanceTests/Identity.AcceptanceTests.csproj

Build Process

  1. Clone Repository:

    git clone <identity-template-repo>
    cd IdentityBackendTemplate
    git submodule update --init --recursive
    

  2. Open Solution:

    dotnet sln IdentityBackend.sln
    

  3. Build:

    dotnet build
    

  4. Test:

    dotnet test
    

Everything works as a normal .NET solution. No Factory required.

Generation Process

When the Factory generates a service from this template:

  1. Load Base Template - Read base template code, docs, metadata
  2. Apply Identity Overlay - Add Identity domain code, patch files, merge metadata
  3. Apply Worker Overlay (if requested) - Add Worker code, merge metadata
  4. Resolve Tokens - Replace ServiceName, Namespace, etc.
  5. Output Final Template - Flattened structure ready for dotnet new

Rules and Best Practices

Do's

Use git submodules for base template in specialized templates
Keep base template domain-agnostic - no Identity/Audit/etc. logic
Build specialized templates as normal solutions - they should compile without Factory
Use overlays for generation-time composition - enables recipe-based templates
Reference base docs via submodule at build time
Merge docs physically at generation time

Don'ts

Don't duplicate base code - use submodules instead
Don't put domain logic in base - base is infrastructure only
Don't modify base template.json directly - use extend files
Don't copy base docs - reference via submodule
Don't require Factory to build - templates must build as normal solutions