plugins/dotnet-blazor/skills/microservices-patterns/SKILL.md
.NET microservices patterns including service decomposition, communication, CQRS, saga, and resilience
npx skillsauth add markus41/claude microservices-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
E-Commerce Platform:
├── Catalog Service (products, categories, search)
├── Order Service (orders, order processing)
├── Payment Service (payment processing, refunds)
├── Inventory Service (stock management)
├── Notification Service (email, SMS, push)
├── Identity Service (authentication, users)
└── Gateway (API gateway, BFF)
Each service owns its data store. No shared databases.
// Catalog uses PostgreSQL
builder.AddNpgsqlDbContext<CatalogDbContext>("catalogdb");
// Order uses SQL Server
builder.AddSqlServerDbContext<OrderDbContext>("orderdb");
// Notification uses Cosmos DB
builder.AddCosmosDbContext<NotificationDbContext>("cosmosdb");
// Command side
public sealed record CreateOrderCommand(int CustomerId, List<OrderItemDto> Items);
public sealed class CreateOrderHandler(OrderDbContext db, IPublishEndpoint bus)
{
public async Task<int> HandleAsync(CreateOrderCommand command, CancellationToken ct)
{
var order = Order.Create(command.CustomerId, command.Items);
db.Orders.Add(order);
await db.SaveChangesAsync(ct);
await bus.Publish(new OrderCreatedEvent(order.Id, order.CustomerId), ct);
return order.Id;
}
}
// Query side (separate read model, possibly different DB)
public sealed class OrderQueryService(IReadOnlyDbContext readDb)
{
public async Task<OrderDetailDto?> GetByIdAsync(int id, CancellationToken ct) =>
await readDb.Orders
.AsNoTracking()
.Where(o => o.Id == id)
.Select(o => o.ToDetailDto())
.FirstOrDefaultAsync(ct);
}
// Using MassTransit state machine
public sealed class OrderSaga : MassTransitStateMachine<OrderSagaState>
{
public OrderSaga()
{
InstanceState(x => x.CurrentState);
Event(() => OrderCreated, x => x.CorrelateById(m => m.Message.OrderId));
Event(() => PaymentProcessed, x => x.CorrelateById(m => m.Message.OrderId));
Event(() => InventoryReserved, x => x.CorrelateById(m => m.Message.OrderId));
Initially(
When(OrderCreated)
.Then(ctx => ctx.Saga.OrderId = ctx.Message.OrderId)
.Publish(ctx => new ProcessPaymentCommand(ctx.Saga.OrderId))
.TransitionTo(AwaitingPayment));
During(AwaitingPayment,
When(PaymentProcessed)
.Publish(ctx => new ReserveInventoryCommand(ctx.Saga.OrderId))
.TransitionTo(AwaitingInventory),
When(PaymentFailed)
.Publish(ctx => new CancelOrderCommand(ctx.Saga.OrderId))
.TransitionTo(Failed));
During(AwaitingInventory,
When(InventoryReserved)
.Publish(ctx => new FulfillOrderCommand(ctx.Saga.OrderId))
.TransitionTo(Completed));
}
}
// Microsoft.Extensions.Resilience + Polly v8
builder.Services.AddHttpClient<ICatalogClient>(client =>
client.BaseAddress = new("https+http://catalog-api"))
.AddStandardResilienceHandler(); // Retry + Circuit Breaker + Timeout
// Custom pipeline
builder.Services.AddResiliencePipeline("custom", pipeline =>
{
pipeline
.AddRetry(new() { MaxRetryAttempts = 3, BackoffType = DelayBackoffType.Exponential })
.AddCircuitBreaker(new() { FailureRatio = 0.5, MinimumThroughput = 10 })
.AddTimeout(TimeSpan.FromSeconds(5));
});
builder.Services.AddHealthChecks()
.AddNpgSql(connectionString, name: "database")
.AddRedis(redisConnection, name: "cache")
.AddRabbitMQ(rabbitConnection, name: "messaging")
.AddCheck<CustomHealthCheck>("custom");
app.MapHealthChecks("/health/ready", new() { Predicate = check => check.Tags.Contains("ready") });
app.MapHealthChecks("/health/live", new() { Predicate = _ => false }); // Just checks app is running
| Pattern | Use when | Trade-offs | |---------|----------|------------| | Direct client-to-service | Few services, internal apps | Simple but couples clients to services | | API Gateway (YARP/Ocelot) | Many services, external clients | Single entry point, adds latency | | BFF (Backend for Frontend) | Multiple client types (web, mobile) | Client-optimized APIs, more gateways |
// YARP reverse proxy (Microsoft's recommended API gateway)
builder.Services.AddReverseProxy()
.LoadFromConfig(builder.Configuration.GetSection("ReverseProxy"));
app.MapReverseProxy();
Each service owns its data. Cross-service data needs are resolved via:
Order Service ──(event)──> Catalog Service
│ OrderDB │ CatalogDB
│ (orders, items) │ (products, stock)
│ │
└──(HTTP)──> Payment Service
│ PaymentDB
│ (payments, refunds)
// Integration events cross service boundaries
public abstract record IntegrationEvent
{
public Guid Id { get; } = Guid.NewGuid();
public DateTime CreatedAt { get; } = DateTime.UtcNow;
}
public sealed record OrderSubmittedIntegrationEvent(
int OrderId, int BuyerId, decimal Total) : IntegrationEvent;
// Publish via outbox pattern for reliability
public sealed class OutboxPublisher(AppDbContext db, IEventBus bus)
{
public async Task PublishPendingEventsAsync(CancellationToken ct)
{
var pending = await db.OutboxMessages
.Where(m => !m.Published)
.OrderBy(m => m.CreatedAt)
.Take(50)
.ToListAsync(ct);
foreach (var message in pending)
{
await bus.PublishAsync(message.Event, ct);
message.Published = true;
message.PublishedAt = DateTime.UtcNow;
}
await db.SaveChangesAsync(ct);
}
}
Each service can own a UI fragment:
@* Main Blazor app composes service-specific components *@
<CatalogProductList /> @* Owned by Catalog team *@
<OrderStatusWidget /> @* Owned by Order team *@
<CartSummary /> @* Owned by Cart team *@
Pattern options:
// JWT validation at API gateway
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://identity-service";
options.Audience = "catalog-api";
});
// Azure Key Vault for secrets
builder.Configuration.AddAzureKeyVault(
new Uri("https://myvault.vault.azure.net/"),
new DefaultAzureCredential());
development
Enhanced plan-authoring skill with Pre-Writing context gathering, task metadata, non-TDD templates, Red Flags, telemetry, and an automated plan linter. Use when you have a spec or requirements for a multi-step task, before touching code.
tools
Documentation intelligence engine with graph-based API docs, algorithm library, and drift detection
tools
Ultraplan cloud planning — kick off a plan in the cloud from your terminal, review and revise in the browser, then execute remotely or send back to CLI
tools
--- name: mcp description: Configure MCP servers for Claude Code — stdio vs HTTP, authentication, Tools/Resources/Prompts distinction, channels (CI webhook, mobile relay, Discord bridge, fakechat), and cost of always-loaded tools. Use this skill whenever adding an MCP server, debugging connection issues, choosing between MCP Tools vs Prompts vs Resources, installing channel servers, or managing .mcp.json. Triggers on: "MCP server", "mcp config", "add Obsidian MCP", "install context7", "channels"