Введение в микросервисы на C#
Микросервисная архитектура (MSA) — это подход к разработке программного обеспечения, при котором приложение строится как набор небольших, независимо развертываемых сервисов. Каждый сервис реализует одну бизнес-возможность, имеет собственную базу данных и может быть написан на разных языках. Однако в экосистеме .NET платформа C# и ASP.NET Core предоставляют одни из лучших инструментов для построения надежных микросервисов.
В этой статье мы рассмотрим полный цикл создания микросервисов на C#: от проектирования архитектуры до развертывания в контейнерах. Вы узнаете, как организовать взаимодействие между сервисами, обеспечить их отказоустойчивость и масштабируемость.
1. Проектирование архитектуры микросервисов на .NET
Первый шаг — правильно разбить монолитное приложение на сервисы. Хорошая практика — следовать принципу Domain-Driven Design (DDD) и выделять Bounded Contexts (ограниченные контексты).
1.1. Определение границ сервисов
Каждый микросервис должен отвечать за одну бизнес-сущность или процесс. Например, для интернет-магазина можно выделить:
- Catalog Service — управление товарами
- Order Service — обработка заказов
- Payment Service — платежи
- Notification Service — уведомления
Важно: каждый сервис владеет своей базой данных. Это предотвращает связность и позволяет независимо масштабировать сервисы.
1.2. Структура проекта ASP.NET Core
Типичный микросервис на C# состоит из нескольких слоев:
- API Layer — контроллеры и middleware
- Application Layer — CQRS, MediatR, команды и запросы
- Domain Layer — сущности, value objects, агрегаты
- Infrastructure Layer — репозитории, DbContext, внешние сервисы
Пример минимальной структуры каталога:
MyMicroservice/
├── MyMicroservice.Api/
│ └── Controllers/
├── MyMicroservice.Application/
│ └── Commands/
├── MyMicroservice.Domain/
│ └── Entities/
└── MyMicroservice.Infrastructure/
└── Data/
2. Реализация микросервиса на C#: практический пример
Создадим простой сервис для управления каталогом товаров с использованием ASP.NET Core 8 и Entity Framework Core.
2.1. Настройка WebApplication
В файле Program.cs настраиваем минимальный API:
var builder = WebApplication.CreateBuilder(args);
// Добавляем сервисы
builder.Services.AddDbContext<CatalogDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("CatalogDb")));
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly));
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Middleware
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
// Minimal API endpoint
app.MapGet("/api/products", async (IMediator mediator) =>
{
var products = await mediator.Send(new GetProductsQuery());
return Results.Ok(products);
});
app.Run();
2.2. Модель данных и CQRS
Создаем сущность Product:
public class Product
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public decimal Price { get; set; }
public string Category { get; set; } = string.Empty;
}
Используем паттерн CQRS с MediatR для разделения команд и запросов:
public record GetProductsQuery() : IRequest<List<Product>>;
public class GetProductsHandler : IRequestHandler<GetProductsQuery, List<Product>>
{
private readonly CatalogDbContext _context;
public GetProductsHandler(CatalogDbContext context)
{
_context = context;
}
public async Task<List<Product>> Handle(GetProductsQuery request, CancellationToken cancellationToken)
{
return await _context.Products.ToListAsync(cancellationToken);
}
}
3. Коммуникация между микросервисами
Существует два основных подхода: синхронный (HTTP/gRPC) и асинхронный (Message Broker). В .NET оба реализованы отлично.
3.1. Синхронное взаимодействие через gRPC
gRPC — современный протокол на HTTP/2 с высокой производительностью. Для C# используем Grpc.AspNetCore:
// Определение сервиса в .proto
service CatalogService {
rpc GetProduct (ProductRequest) returns (ProductResponse);
}
// Реализация на сервере
public class CatalogService : Catalog.CatalogBase
{
public override async Task<ProductResponse> GetProduct(ProductRequest request, ServerCallContext context)
{
var product = await _dbContext.Products.FindAsync(request.Id);
return new ProductResponse
{
Id = product.Id.ToString(),
Name = product.Name,
Price = (double)product.Price
};
}
}
3.2. Асинхронное взаимодействие через RabbitMQ
Для событийной архитектуры используем RabbitMQ с библиотекой MassTransit:
// Публикация события
public class OrderCreatedHandler : IRequestHandler<CreateOrderCommand, Guid>
{
private readonly IPublishEndpoint _publishEndpoint;
public async Task<Guid> Handle(CreateOrderCommand request, CancellationToken ct)
{
// Сохраняем заказ в БД
var orderId = Guid.NewGuid();
await _dbContext.Orders.AddAsync(new Order { Id = orderId, ... });
await _dbContext.SaveChangesAsync(ct);
// Публикуем событие
await _publishEndpoint.Publish(new OrderCreatedEvent
{
OrderId = orderId,
TotalAmount = request.TotalAmount
}, ct);
return orderId;
}
}
// Подписка в другом сервисе
public class OrderCreatedConsumer : IConsumer<OrderCreatedEvent>
{
public async Task Consume(ConsumeContext<OrderCreatedEvent> context)
{
// Отправляем уведомление, обновляем склад и т.д.
await _emailService.Send