ADR-0002: Template Layering and Submodules¶
- Status: accepted
- Deciders: ConnectSoft Architecture Team
- Date: 2026-01-15
Context and Problem Statement¶
ConnectSoft maintains multiple specialized templates (Identity, Auth, Audit, Worker, etc.) that share significant common infrastructure. Without a structured approach, we face:
- Code Duplication - Common infrastructure duplicated across templates
- Maintenance Burden - Changes to base infrastructure require updates in multiple places
- Inconsistency - Templates drift apart over time
- Build Complexity - Templates become difficult to build and test independently
We need a way to share common infrastructure while allowing specialized templates to add domain-specific functionality without modifying the base.
Decision Drivers¶
- Need to avoid code duplication across templates
- Requirement that templates remain buildable as normal .NET solutions
- Need for base infrastructure changes to propagate automatically
- Requirement for specialized templates to add domain logic without modifying base
- Need to support both build-time development and generation-time composition
Considered Options¶
Option 1: Copy-Paste Base Code¶
Approach: Copy base template code into each specialized template repository.
Pros: - Simple to understand - Each template is self-contained
Cons: - Massive code duplication - Changes require updates in multiple places - Templates drift apart over time - Maintenance nightmare
Option 2: NuGet Package for Base Template¶
Approach: Package base template as NuGet package, specialized templates reference it.
Pros: - Base changes propagate via package updates - No code duplication
Cons: - Templates can't be built as normal solutions (NuGet packages don't contain source) - Difficult to develop and test base + specialized together - Version management complexity - Doesn't solve documentation reuse
Option 3: Git Submodules (Chosen)¶
Approach: Base template as git submodule in specialized template repositories.
Pros: - Base code shared without duplication - Templates buildable as normal .NET solutions - Base changes propagate via submodule updates - Supports both build-time and generation-time workflows - Documentation can reference base docs via submodule - Clear separation of concerns
Cons: - Requires understanding of git submodules - Submodule updates require explicit steps - Slightly more complex repository structure
Option 4: Template Inheritance / Composition at Generation Time Only¶
Approach: Base template + overlays composed only at generation time, not at build time.
Pros: - Clean separation at generation time - No build-time complexity
Cons: - Templates not buildable as normal solutions - Difficult to develop and test specialized templates - Poor developer experience
Decision Outcome¶
Chosen option: Option 3: Git Submodules, because it provides the best balance of:
- Zero code duplication
- Buildable templates (normal .NET solutions)
- Automatic propagation of base changes
- Support for both build-time development and generation-time composition
- Clear separation between base infrastructure and domain-specific logic
Architecture¶
We adopt a three-layer model:
- Layer 1: Shared Libraries (
ConnectSoft.Extensions.*) - Generic, cross-cutting infrastructure
- Delivered as NuGet packages
-
Used by all templates and services
-
Layer 2: Base Service Template (
MicroserviceTemplate.Base) - Canonical microservice "kernel"
- Solution layout, bootstrapping, common infrastructure
- No domain-specific logic
-
Included as git submodule in specialized templates
-
Layer 3: Specialized Templates (Identity, Auth, Audit, Worker, etc.)
- Each template is its own repository
- Includes base as git submodule (
base-template/) - Adds domain-specific projects, tests, docs
- Fully buildable as normal .NET solution
Build Time vs Generation Time¶
We distinguish two modes:
- Build Time: Specialized template repo includes base as submodule. Solution includes projects from both. Developer works on real, concrete application.
- Generation Time: Base template + overlays composed to produce final template artifact. Overlays are "pure" composition applied on top of base.
Overlays¶
At generation time, overlays are applied: - Add files (domain code, docs) - Patch existing files (Program.cs, pipelines) - Insert between markers in code - Token replacements - Merge template metadata
Overlays can be stacked: base → base + identity → base + identity + worker
Positive Consequences¶
- Zero Duplication - Base code exists in one place only
- Buildable Templates - All templates build as normal .NET solutions
- Automatic Propagation - Base changes propagate to all templates via submodule updates
- Clear Separation - Base is infrastructure-only, specialized templates add domain logic
- Developer Experience - Developers work on real solutions, not abstract templates
- Flexibility - Overlays enable recipe-based template composition
- Maintainability - Changes to base infrastructure benefit all templates
Negative Consequences¶
- Submodule Complexity - Team must understand git submodules
- Submodule Updates - Requires explicit
git submodule updatesteps - Repository Structure - Slightly more complex than single-repo approach
- Learning Curve - New team members need to understand three-layer model
Implementation Notes¶
Base Template Structure¶
MicroserviceTemplate.Base/
├── src/
│ ├── Host/
│ ├── Domain/
│ ├── Application/
│ └── Infrastructure/
├── tests/
│ └── Base.Testing.Infrastructure/
├── docs/
│ ├── overview.md
│ └── architecture.md
└── template/
└── template.json
Specialized Template Structure¶
IdentityBackendTemplate/
├── base-template/ # Git submodule
├── src/
│ ├── Identity.Api/
│ ├── Identity.Domain/
│ └── Identity.Infrastructure/
├── tests/
│ └── Identity.AcceptanceTests/
├── docs/
│ └── identity-*.md
└── template/
└── identity.template.extend.json
Workflow¶
- Base Template Changes:
- Update
MicroserviceTemplate.Baserepository - Commit and push changes
-
Update submodule reference in specialized templates
-
Specialized Template Development:
- Clone specialized template repository
- Run
git submodule update --init --recursive -
Open solution, build, test as normal .NET solution
-
Template Generation:
- Load base template
- Apply overlays (Identity, Worker, etc.)
- Resolve tokens
- Output final template artifact
Alternatives Considered¶
See "Considered Options" section above. We evaluated copy-paste, NuGet packages, and generation-time-only composition, but git submodules provided the best balance of benefits.
Links¶
- Template Architecture Overview - Business overview of the template architecture
- Template Layering Guide - Detailed guide to the three-layer architecture
- Git Submodules Documentation