← Документация
Koto.Application
CQRS-диспетчер, pipeline-поведения и абстракции межсервисной интеграции.
Установка
dotnet add package Koto.Application
Что внутри
| Тип | Назначение |
|---|---|
ICommand / ICommand<TResult> | Маркерные интерфейсы для операций записи (ICommandBase) |
IQuery<TResult> | Маркерный интерфейс для операций чтения (IQueryBase) |
ICommandHandler<T> / ICommandHandler<T, TResult> | Контракты обработчиков команд |
IQueryHandler<TQuery, TResult> | Контракт обработчика запроса |
ICqrsDispatcher | Отправляет команды и запросы зарегистрированным обработчикам |
IPipelineBehavior<TRequest, TResponse> | Middleware для CQRS-пайплайна |
KotoApplicationOptions | Опциональная регистрация поведений (AddLoggingBehavior(), AddTransactionBehavior(), AddBehavior(type)) |
LoggingBehavior<,> | Логирует каждую команду/запрос с замером времени |
TransactionBehavior<,> | Оборачивает каждую команду в транзакцию IUnitOfWork |
IUnitOfWork | Абстракция коммита транзакции БД |
IRepository<TAgg, TId> | Контракт персистентности для агрегатов (в паре с IUnitOfWork) |
IIntegrationEvent / IntegrationEvent | Контракт внешнего события |
IIntegrationCommand / IIntegrationCommand<TResult> | Контракт межсервисной команды |
IIntegrationEventPublisher | Публикует интеграционные события (реализация в инфраструктуре) |
IIntegrationCommandDispatcher | Отправляет интеграционные команды (реализация в инфраструктуре) |
AssemblyScanning | GetLoadableTypes — защита сканирования от ReflectionTypeLoadException |
Использование
Регистрация
// Только обработчики — без pipeline-поведений:
builder.Services.AddKotoApplication(typeof(Program).Assembly);
// С поведениями (опционально; порядок регистрации = порядок выполнения, первое — самое внешнее):
builder.Services.AddKotoApplication(
o => o.AddLoggingBehavior().AddTransactionBehavior(),
typeof(Program).Assembly);
builder.Services.AddKotoValidation(typeof(Program).Assembly); // ValidationBehavior из Koto.Validation
// Рекомендуемый порядок: Logging → Validation → Transaction.
Реализация команды
public sealed record PlaceOrderCommand(Guid CustomerId, List<OrderItem> Items)
: ICommand<OrderId>;
public sealed class PlaceOrderHandler : ICommandHandler<PlaceOrderCommand, OrderId>
{
public async Task<Result<OrderId>> HandleAsync(PlaceOrderCommand cmd, CancellationToken ct)
{
// бизнес-логика
}
}
Отправка
var result = await _dispatcher.SendAsync(new PlaceOrderCommand(customerId, items), ct);
Pipeline-поведения
Поведения резолвятся относительно конкретного типа команды/запроса: открытая
generic-регистрация вроде ValidationBehavior<,> замыкается на PlaceOrderCommand в момент
отправки, поэтому находится IValidator<PlaceOrderCommand>. Поведение также может быть
нацелено на один конкретный запрос через IPipelineBehavior<PlaceOrderCommand, Result<OrderId>>.
// Собственное открытое generic-поведение:
builder.Services.AddKotoApplication(
o => o.AddLoggingBehavior().AddBehavior(typeof(MyMetricsBehavior<,>)),
typeof(Program).Assembly);
Заметки по дизайну
IDomainEvent— внутренний; можно менять свободно; никогда не пересекает границу сервиса.IIntegrationEvent— внешний контракт; версионируется; публикуется в Kafka через Wolverine.IIntegrationCommand— fire-and-forget команда другому сервису;IIntegrationCommand<TResult>ожидает ответ.