Architecture¶
Design principles, project structure, namespace conventions, and architectural patterns used in the ConnectSoft Library Template.
Design Principles¶
The template follows several key design principles to ensure maintainability, testability, and extensibility.
Clean Architecture¶
The template promotes separation of concerns with clear boundaries:
- Domain Logic: Core business logic in the main library project
- Infrastructure: Cross-cutting concerns (logging, metrics, configuration) as optional features
- Testing: Separate test project with clear test organization
- Dependencies: Dependency direction flows inward (tests → library → frameworks)
Domain-Driven Design (DDD)¶
While primarily a library template, DDD principles guide organization:
- Feature-Based Organization: Code organized by feature/domain (Options, Metrics, Diagnostics)
- Clear Boundaries: Each feature folder represents a bounded context
- Value Objects: Options classes represent configuration value objects
- Services: Metrics and Diagnostics represent domain services
Dependency Injection¶
DI is a first-class citizen (when enabled):
- Service Registration: Extension methods for
IServiceCollection - Constructor Injection: Preferred method for dependencies
- Interface-Based Design: Abstractions over concrete implementations
- Testability: Easy mocking and testing with DI
Configuration Pattern¶
Strongly-typed configuration with validation:
- Options Pattern:
IOptions<T>for configuration - Validation: DataAnnotations and source-generated validators
- Startup Validation: Configuration validated on application startup
- Type Safety: Compile-time checking of configuration
Observability First¶
Built-in support for observability:
- Structured Logging:
ILogger<T>for consistent logging - Metrics: OpenTelemetry-compatible metrics
- Tracing: ActivitySource for distributed tracing
- Correlation: Logs correlated with traces via trace IDs
Project Structure¶
Solution Organization¶
Solution Root
├── src/ # Source code
│ └── YourLibraryName/ # Main library project
├── tests/ # Test projects
│ └── YourLibraryName.UnitTests/ # Unit test project
└── docs/ # Documentation
Library Project Structure¶
src/YourLibraryName/
├── YourLibraryName.csproj # Project file
├── GlobalSuppressions.cs # Code analyzer suppressions
├── Options/ # Configuration options (if UseOptions=true)
│ ├── LibraryTemplateOptions.cs
│ └── ValidateLibraryTemplateOptions.cs
├── Metrics/ # Metrics implementation (if UseMetrics=true)
│ └── LibraryTemplateMetrics.cs
├── Diagnostics/ # Tracing implementation (if UseActivitySource=true)
│ └── LibraryTemplateDiagnostics.cs
└── Samples/ # Usage samples (if UseActivitySource=true)
└── TracingDemo.cs
Test Project Structure¶
tests/YourLibraryName.UnitTests/
├── YourLibraryName.UnitTests.csproj
├── GlobalSuppressions.cs
├── Properties/
│ └── AssemblyInfo.cs
├── Metrics/ # Metrics tests (if UseMetrics=true)
│ └── LibraryTemplateMetricsUnitTests.cs
└── Diagnostics/ # Diagnostics tests (if UseActivitySource=true)
└── LibraryTemplateDiagnosticsTests.cs
Namespace Conventions¶
Main Namespace¶
The main namespace matches the library name:
Feature Namespaces¶
Feature folders map to namespace segments:
// Options/ folder
namespace YourLibraryName.Options
{
public class LibraryTemplateOptions { }
}
// Metrics/ folder
namespace YourLibraryName.Metrics
{
public class LibraryTemplateMetrics { }
}
// Diagnostics/ folder
namespace YourLibraryName.Diagnostics
{
public static class LibraryTemplateDiagnostics { }
}
Test Namespaces¶
Test namespaces mirror source namespaces with .UnitTests suffix:
// Source: YourLibraryName.Metrics
// Test: YourLibraryName.Metrics.UnitTests
namespace YourLibraryName.Metrics.UnitTests
{
[TestClass]
public class LibraryTemplateMetricsUnitTests { }
}
Code Organization Patterns¶
Feature-Based Organization¶
Code is organized by feature/domain rather than by technical layer:
Benefits:
- Related code stays together
- Easy to find feature-specific code
- Clear feature boundaries
- Simplified navigation
Extension Methods Pattern¶
Service registration uses extension methods:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMyLibrary(
this IServiceCollection services,
IConfiguration configuration)
{
// Service registration
return services;
}
}
Benefits:
- Discoverable via IntelliSense
- Fluent API style
- Consistent with .NET patterns
- Easy to extend
Options Pattern¶
Configuration uses the Options pattern:
public class MyLibraryOptions
{
public const string SectionName = "MyLibrary";
[Required]
public required string ApiKey { get; set; }
}
Benefits:
- Strongly-typed configuration
- Validation support
- Environment-specific overrides
- Testable configuration
Metrics Pattern¶
Metrics use OpenTelemetry patterns:
public class MyLibraryMetrics
{
private readonly Counter<long> requestCounter;
public MyLibraryMetrics(IMeterFactory meterFactory)
{
var meter = meterFactory.Create("MyLibrary");
requestCounter = meter.CreateCounter<long>("mylibrary.requests.count");
}
}
Benefits:
- OpenTelemetry compatibility
- Standard metric types
- Integration with observability platforms
- Performance monitoring
Diagnostics Pattern¶
Tracing uses ActivitySource:
public static class MyLibraryDiagnostics
{
public const string ActivitySourceName = "MyLibrary";
public static readonly ActivitySource ActivitySource = new(ActivitySourceName);
public static Activity? StartActivity(string name, ...)
{
return ActivitySource.StartActivity(name, ActivityKind.Internal);
}
}
Benefits:
- OpenTelemetry compatibility
- Distributed tracing support
- Log correlation
- Performance profiling
Dependency Management Strategy¶
Central Package Management (CPM)¶
All package versions managed in Directory.Packages.props:
Benefits:
- Single source of truth
- Easier upgrades
- Consistent versions
- Cleaner project files
Conditional Package References¶
Packages included conditionally based on template parameters:
<ItemGroup Label="Logging Libraries" Condition="'$(UseLogging)' == 'true'">
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
</ItemGroup>
Benefits:
- Only include what's needed
- Smaller package size
- Clear dependencies
- Flexible feature selection
Package Categories¶
Packages organized by category:
- Testing Libraries: MSTest, coverlet
- Static Code Analyzers: StyleCop, SonarAnalyzer
- Logging Libraries: Microsoft.Extensions.Logging.*
- Options Libraries: Microsoft.Extensions.Options.*
- DI Libraries: Microsoft.Extensions.DependencyInjection.*
- Metrics Libraries: Microsoft.Extensions.Diagnostics
Testing Strategy¶
Test Project Organization¶
Tests mirror source project structure:
Source: src/YourLibraryName/Metrics/LibraryTemplateMetrics.cs
Test: tests/YourLibraryName.UnitTests/Metrics/LibraryTemplateMetricsUnitTests.cs
Benefits:
- Easy to find corresponding tests
- Clear test organization
- Maintainable test structure
Test Framework¶
Framework: MSTest
Coverage: coverlet.collector
Assertions: MSTest assertions
Example:
[TestClass]
public class LibraryTemplateMetricsUnitTests
{
[TestMethod]
public void IncrementRequestCounter_ShouldIncrementCounter()
{
// Arrange
var meterFactory = new TestMeterFactory();
var metrics = new LibraryTemplateMetrics(meterFactory);
// Act
metrics.IncrementRequestCounter();
// Assert
// Verify counter increment
}
}
Code Coverage¶
- Tool: coverlet.collector
- Format: Cobertura XML
- Threshold: Configurable (default: 0, recommended: 70+)
- Collection: Automatic during test runs
Documentation Structure¶
MkDocs Organization¶
Documentation organized by topic:
docs/
├── index.md # Home page
├── Overview.md # Template overview
├── Features.md # Feature documentation
├── GettingStarted.md # Getting started guide
├── UseCases.md # Use case examples
└── Runbook.md # Operations guide
Code Documentation¶
XML documentation comments for all public APIs:
/// <summary>
/// Provides custom application metrics for request counting.
/// </summary>
/// <see href="https://learn.microsoft.com/en-us/dotnet/core/diagnostics/metrics-instrumentation"/>
public class LibraryTemplateMetrics
{
/// <summary>
/// Increments the custom request counter by one.
/// </summary>
public void IncrementRequestCounter()
{
// Implementation
}
}
Design Patterns Used¶
Factory Pattern¶
Metrics use IMeterFactory for meter creation:
Singleton Pattern¶
Metrics classes typically registered as singletons:
Extension Method Pattern¶
Service registration uses extension methods:
Options Pattern¶
Configuration uses Options pattern:
Source Generator Pattern¶
Options validation uses source generators:
[OptionsValidator]
public partial class ValidateMyLibraryOptions : IValidateOptions<MyLibraryOptions>
{
}
Best Practices¶
Code Organization¶
- Feature-Based Folders: Organize by feature, not by technical layer
- One Class Per File: Each class in its own file
- Namespace Matching Folders: Namespace structure matches folder structure
- Clear Naming: Use descriptive, consistent names
Dependency Management¶
- Central Package Management: Use CPM for version management
- Conditional References: Only include packages that are needed
- Version Consistency: Keep versions consistent across projects
- Regular Updates: Regularly update package versions
Testing¶
- Test Organization: Mirror source structure in tests
- Test Naming: Use descriptive test method names
- Test Coverage: Aim for 70%+ code coverage
- Test Isolation: Each test should be independent
Documentation¶
- XML Comments: Document all public APIs
- README: Keep README.md up to date
- Examples: Include usage examples
- Architecture: Document architectural decisions
Related Documentation¶
- Generated Structure - File and folder structure
- Features - Feature implementation details
- Configuration - Options pattern deep dive
- Metrics & Observability - Observability patterns
- Development Guide - Development workflow