TaskHub.Shared

TaskHub.Shared.Networking - Exhaustive Technical Manual

The TaskHub.Shared.Networking module is a high-performance, resilience-oriented inter-service communication stack. It provides a declarative framework for managing HttpClient instances, Polly-based retry policies, and automatic security context propagation.

🏛 Deep Architecture

1. The Declarative Registration Lifecycle

The module uses a “Configuration-First” approach. When AddAppNetworkingSettings is called:

  1. Options Binding: It resolves the NetworkOptions containing global Defaults and a dictionary of named Services.
  2. Effective Options Resolution: For every named service, the OptionsResolver merges global defaults with service-specific overrides (e.g., a specific service might need a longer timeout than the default).
  3. HttpClient Factory Wiring: For each service node, it registers a named HttpClient in the standard .NET IHttpClientFactory.
  4. Automatic Resilience: It injects a Polly AsyncRetryPolicy directly into the message handler pipeline of each client.
  5. Security Propagation: It automatically adds the BearerTokenHandler to forward authentication headers from the incoming request to the outgoing call.

2. Resilience Engine (Polly Implementation)

The module uses HttpPolicyExtensions.HandleTransientHttpError() to catch:

Retry Strategies:

3. Identity Propagation

The BearerTokenHandler is a DelegatingHandler that intercepts outgoing requests. It extracts the Authorization header from the current HttpContext and injects it into the outgoing HttpRequestMessage. This ensures that user identity is maintained throughout the microservice call chain without manual token management in business logic.


🛠 API Reference

AddAppNetworkingSettings(Action<NetworkOptions> action)

| Parameter | Type | Description | | :— | :— | :— | | action | Action<NetworkOptions> | Delegate to configure global defaults and service nodes. |

NetworkOptions (Configuration Schema)

| Property | Type | Description | | :— | :— | :— | | Defaults | NetworkDefaults | Global fallback settings for all clients. | | Services | Dictionary<string, NetworkNode> | Map of named service configurations. |

NodeSettings (Common Properties for Defaults and Nodes)

| Property | Type | Description | | :— | :— | :— | | BaseUrl | string | (Node Only) The base URI for the remote service. | | RetryCount | int | Number of retry attempts (Default: 3). | | RetryDelayInMilliseconds | int | Base delay between retries (Default: 100ms). | | TimeoutInMilliseconds | int | Global timeout for the request (Default: 5000ms). | | RetryStrategy | RetryStrategy | Fixed or Exponential. | | UserAgent | string | The string sent in the User-Agent header. |


🚀 Complex Implementation Examples

1. Multi-Node Configuration with Overrides

builder.Services.AddAppNetworkingSettings(options => 
{
    // 1. Global Resilience Defaults
    options.Defaults = new NetworkDefaults 
    {
        RetryCount = 2,
        RetryStrategy = RetryStrategy.Exponential,
        TimeoutInMilliseconds = 3000,
        UserAgent = "TaskHub.Gateway/v2"
    };

    // 2. High-Priority Internal Service (Strict)
    options.Services.Add("IdentityService", new NetworkNode 
    {
        BaseUrl = "https://identity-internal:5001",
        RetryCount = 1, // Fail fast
        TimeoutInMilliseconds = 1000 
    });

    // 3. Unreliable Third-Party API (Relaxed)
    options.Services.Add("LegacyLegacyAPI", new NetworkNode 
    {
        BaseUrl = "https://legacy-vendor.com/api",
        RetryCount = 5,
        RetryDelayInMilliseconds = 500,
        RetryStrategy = RetryStrategy.Fixed,
        TimeoutInMilliseconds = 20000 
    });
});

2. Using the IClient Marker for Auto-DI

The networking module automatically registers all IClient implementations.

// Define your client contract
public interface IPaymentClient : IClient;

// Implement using the named HttpClient
public class StripeClient(IHttpClientFactory factory) : IPaymentClient
{
    private readonly HttpClient _http = factory.CreateClient("StripeNode");

    public async Task<Result> ChargeAsync(decimal amount, CancellationToken ct)
    {
        var res = await _http.PostAsJsonAsync("/v1/charges", new { amount }, ct);
        return res.IsSuccessStatusCode ? ResultFactory.OnSuccess() : ResultFactory.OnFailed();
    }
}

⚙️ Configuration Schema (appsettings.json)

"Networking": {
  "Defaults": {
    "RetryCount": 3,
    "RetryDelayInMilliseconds": 200,
    "RetryStrategy": "Exponential",
    "TimeoutInMilliseconds": 10000,
    "UserAgent": "TaskHub-Microservice"
  },
  "Services": {
    "OrderService": {
      "BaseUrl": "http://orders:8080"
    },
    "EmailProvider": {
      "BaseUrl": "https://api.sendgrid.com",
      "RetryCount": 10,
      "RetryStrategy": "Fixed",
      "TimeoutInMilliseconds": 30000
    }
  }
}

👁 Telemetry & Diagnostics

OpenTelemetry Spans

All outgoing requests are automatically instrumented via HttpClientInstrumentation:

Custom Polly Tags

The module attaches resilience metadata to the current Activity:

Metrics


✅ Best Practices & Anti-Patterns

🟢 Best Practices

🔴 Anti-Patterns