Template Metadata Composition¶
This document explains how ConnectSoft templates compose template metadata (template.json) from base template and specialized template overlays. It is written for architects and engineers who need to understand or create template extend files and the pack script behavior.
Template metadata follows the same composition pattern as code and documentation: base template provides canonical metadata, and specialized templates provide extend files that are merged at generation time.
Important
The final template.json is always generated from base + extend files. Never manually duplicate base parameters in specialized template repos. Use extend files to add or override metadata.
Goals¶
Template metadata composition is designed to achieve:
- No Duplication - Base parameters are never duplicated in specialized templates
- Composition - Final template.json is composed from base + one or more overlays
- Flexibility - Specialized templates can override or extend base metadata
- Recipe Support - Multiple overlays can be combined (Identity + Worker)
- Maintainability - Base parameter changes propagate automatically
Base Template Folder Structure¶
The base template defines the canonical template metadata structure:
MicroserviceTemplate.Base/
└── template/
├── template.json # Base template metadata
├── ide.host.json # IDE-specific host configuration
└── dotnetcli.host.json # CLI-specific host configuration
Base template.json Structure¶
The base template.json defines:
Top-Level Fields:
name- Template display nameshortName- Template short name fordotnet newdescription- Template descriptiontags- Template tags and classificationsauthor- Template authorclassifications- Template classifications
Symbols (Parameters):
ServiceName- Name of the generated serviceRootNamespace- Root namespace for generated codeUseMassTransit- Whether to use MassTransit messagingUseNHibernate- Whether to use NHibernate persistencePersistenceType- Type of persistence (SqlServer, PostgreSQL, etc.)MessagingType- Type of messaging (MassTransit, NServiceBus, None)- Common infrastructure flags
Post-Actions:
- Restore NuGet packages
- Run build
- Run tests (optional)
- Setup git repository
Example Base template.json:
{
"$schema": "http://json.schemastore.org/template",
"author": "ConnectSoft",
"classifications": ["ConnectSoft", "Microservice", "Clean Architecture"],
"name": "ConnectSoft Microservice Base",
"shortName": "cs-microservice-base",
"description": "Base microservice template with Clean Architecture and DDD",
"tags": {
"language": "C#",
"type": "project",
"connectsoft-template": "microservice-base"
},
"symbols": {
"ServiceName": {
"type": "parameter",
"datatype": "string",
"defaultValue": "MyService",
"description": "Name of the microservice",
"displayName": "Service Name",
"replaces": "ServiceName"
},
"RootNamespace": {
"type": "parameter",
"datatype": "string",
"defaultValue": "MyCompany.MyService",
"description": "Root namespace for generated code",
"displayName": "Root Namespace",
"replaces": "RootNamespace"
},
"UseMassTransit": {
"type": "parameter",
"datatype": "bool",
"defaultValue": "true",
"description": "Enable MassTransit messaging",
"displayName": "Use MassTransit"
},
"PersistenceType": {
"type": "parameter",
"datatype": "choice",
"choices": [
{ "choice": "SqlServer", "description": "SQL Server" },
{ "choice": "PostgreSQL", "description": "PostgreSQL" }
],
"defaultValue": "SqlServer",
"description": "Persistence technology",
"displayName": "Persistence Type"
}
},
"postActions": [
{
"id": "restore",
"description": "Restore NuGet packages",
"manualInstructions": [
{ "text": "Run 'dotnet restore' to restore packages" }
],
"actionId": "210D431B-AF52-49D1-90D9-CCB93A37EFD7"
},
{
"id": "build",
"description": "Build the solution",
"manualInstructions": [
{ "text": "Run 'dotnet build' to build the solution" }
],
"actionId": "D396686C-DE45-4ED5-9664-E8E6BEF12E5D"
}
],
"primaryOutputs": [
{
"path": "src/Host/Host.csproj"
}
]
}
Extend Files in Specialized Templates¶
Specialized templates define extend files that provide deltas (additions and overrides) to the base template.json.
Extend File Structure¶
Extend files use a structured format to specify what to override, add, or modify:
IdentityBackendTemplate/
└── template/
├── identity.template.extend.json # Identity overlay metadata
└── worker.template.extend.json # Optional: Worker overlay metadata
Extend File Format¶
Extend files contain sections for different types of modifications:
{
"identityOverrides": {
// Override top-level fields from base template.json
},
"symbolOverrides": {
// Override existing symbols (parameters) from base
},
"symbolAdds": {
// Add new symbols (parameters) not in base
},
"postActionsAdds": [
// Add additional post-actions
],
"primaryOutputsAdds": [
// Add additional primary outputs
]
}
Merge Rules¶
The pack script applies merge rules in a specific order to compose the final template.json.
Merge Process Flow¶
flowchart TD
BASE[Load Base template.json]
EXTEND1[Load identity.template.extend.json]
EXTEND2[Load worker.template.extend.json]
MERGE[Merge Process]
STEP1[Apply identityOverrides]
STEP2[Apply symbolOverrides]
STEP3[Apply symbolAdds]
STEP4[Apply postActionsAdds]
STEP5[Apply primaryOutputsAdds]
OUTPUT[Final template.json]
BASE --> MERGE
EXTEND1 --> MERGE
EXTEND2 --> MERGE
MERGE --> STEP1
STEP1 --> STEP2
STEP2 --> STEP3
STEP3 --> STEP4
STEP4 --> STEP5
STEP5 --> OUTPUT
style BASE fill:#BBDEFB
style EXTEND1 fill:#C8E6C9
style EXTEND2 fill:#FFF9C4
style OUTPUT fill:#A5D6A7
identityOverrides¶
Purpose: Override top-level fields from base template.json.
Fields that can be overridden:
name- Template display nameshortName- Template short namedescription- Template descriptiontags- Template tags (merged, not replaced)author- Template authorclassifications- Template classifications (merged)
Example:
{
"identityOverrides": {
"name": "ConnectSoft Identity Backend Service",
"shortName": "cs-identity-backend",
"description": "Identity Backend built on ConnectSoft base microservice with user management, authentication, and authorization.",
"tags": {
"domain": "identity",
"connectsoft-template": "identity-backend"
},
"classifications": ["ConnectSoft", "Microservice", "Identity", "Authentication"]
}
}
Merge Behavior:
- Simple fields (name, shortName, description) are replaced
- Tags are merged (base tags + identity tags)
- Classifications are merged (base + identity)
symbolOverrides¶
Purpose: Override existing symbols (parameters) from base template.json.
What can be overridden:
defaultValue- Default value for the parameterdescription- Parameter descriptiondisplayName- Display name for the parameterchoices- Available choices (for choice parameters)
Example:
{
"symbolOverrides": {
"ServiceName": {
"defaultValue": "IdentityBackendService",
"description": "Name of the Identity Backend microservice"
},
"RootNamespace": {
"defaultValue": "ConnectSoft.IdentityBackend",
"description": "Root namespace for Identity Backend code"
},
"PersistenceType": {
"defaultValue": "SqlServer",
"description": "Persistence technology for Identity data (SQL Server recommended)"
}
}
}
Merge Behavior:
- Only specified fields are overridden
- Unspecified fields retain base values
- Choices arrays are replaced (not merged)
symbolAdds¶
Purpose: Add new symbols (parameters) not present in base template.json.
Example:
{
"symbolAdds": {
"UseExternalIdp": {
"type": "parameter",
"datatype": "bool",
"defaultValue": "false",
"description": "Configure external identity provider (Azure AD, Google, etc.)",
"displayName": "Use External IdP",
"replaces": "UseExternalIdp"
},
"RequireEmailConfirmation": {
"type": "parameter",
"datatype": "bool",
"defaultValue": "true",
"description": "Require email confirmation for new user registrations",
"displayName": "Require Email Confirmation",
"replaces": "RequireEmailConfirmation"
},
"IdentityProviderType": {
"type": "parameter",
"datatype": "choice",
"choices": [
{ "choice": "Local", "description": "Local identity provider" },
{ "choice": "AzureAD", "description": "Azure Active Directory" },
{ "choice": "Google", "description": "Google OAuth" }
],
"defaultValue": "Local",
"description": "Type of identity provider to use",
"displayName": "Identity Provider Type",
"replaces": "IdentityProviderType"
}
}
}
Merge Behavior:
- New symbols are added to the symbols collection
- No conflicts should exist (validation checks for duplicates)
postActionsAdds¶
Purpose: Add additional post-actions beyond those in base template.json.
Example:
{
"postActionsAdds": [
{
"id": "run-identity-migrations",
"description": "Run Identity database migrations",
"manualInstructions": [
{ "text": "Run 'dotnet ef database update' in Identity.Infrastructure project" }
],
"actionId": "D396686C-DE45-4ED5-9664-E8E6BEF12E5D",
"args": {
"executable": "dotnet",
"args": "ef database update --project src/Identity.Infrastructure/Identity.Infrastructure.csproj"
}
},
{
"id": "setup-identity-seed-data",
"description": "Seed Identity database with initial data",
"manualInstructions": [
{ "text": "Run the seed data script: dotnet run --project src/Identity.Infrastructure -- seed" }
]
}
]
}
Merge Behavior:
- Post-actions are appended to base post-actions
- Order: base post-actions first, then overlay post-actions
primaryOutputsAdds¶
Purpose: Add additional primary outputs beyond those in base template.json.
Example:
{
"primaryOutputsAdds": [
{
"path": "src/Identity.Api/Identity.Api.csproj"
},
{
"path": "src/Identity.Domain/Identity.Domain.csproj"
}
]
}
Merge Behavior:
- Primary outputs are appended to base primary outputs
pack-template Script Behavior¶
The pack-template.ps1 script orchestrates the merge process.
Script Workflow¶
flowchart TD
START[Start pack-template.ps1]
LOADBASE[Load base template.json]
LOADEXTEND[Load extend files<br/>identity.template.extend.json<br/>worker.template.extend.json]
COPY[Copy base template folder<br/>to artifacts/template-working/]
MERGE[Merge Process]
APPLY1[Apply identityOverrides]
APPLY2[Apply symbolOverrides]
APPLY3[Apply symbolAdds]
APPLY4[Apply postActionsAdds]
APPLY5[Apply primaryOutputsAdds]
VALIDATE[Validate merged template.json]
WRITE[Write merged template.json<br/>to artifacts/template-working/]
PACK[Pack as NuGet package<br/>or Factory artifact]
START --> LOADBASE
LOADBASE --> LOADEXTEND
LOADEXTEND --> COPY
COPY --> MERGE
MERGE --> APPLY1
APPLY1 --> APPLY2
APPLY2 --> APPLY3
APPLY3 --> APPLY4
APPLY4 --> APPLY5
APPLY5 --> VALIDATE
VALIDATE --> WRITE
WRITE --> PACK
style START fill:#E3F2FD
style MERGE fill:#FFE0B2
style PACK fill:#A5D6A7
Script Pseudocode¶
# pack-template.ps1 (conceptual)
# 1. Load base template.json
$baseTemplate = Get-Content "base-template/template/template.json" | ConvertFrom-Json
# 2. Load extend files (from recipe or command line)
$identityExtend = Get-Content "template/identity.template.extend.json" | ConvertFrom-Json
$workerExtend = Get-Content "template/worker.template.extend.json" | ConvertFrom-Json
# 3. Copy base template folder to working directory
Copy-Item "base-template/template/*" -Destination "artifacts/template-working/" -Recurse
# 4. Apply identityOverrides
if ($identityExtend.identityOverrides) {
$baseTemplate.name = $identityExtend.identityOverrides.name
$baseTemplate.shortName = $identityExtend.identityOverrides.shortName
# ... merge tags, classifications
}
# 5. Apply symbolOverrides
if ($identityExtend.symbolOverrides) {
foreach ($symbolName in $identityExtend.symbolOverrides.PSObject.Properties.Name) {
$override = $identityExtend.symbolOverrides.$symbolName
if ($baseTemplate.symbols.$symbolName) {
# Merge override into base symbol
foreach ($prop in $override.PSObject.Properties.Name) {
$baseTemplate.symbols.$symbolName.$prop = $override.$prop
}
}
}
}
# 6. Apply symbolAdds
if ($identityExtend.symbolAdds) {
foreach ($symbolName in $identityExtend.symbolAdds.PSObject.Properties.Name) {
$baseTemplate.symbols.$symbolName = $identityExtend.symbolAdds.$symbolName
}
}
# 7. Apply worker overlay (if present)
# Repeat steps 4-6 for worker extend file
# 8. Apply postActionsAdds
if ($identityExtend.postActionsAdds) {
$baseTemplate.postActions += $identityExtend.postActionsAdds
}
# 9. Validate merged template.json
# Check for required fields, valid JSON schema, etc.
# 10. Write merged template.json
$baseTemplate | ConvertTo-Json -Depth 100 |
Set-Content "artifacts/template-working/template.json"
# 11. Pack as NuGet package or Factory artifact
dotnet pack artifacts/template-working/ -o artifacts/packages/
Multi-Overlay Metadata (Identity + Worker)¶
When combining multiple overlays, extend files are applied in recipe order.
Recipe Example¶
# Recipe: identity-worker-service.yaml
templateId: identity-worker-service
displayName: "Identity Backend with Worker"
layers:
- base: microservice/base
- overlay: microservice/overlays/identity-backend
- overlay: microservice/overlays/worker
Merge Order¶
- Load base template.json
- Apply Identity overlay:
- identityOverrides → override name, shortName, description
- symbolOverrides → override ServiceName default
- symbolAdds → add UseExternalIdp, RequireEmailConfirmation
- postActionsAdds → add identity migrations post-action
- Apply Worker overlay:
- identityOverrides → override name, description (further customization)
- symbolAdds → add WorkerEnabled, WorkerScheduleInterval
- postActionsAdds → add worker setup post-action
Result¶
Final template.json contains:
- Top-level fields: Overridden by Identity, then further customized by Worker
- Symbols: Base symbols + Identity additions + Worker additions
- Post-actions: Base post-actions + Identity post-actions + Worker post-actions
Multi-Overlay Diagram¶
flowchart LR
BASE[Base template.json]
IDENTITY[Identity Overlay<br/>identity.template.extend.json]
WORKER[Worker Overlay<br/>worker.template.extend.json]
MERGE[Merge Process]
FINAL[Final template.json<br/>Base + Identity + Worker]
BASE --> MERGE
IDENTITY --> MERGE
WORKER --> MERGE
MERGE --> FINAL
style BASE fill:#BBDEFB
style IDENTITY fill:#C8E6C9
style WORKER fill:#FFF9C4
style FINAL fill:#A5D6A7
Complete Example: Identity Template Extend File¶
Here's a complete example of an identity template extend file:
{
"identityOverrides": {
"name": "ConnectSoft Identity Backend Service",
"shortName": "cs-identity-backend",
"description": "Identity Backend microservice with user management, authentication, authorization, and multi-tenant support. Built on ConnectSoft base microservice template.",
"tags": {
"domain": "identity",
"connectsoft-template": "identity-backend",
"features": "authentication,authorization,user-management"
},
"classifications": [
"ConnectSoft",
"Microservice",
"Identity",
"Authentication",
"Authorization"
]
},
"symbolOverrides": {
"ServiceName": {
"defaultValue": "IdentityBackendService",
"description": "Name of the Identity Backend microservice"
},
"RootNamespace": {
"defaultValue": "ConnectSoft.IdentityBackend",
"description": "Root namespace for Identity Backend code"
}
},
"symbolAdds": {
"UseExternalIdp": {
"type": "parameter",
"datatype": "bool",
"defaultValue": "false",
"description": "Configure external identity provider (Azure AD, Google, Facebook, etc.)",
"displayName": "Use External IdP",
"replaces": "UseExternalIdp"
},
"RequireEmailConfirmation": {
"type": "parameter",
"datatype": "bool",
"defaultValue": "true",
"description": "Require email confirmation for new user registrations",
"displayName": "Require Email Confirmation",
"replaces": "RequireEmailConfirmation"
},
"IdentityProviderType": {
"type": "parameter",
"datatype": "choice",
"choices": [
{ "choice": "Local", "description": "Local identity provider with email/password" },
{ "choice": "AzureAD", "description": "Azure Active Directory" },
{ "choice": "Google", "description": "Google OAuth 2.0" },
{ "choice": "Facebook", "description": "Facebook OAuth 2.0" }
],
"defaultValue": "Local",
"description": "Type of identity provider to use",
"displayName": "Identity Provider Type",
"replaces": "IdentityProviderType"
},
"MultiTenantEnabled": {
"type": "parameter",
"datatype": "bool",
"defaultValue": "true",
"description": "Enable multi-tenant support (tenant isolation)",
"displayName": "Multi-Tenant Enabled",
"replaces": "MultiTenantEnabled"
}
},
"postActionsAdds": [
{
"id": "run-identity-migrations",
"description": "Run Identity database migrations",
"manualInstructions": [
{ "text": "Navigate to Identity.Infrastructure project" },
{ "text": "Run 'dotnet ef database update' to apply migrations" }
],
"actionId": "D396686C-DE45-4ED5-9664-E8E6BEF12E5D",
"args": {
"executable": "dotnet",
"args": "ef database update --project src/Identity.Infrastructure/Identity.Infrastructure.csproj"
}
},
{
"id": "setup-identity-seed-data",
"description": "Seed Identity database with initial admin user and roles",
"manualInstructions": [
{ "text": "Run the seed data script: dotnet run --project src/Identity.Infrastructure -- seed" },
{ "text": "Default admin credentials: admin@example.com / Admin123!" }
]
},
{
"id": "configure-external-idp",
"condition": "(UseExternalIdp == true)",
"description": "Configure external identity provider",
"manualInstructions": [
{ "text": "Update appsettings.json with external IdP credentials" },
{ "text": "Set IdentityProviderType in configuration" }
]
}
],
"primaryOutputsAdds": [
{
"path": "src/Identity.Api/Identity.Api.csproj"
},
{
"path": "src/Identity.Domain/Identity.Domain.csproj"
},
{
"path": "src/Identity.Infrastructure/Identity.Infrastructure.csproj"
}
]
}
Best Practices¶
Do's¶
✅ Use extend files - Never duplicate base parameters in specialized templates
✅ Override only what's needed - Don't override base parameters unnecessarily
✅ Use descriptive names - Make extend file names clear (identity.template.extend.json)
✅ Validate extend files - Ensure JSON is valid and follows schema
✅ Test merge process - Verify merged template.json is correct
✅ Document custom parameters - Add clear descriptions for new symbols
Don'ts¶
❌ Don't duplicate base parameters - Use symbolOverrides to change defaults, not duplicate
❌ Don't modify base template.json - Base is canonical, use extend files
❌ Don't create conflicting symbols - Ensure symbol names don't conflict
❌ Don't skip validation - Always validate merged template.json
❌ Don't hard-code values - Use parameters and symbols, not hard-coded strings
Related Documents¶
- Template Architecture Specification - Comprehensive technical specification covering template hierarchy, inheritance, JSON/CLI inheritance, build-time vs generation-time extension, and reference wiring mechanisms
- Template Overlays Specification - Detailed overlay system specification covering overlay architecture, application process, versioning, compatibility, and recipe system
- Template Layering and Reuse - Overview of the three-layer template architecture
- Metrics, Options and Testing Extensibility - How to extend base infrastructure
- Microservice Template Architecture - Architecture details of the microservice template
- Templates Overview - Overview of all ConnectSoft templates