The Networking layer standardizes how TaskHub services call each other (and external HTTP APIs). It enforces resilience, observability, and declarative configuration β services declare who they want to talk to in appsettings.json, not how.
HttpClient.appsettings.json. Code stays clean.DelegatingHandler, so internal calls preserve the callerβs identity.| Module | Purpose |
|---|---|
| Networking Abstractions | IClient marker, ClientBase, settings models β what every client implements. |
| Networking Implementation | Polly-backed AppHttpClient, BearerTokenHandler, options resolution, DI registration. |
A Client is a typed wrapper around a named HttpClient for one downstream service:
ββββββββββββββββββββββββββββββββββββββββββ
β IUsersClient (your code) β
β βββΊ HttpClient "Users" β
β ββ BaseUrl: https://users:7006β
β ββ Retry Γ 3 (exponential) β
β ββ Timeout: 750 ms β
β ββ BearerTokenHandler β
β ββ OTel HttpClient tracing β
ββββββββββββββββββββββββββββββββββββββββββ
The implementation module reads Networking:Services:Users:BaseUrl from config and merges it with Networking:Defaults (retry count, timeout, etc.) to produce effective options per client.
"Networking": {
"Defaults": {
"RetryCount": 3,
"TimeoutInMilliseconds": 750,
"RetryDelayInMilliseconds": 500,
"RetryStrategy": "Exponential",
"UserAgent": "task-service/1.0"
},
"Services": {
"Users": { "BaseUrl": "https://users:7006" },
"Plans": { "BaseUrl": "https://plans:7005" }
}
}
Services-specific overrides nest inside each service: "Users": { "BaseUrl": "...", "TimeoutInMilliseconds": 2000 }.
| You want to⦠| Use |
|---|---|
| Call another TaskHub service | Define IFooClient : IClient, implement against ClientBase |
| Call an external REST API | Same pattern β register the BaseUrl, get retry+timeout for free |
| Call something raw (no resilience) | Resolve IHttpClientFactory directly β but justify it |
BearerTokenHandler.