Skip to content

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:

  1. 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.
  2. HttpClientFactory Pattern:

    • Proper HTTP client lifecycle management.
    • Connection pooling and DNS update handling.
    • Thread-safe HTTP client instances.
  3. Options Pattern:

    • Strongly-typed configuration with validation.
    • Separation of configuration from implementation.
    • Support for nested and complex configuration structures.
  4. Resilience First:

    • Built-in fault tolerance mechanisms.
    • Configurable retry, circuit breaker, and timeout strategies.
    • Chaos injection for testing resiliency.
  5. Observability:

    • Comprehensive logging, metrics, and tracing support.
    • Integration with OpenTelemetry for production monitoring.
    • Error tracking and performance monitoring.
  6. Testability:

    • Interface-based design for easy mocking.
    • Mock server integration for integration testing.
    • Comprehensive unit test structure.
  7. 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
Hold "Alt" / "Option" to enable pan & zoom

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.

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.json and appsettings.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:

  1. Interface Definition: Service contract defined through interfaces.
  2. Implementation: HTTP client-based implementation.
  3. Abstraction: Consumers interact with interfaces, not HTTP clients directly.
  4. 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 (if UseMetrics=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 Options folder.
  • Metrics Classes: Metrics classes in Metrics folder (if UseMetrics=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 registration
    • Add{ServiceName}Metrics(): Metrics registration (if UseMetrics=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:

  1. Standard Resilience Handler:

    • Bulkhead → Total Request Timeout → Retry → Circuit Breaker → Attempt Timeout
  2. 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 Authorization header
  • 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
Hold "Alt" / "Option" to enable pan & zoom

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
Hold "Alt" / "Option" to enable pan & zoom

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

  1. Single Responsibility: Each class has a single, well-defined responsibility.
  2. Interface Segregation: Interfaces are focused and minimal.
  3. Dependency Inversion: Depend on abstractions, not concretions.

Error Handling

  1. Custom Exceptions: Use custom exceptions for API-specific errors.
  2. Error Context: Include context in exceptions for debugging.
  3. Logging: Log errors with structured logging.

Configuration

  1. Validation: Validate configuration at startup.
  2. Strongly-Typed: Use strongly-typed options classes.
  3. Environment-Specific: Support environment-specific configuration.

Testing

  1. Unit Tests: Test individual methods in isolation.
  2. Integration Tests: Test with mock servers for end-to-end scenarios.
  3. 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.