Using (CQRS • Saga • Azure Service Bus • Circuit Breaker • Adapter • Patterns & Best Practices)
TL;DR (What you'll build)
A cloud-ready microservices system where each microservice:
-
Is a .NET Core Web API (REST) exposing bounded-capability endpoints.
-
Uses CQRS: separate read model and write model (commands vs queries).
-
Coordinates complex business workflows using SAGA (via Azure Service Bus).
-
Communicates async via Azure Service Bus (topics/subscriptions or queues).
-
Uses Circuit Breaker (Polly) + retry/backoff to improve resilience.
-
Uses Adapter pattern for external systems and for infrastructure abstraction.
-
Front-end is an Angular SPA calling an API Gateway (or BFF) that routes to microservices.
Architecture overview (high level)
-
API Gateway / BFF (optional): Single public endpoint, routing, auth, aggregation.
-
Microservices (multiple): each with its own database (DB-per-service), own bounded context.
-
Example services:
Orders
,Payments
,Inventory
,Customers
,Shipping
.
-
-
Azure Service Bus: topics for publish/subscribe events, queues for commands/tasks.
-
CQRS: Commands handled by write-side; queries served by optimized read DB (e.g., read replica or denormalized store like CosmosDB/SQL).
-
Sagas: Orchestrate long-running transactions (compensations) via events/messages.
-
Polly: Circuit Breaker + Retry + Timeout for outbound HTTP/Bus calls.
-
Monitoring & Logging: Application Insights, centralized logs (ELK/Serilog sink).
-
CI/CD & Hosting: Build pipeline -> container images -> AKS / App Service.
Diagram (textual):
Core design principles
-
Single Responsibility & Bounded Contexts — keep microservices small and focused.
-
DB-per-service — avoid shared DB to reduce coupling.
-
Event-driven — communicate asynchronously where eventual consistency is acceptable.
-
Idempotency — message handlers must be idempotent.
-
Id-based correlation — include correlation IDs in messages for tracing.
-
Resilience — use bulkheads, circuit breakers, retries, timeouts.
-
Observability — structured logs, distributed tracing (W3C TraceContext).
Key patterns & how to apply them
1. CQRS (Command Query Responsibility Segregation)
-
Write side: handles commands (
POST /orders
→CreateOrderCommand
). Validations and state changes happen here. -
Read side: optimized projection used for queries (
GET /orders/123
). Read DB is updated by event subscribers. -
Implementation:
-
Commands go to controllers → Command Handlers (MediatR is common).
-
After state change, publish domain event (to Azure Service Bus).
-
Event consumers update read model (denormalized tables or NoSQL).
-
Example files/folders:
2. Saga (long-running transactions)
-
Use Saga to coordinate multi-step flows (e.g., order -> reserve inventory -> charge payment -> ship).
-
Two approaches:
-
Choreography: each service reacts to events. Simple but can be hard to reason about for complex flows.
-
Orchestration: Saga Orchestrator service sends commands to participants and listens for replies/events. Easier to visualize and control compensations.
-
-
Implementation with Azure Service Bus:
-
Orchestrator publishes
ReserveInventoryCommand
to a queue. -
Inventory service replies
InventoryReservedEvent
orInventoryReserveFailedEvent
. -
Orchestrator then issues
ChargePaymentCommand
orCompensateOrderCommand
.
-
-
Always plan for compensation (rollback-like steps) when a later step fails.
3. Azure Service Bus (Messaging)
-
Use Topics + Subscriptions for publish/subscribe; Queues for point-to-point commands.
-
Message schema:
-
MessageId
,CorrelationId
,CausationId
,MessageType
,Payload
.
-
-
Use dead-letter queues for poison messages and implement retries & DLQ handling.
4. Circuit Breaker & Resilience (Polly)
-
Wrap all outbound network calls (HTTP, DB, Service Bus operations) with retry + jittered backoff + circuit breaker.
-
Typical policy: Retry (3 attempts, exponential backoff) + Circuit Breaker (open after 5 failures for 30s) + Timeout (5s).
-
Example: use
HttpClientFactory
with Polly policies.
5. Adapter Pattern
-
Use adapters to isolate external/specific client libs (e.g., Azure Service Bus SDK, Payment Gateway SDK) from your domain.
-
Define an interface (e.g.,
IPaymentGateway
) and implementStripeAdapter
/DummyAdapter
. This supports testability and swapping providers.
6. Repository + Unit of Work (when needed)
-
Use repository for aggregate persistence; Unit of Work if multiple repositories within same DB transaction (but prefer small transactions).
7. Anti-Corruption Layer
-
When integrating with legacy systems, use an adapter/anti-corruption layer to map incompatible models.
Concrete implementation steps
1. Set up projects
Solution example:
2. Shared contracts & DTOs
Create a Shared/Messaging
project with message types (events/commands) used across services. Strongly-typed messages reduce coupling.
Example OrderCreatedEvent
:
3. Implement CQRS in a service (Orders example)
-
Use MediatR for intra-service command/handler pattern.
-
Controller:
-
CommandHandler:
4. Publish/Subscribe (Azure Service Bus)
-
Implement
IMessageBus
adapter that wraps Azure Service BusServiceBusClient
. -
For publishing events:
-
For subscriptions, create background services (hosted services) to process messages and project read model.
5. Saga Orchestration (example flow)
-
Orders
publishesOrderCreatedEvent
. -
Saga.Orchestrator
subscribes. OnOrderCreated
:-
Send
ReserveInventoryCommand
toInventory
queue. -
Wait for
InventoryReservedEvent
orInventoryReserveFailedEvent
. -
If reserved, send
ChargePaymentCommand
toPayments
. -
If payment succeeds, send
ShipOrderCommand
toShipping
. -
If any step fails, run compensating actions (e.g.,
ReleaseInventoryCommand
,RefundPaymentCommand
) and mark orderFailed
.
-
Orchestrator pattern: implement state persistence for saga instances (e.g., in CosmosDB/SQL). Store Saga state keyed by CorrelationId
.
6. Circuit Breaker with HttpClientFactory + Polly
Register in Program.cs
:
7. Angular front-end (simple BFF pattern)
-
Angular calls API Gateway/BFF endpoints which route to microservices.
-
Use typed services, interceptors for auth (JWT) and correlation headers.
Example Angular service:
Add an HTTP interceptor to send x-correlation-id
header and Authorization bearer token.
8. Idempotency & Message deduplication
-
Keep
ProcessedMessage
table withMessageId
to ignore duplicate message deliveries. -
Use Service Bus message-id + dedupe logic.
9. Testing
-
Unit test command handlers and domain logic.
-
Integration tests for message flows (use in-memory or test instance of Service Bus or use the Azure Service Bus emulator).
-
Contract tests for message contracts.
10. Observability, Monitoring & Health
-
Use Application Insights (or OpenTelemetry) for distributed tracing and metrics.
-
Add Health Checks endpoints and Kubernetes liveness/readiness probes.
-
Structured logging with Serilog, sink to central store.
Deployment & CI/CD (short)
-
Dockerize each service.
-
Build pipeline: build -> test -> image -> push to ACR.
-
Deploy pipeline: pull image -> AKS (Kubernetes manifests) or Azure App Service.
-
Use Helm charts for Kubernetes; include config via ConfigMaps and secrets via Azure Key Vault.
-
Use canary / blue-green with ingress controller and feature flags (LaunchDarkly or open-source).
Practical tips & pitfalls
-
Do not share a single database across services — this creates high coupling.
-
Prefer eventual consistency; design UIs to show pending states (e.g., "Payment Processing").
-
Compensation is not rollback — design compensating actions carefully.
-
Keep messages small and versionable — maintain backward compatibility.
-
Start with choreography for simple flows; move to orchestrator for complex flows requiring centralized control.
-
Use feature toggles while deploying new message flows.
Minimal code sample: publish event to Service Bus (adapter)
Folder structure suggestion (service-level)
Checklist before go-live
-
Message contracts versioned & documented.
-
Idempotency & DLQ handled.
-
Circuit breakers instrumented.
-
Health checks + readiness gates.
-
Logging and distributed tracing configured.
-
Saga persistence & compensations tested.
-
Secure secrets (Key Vault) and communication (TLS, JWT).
Suggested next steps (practical small milestones)
-
Create a
Shared.Messaging
project with event/command contracts. -
Scaffold
Orders
service with basic create order command and publisher (no Saga yet). -
Create
Inventory
service subscriber to update read model. -
Add Azure Service Bus adapter and test end-to-end message flow locally (or in dev subscription).
-
Implement a simple saga orchestrator for a 3-step flow (inventory → payment → shipping).
-
Add Polly policies and instrument logging/tracing.
-
Dockerize and deploy to a dev cluster.
No comments:
Post a Comment