ADR-00xy: 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¶
- Metrics, Options and Testing Extensibility - Detailed guide to extension point patterns
- Template Layering and Reuse - Overview of the three-layer architecture
- Microsoft.Extensions.Options Documentation