ADR-0003: Metrics, Options and Testing Extensibility¶
- Status: accepted
- Deciders: ConnectSoft Architecture Team
- Date: 2026-01-15
Context and Problem Statement¶
ConnectSoft templates need to support domain-specific functionality (metrics, configuration options, testing patterns) without modifying the base template. Specialized templates (Identity, Auth, Audit, etc.) need to:
- Add domain-specific metrics (login success/failure counters, authentication duration, etc.)
- Define domain-specific configuration options (password policies, lockout settings, etc.)
- Implement domain-specific tests while reusing base test infrastructure
The base template must remain domain-agnostic and never contain Identity, Auth, or Audit-specific logic.
Decision Drivers¶
- Base template must remain domain-agnostic
- Specialized templates need to add domain functionality without modifying base
- Need for plugin-style architecture where domains plug into base infrastructure
- Requirement to reuse common test patterns while allowing domain-specific tests
- Need for auto-discovery of domain implementations
Considered Options¶
Option 1: Modify Base Template for Each Domain¶
Approach: Add Identity/Auth/Audit-specific code directly to base template.
Pros: - Simple to implement - All functionality in one place
Cons: - Base template becomes domain-specific - Violates separation of concerns - Base template grows unbounded - New domains require base template changes - Rejected - Violates core principle of domain-agnostic base
Option 2: Copy Base Infrastructure in Each Template¶
Approach: Each specialized template copies and modifies base metrics/options infrastructure.
Pros: - Templates are independent - No base template modifications
Cons: - Code duplication - Base improvements don't propagate - Maintenance burden - Rejected - Creates duplication problem
Option 3: Extension Point Interfaces (Chosen)¶
Approach: Base provides extension point interfaces, specialized templates implement them.
Pros: - Base remains domain-agnostic - Domain functionality plugs into base via interfaces - Auto-discovery via dependency injection scanning - Base improvements benefit all domains - Clear separation of concerns
Cons: - Requires understanding of extension point pattern - Slightly more complex than direct modification
Option 4: Configuration-Based Plugins¶
Approach: Domain functionality configured via JSON/YAML, loaded dynamically.
Pros: - Very flexible - No code changes for new domains
Cons: - Complex runtime loading - Type safety issues - Difficult to test - Overkill for our use case
Decision Outcome¶
Chosen option: Option 3: Extension Point Interfaces, because it provides:
- Domain-agnostic base template
- Plugin-style architecture
- Type-safe extensions
- Auto-discovery via DI scanning
- Reusable test infrastructure
Metrics Extension Point: IMetricsFeature¶
Base Infrastructure:
Base Registration:
services.Scan(scan => scan
.FromApplicationDependencies()
.AddClasses(c => c.AssignableTo<IMetricsFeature>())
.AsImplementedInterfaces()
.WithSingletonLifetime());
Domain Implementation:
public sealed class IdentityMetricsFeature : IMetricsFeature
{
public void Register(IMeterFactory meterFactory)
{
// Register Identity-specific metrics
}
}
Options Extension Point: IConfigureOptions¶
Base Infrastructure:
services.Scan(scan => scan
.FromApplicationDependencies()
.AddClasses(c => c.AssignableTo(typeof(IConfigureOptions<>)))
.AsImplementedInterfaces());
Domain Implementation:
public sealed class ConfigureIdentitySecurityOptions
: IConfigureOptions<IdentitySecurityOptions>
{
public void Configure(IdentitySecurityOptions options)
{
// Configure Identity-specific options
}
}
Testing Extension Point: ITestAppFactory¶
Base Infrastructure:
Base Test Classes:
public abstract class AcceptanceTestBase
{
protected abstract ITestAppFactory AppFactory { get; }
// Base test methods
}
Domain Implementation:
public sealed class IdentityTestAppFactory : ITestAppFactory
{
// Identity-specific test setup
}
public class IdentityHealthChecksTests : AcceptanceTestBase
{
protected override ITestAppFactory AppFactory => new IdentityTestAppFactory();
}
Positive Consequences¶
- Domain-Agnostic Base - Base template contains no Identity/Auth/Audit logic
- Plugin Architecture - Domains plug into base via well-defined interfaces
- Auto-Discovery - Base infrastructure automatically discovers domain implementations
- Type Safety - Extension points are type-safe interfaces
- Test Reuse - Base test infrastructure reused by all domains
- Maintainability - Base improvements benefit all domains automatically
- Flexibility - New domains can be added without modifying base
Negative Consequences¶
- Learning Curve - Team must understand extension point pattern
- Interface Design - Extension point interfaces must be well-designed
- DI Scanning - Requires dependency injection scanning (performance consideration)
- Documentation - Must document extension points clearly
Implementation Notes¶
Metrics Extension Pattern¶
- Base provides
IMetricsFeatureinterface - Base auto-discovers implementations via DI scanning
- Domain implements
IMetricsFeaturewith domain-specific metrics - Base bootstrapper calls
Register()on all implementations
Options Extension Pattern¶
- Base provides
IConfigureOptions<T>scanning - Domain defines options class (e.g.,
IdentitySecurityOptions) - Domain implements
IConfigureOptions<IdentitySecurityOptions> - Base auto-discovers and registers configuration
Testing Extension Pattern¶
- Base provides
ITestAppFactoryinterface andAcceptanceTestBaseclass - Domain implements
ITestAppFactorywith domain-specific test setup - Domain tests extend
AcceptanceTestBaseand provide factory - Base test methods (health checks, etc.) work automatically
Rules¶
- Base never references domains - Base should not have
using Identity.*or similar - Domains implement extension points - All domain functionality via interfaces
- Auto-discovery preferred - Use DI scanning, not manual registration
- Test infrastructure reusable - Base provides test helpers, domains use them
Alternatives Considered¶
See "Considered Options" section above. We evaluated modifying base template, copying infrastructure, and configuration-based plugins, but extension point interfaces provided the best balance.
Links¶
- Template Architecture Overview - Business overview of the template architecture
- Extensibility Guide - Detailed guide to extension point patterns
- Microsoft.Extensions.Options Documentation