Architecture of the ConnectSoft API Library Template¶
The ConnectSoft API Library Template is designed with a focus on creating robust, maintainable, and scalable API service agent libraries. It embodies several architectural principles and patterns to ensure that generated projects adhere to high standards of quality and reusability. This document outlines the core architectural decisions and the resulting structure.
Design Principles¶
The template is built upon the following fundamental design principles:
-
Service Agent Pattern:
- Encapsulates API interaction logic in dedicated service classes.
- Provides a clean abstraction layer between consumers and external APIs.
- Enables easy testing and mocking.
-
HttpClientFactory Pattern:
- Proper HTTP client lifecycle management.
- Connection pooling and DNS update handling.
- Thread-safe HTTP client instances.
-
Options Pattern:
- Strongly-typed configuration with validation.
- Separation of configuration from implementation.
- Support for nested and complex configuration structures.
-
Resilience First:
- Built-in fault tolerance mechanisms.
- Configurable retry, circuit breaker, and timeout strategies.
- Chaos injection for testing resiliency.
-
Observability:
- Comprehensive logging, metrics, and tracing support.
- Integration with OpenTelemetry for production monitoring.
- Error tracking and performance monitoring.
-
Testability:
- Interface-based design for easy mocking.
- Mock server integration for integration testing.
- Comprehensive unit test structure.
-
Dependency Injection:
- Loose coupling through DI container.
- Extension methods for easy service registration.
- Support for scoped, transient, and singleton lifetimes.
Project Structure and Organization¶
The generated solution typically consists of two main projects: the core API library project and its associated unit test project.
graph TD
A[Solution Root] --> B[src/API Library Project]
A --> C[tests/Unit Test Project]
A --> D[Configuration Files]
A --> E[CI/CD Pipeline]
subgraph "API Library Project"
B1[Service Interface]
B2[Service Implementation]
B3[Request/Response Models]
B4[Exception Classes]
B5[DI Extensions]
B6[Options Classes]
B7[Metrics Classes]
B8[Handlers Folder]
B9[Resilience Folder]
end
subgraph "Unit Test Project"
C1[Unit Tests]
C2[Mock Server Tests]
C3[Metrics Tests]
C4[Test Configuration]
end
B --> B1
B --> B2
B --> B3
B --> B4
B --> B5
B --> B6
B --> B7
B --> B8
B --> B9
C --> C1
C --> C2
C --> C3
C --> C4
Core Library Project (src/{ProjectName}/)¶
- Purpose: Contains the primary API client logic, service implementations, and supporting infrastructure.
- Key Components:
- Service Interface (
I{ServiceName}Service.cs): Defines the API contract. - Service Implementation (
{ServiceName}Service.cs): HTTP client-based implementation. - Request/Response Models: Data transfer objects for API communication.
- Exception Classes: Custom exceptions for API-specific errors.
- DI Extensions (
{ServiceName}ServiceExtensions.cs): Service registration and configuration. - Options Classes (
Options/): Configuration options with validation. - Metrics Classes (
Metrics/, conditional): Custom metrics implementation. - Handlers Folder (empty): For custom HTTP message handlers.
- Resilience Folder (empty): For custom resilience policies.
- Service Interface (
Unit Test Project (tests/{ProjectName}.UnitTests/)¶
- Purpose: Contains unit tests, integration tests, and mock server tests.
- Key Components:
- Unit Tests: Tests for individual service methods.
- Mock Server Tests: Tests using WireMock.Net for API simulation.
- Metrics Tests (conditional): Tests for metrics instrumentation.
- Test Configuration:
appsettings.UnitTests.jsonandappsettings.MockedServerUnitTests.json.
HTTP Client Architecture¶
HttpClientFactory Pattern¶
The template uses HttpClientFactory to manage HTTP client instances:
services.AddHttpClient<I{ServiceName}Service, {ServiceName}Service>(configureClient =>
{
client.BaseAddress = new Uri(options.BaseUrl);
client.Timeout = options.DefaultTimeout;
// ... additional configuration
});
Benefits¶
- Connection Pooling: Reuses HTTP connections efficiently.
- DNS Updates: Handles DNS changes without restarting the application.
- Lifecycle Management: Proper disposal and resource management.
- Thread Safety: Thread-safe instances for concurrent use.
Typed Client Pattern¶
The template implements the typed client pattern:
public interface IGoogleAnalyticsService
{
Task<GoogleAnalyticsResult> GetDataAsync(CancellationToken cancellationToken = default);
}
public class GoogleAnalyticsService : IGoogleAnalyticsService
{
private readonly HttpClient _httpClient;
public GoogleAnalyticsService(HttpClient httpClient)
{
_httpClient = httpClient;
}
// Implementation
}
Benefits¶
- Type Safety: Compile-time type checking.
- IntelliSense: Full IDE support for API methods.
- Testability: Easy to mock interfaces for testing.
- Dependency Injection: Seamless integration with DI container.
Request/Response Handling¶
The template uses HttpRequestMessage and HttpResponseMessage for flexible request handling:
var request = new HttpRequestMessage(HttpMethod.Get, "/api/data");
var response = await _httpClient.SendAsync(request, cancellationToken);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStreamAsync(cancellationToken);
Benefits¶
- Flexibility: Full control over HTTP method, headers, and content.
- Per-Request Headers: Headers can be set per request, not just on the client.
- Streaming: Support for streaming large responses.
- Cancellation: Built-in cancellation token support.
Service Agent Pattern Implementation¶
Pattern Overview¶
The Service Agent pattern encapsulates API interaction logic in dedicated service classes:
- Interface Definition: Service contract defined through interfaces.
- Implementation: HTTP client-based implementation.
- Abstraction: Consumers interact with interfaces, not HTTP clients directly.
- Configuration: Service configuration through options pattern.
Service Registration¶
Services are registered using extension methods:
// Register options
services.AddGoogleAnalyticsOptions(configuration);
// Register metrics (if UseMetrics=true)
services.AddGoogleAnalyticsMetrics();
// Register service
services.AddGoogleAnalyticsService();
Service Usage¶
Consumers inject the service interface:
public class MyController : ControllerBase
{
private readonly IGoogleAnalyticsService _service;
public MyController(IGoogleAnalyticsService service)
{
_service = service;
}
[HttpGet]
public async Task<IActionResult> Get()
{
var result = await _service.GetDataAsync();
return Ok(result);
}
}
Namespace Conventions¶
The template follows consistent namespace conventions:
- Root Namespace:
{ProjectName}(e.g.,ConnectSoft.GoogleAnalytics) - Options Namespace:
{ProjectName}.Options - Metrics Namespace:
{ProjectName}.Metrics(ifUseMetrics=true) - Test Namespace:
{ProjectName}.UnitTests
Example¶
namespace ConnectSoft.GoogleAnalytics
{
public interface IGoogleAnalyticsService { }
public class GoogleAnalyticsService { }
}
namespace ConnectSoft.GoogleAnalytics.Options
{
public class GoogleAnalyticsServiceOptions { }
}
namespace ConnectSoft.GoogleAnalytics.Metrics
{
public class GoogleAnalyticsServiceMetrics { }
}
Code Organization Patterns¶
File Organization¶
- One Class Per File: Each class in its own file.
- Folder by Feature: Related files grouped in folders (Options, Metrics, etc.).
- Naming Consistency: Files named after their primary class.
Class Organization¶
- Service Classes: Interface and implementation in root namespace.
- Options Classes: All options classes in
Optionsfolder. - Metrics Classes: Metrics classes in
Metricsfolder (ifUseMetrics=true). - Exception Classes: Exception classes in root namespace.
Extension Methods¶
Extension methods are organized in a single file:
- File Name:
{ServiceName}ServiceExtensions.cs - Namespace: Root namespace
- Methods:
Add{ServiceName}Options(): Options registrationAdd{ServiceName}Metrics(): Metrics registration (ifUseMetrics=true)Add{ServiceName}Service(): Service registration
Dependency Management Strategy¶
Central Package Management (CPM)¶
The template uses Central Package Management for consistent package versioning:
- Single Source of Truth: Package versions defined in
Directory.Packages.props. - Consistent Versions: All projects use the same package versions.
- Easy Upgrades: Update versions in one place.
Package Organization¶
Packages are organized by purpose:
- Core Libraries:
Microsoft.Extensions.*packages - HTTP Libraries:
Microsoft.Extensions.Http,Microsoft.Extensions.Http.Resilience - Resilience:
Polly.Core - Authentication:
ConnectSoft.Extensions.Http.OAuth2(conditional) - Metrics:
Microsoft.Extensions.Diagnostics(conditional) - Testing:
WireMock.Net,MSTest.* - Analyzers: Static code analyzers
Testing Strategy and Structure¶
Unit Testing¶
- Framework: MSTest
- Organization: Tests mirror source structure
- Coverage: Code coverage collection enabled
- Mocking: Interface-based mocking
Integration Testing¶
- Mock Server: WireMock.Net for API simulation
- Configuration: Separate configuration files for test scenarios
- End-to-End: Tests with real or mocked APIs
Test Structure¶
tests/
└── {ProjectName}.UnitTests/
├── {ServiceName}ServiceUnitTests.cs
├── {ServiceName}ServiceWithMockedServerUnitTests.cs
├── Metrics/
│ └── {ServiceName}ServiceMetricsUnitTests.cs (if UseMetrics=true)
├── appsettings.UnitTests.json
└── appsettings.MockedServerUnitTests.json
Resilience Architecture¶
Resilience Handler Layers¶
The template implements layered resilience strategies:
-
Standard Resilience Handler:
- Bulkhead → Total Request Timeout → Retry → Circuit Breaker → Attempt Timeout
-
Standard Hedging Handler:
- Total Request Timeout → Hedging → Endpoint (Bulkhead → Circuit Breaker → Attempt Timeout)
Resilience Configuration¶
Resilience is configured through options:
{
"EnableHttpStandardResilience": true,
"HttpStandardResilience": {
"TotalRequestTimeout": { "Timeout": "00:00:30" },
"Retry": { /* retry options */ },
"CircuitBreaker": { /* circuit breaker options */ },
"AttemptTimeout": { "Timeout": "00:00:10" }
}
}
Authentication Architecture¶
Authentication Handler Pattern¶
Authentication is handled through HTTP client configuration:
- Basic Auth: Base64-encoded credentials in
Authorizationheader - API Key: API key in custom header
- OAuth2: Token acquisition and refresh via
ConnectSoft.Extensions.Http.OAuth2 - OpenID Connect: Token acquisition with validation
Authentication Flow¶
sequenceDiagram
participant Client
participant Service
participant AuthHandler
participant API
Client->>Service: Request
Service->>AuthHandler: Get Token (if needed)
AuthHandler->>API: Authenticated Request
API->>AuthHandler: Response
AuthHandler->>Service: Response
Service->>Client: Result
Metrics Architecture¶
Metrics Implementation¶
When UseMetrics=true, the template generates a metrics class:
- Meter: Created using
IMeterFactory - Instruments: Counter, Histogram for request tracking
- Tags: Status codes, error types for detailed tracking
Metrics Flow¶
graph LR
A[Service Method] --> B[Increment Counter]
A --> C[Start Stopwatch]
A --> D[API Call]
D --> E[Record Duration]
D --> F[Increment Failure Counter]
E --> G[OpenTelemetry]
F --> G
Documentation Structure¶
The template includes a documentation structure (if MkDocs is configured):
docs/
├── index.md
├── api-reference.md
├── authentication.md
├── configuration.md
├── features.md
└── getting-started.md
Best Practices¶
Code Organization¶
- Single Responsibility: Each class has a single, well-defined responsibility.
- Interface Segregation: Interfaces are focused and minimal.
- Dependency Inversion: Depend on abstractions, not concretions.
Error Handling¶
- Custom Exceptions: Use custom exceptions for API-specific errors.
- Error Context: Include context in exceptions for debugging.
- Logging: Log errors with structured logging.
Configuration¶
- Validation: Validate configuration at startup.
- Strongly-Typed: Use strongly-typed options classes.
- Environment-Specific: Support environment-specific configuration.
Testing¶
- Unit Tests: Test individual methods in isolation.
- Integration Tests: Test with mock servers for end-to-end scenarios.
- Chaos Tests: Validate resiliency under failure conditions.
Conclusion¶
The ConnectSoft API Library Template architecture provides a solid foundation for building robust, maintainable, and scalable API service agent libraries. By following established patterns and best practices, it ensures that generated projects are well-structured, testable, and production-ready.
The architecture emphasizes:
- Separation of Concerns: Clear boundaries between layers
- Testability: Easy to test and mock
- Observability: Comprehensive logging and metrics
- Resilience: Built-in fault tolerance
- Flexibility: Configurable and extensible
This comprehensive architectural approach ensures that any API library created with the ConnectSoft API Library Template is not only functional but also adheres to modern software engineering principles, making it easy to develop, maintain, and integrate into larger systems.