Docs

Koto.Application

CQRS dispatcher, pipeline behaviors, and cross-service integration abstractions.

Install

dotnet add package Koto.Application

What's included

TypePurpose
ICommand / ICommand<TResult>Marker interfaces for write operations (ICommandBase)
IQuery<TResult>Marker interface for read operations (IQueryBase)
ICommandHandler<T> / ICommandHandler<T, TResult>Command handler contracts
IQueryHandler<TQuery, TResult>Query handler contract
ICqrsDispatcherDispatches commands and queries to registered handlers
IPipelineBehavior<TRequest, TResponse>Middleware for the CQRS pipeline
KotoApplicationOptionsOpt-in behavior registration (AddLoggingBehavior(), AddTransactionBehavior(), AddBehavior(type))
LoggingBehavior<,>Logs every command/query with timing
TransactionBehavior<,>Wraps each command in a IUnitOfWork transaction
IUnitOfWorkAbstraction for committing a database transaction
IRepository<TAgg, TId>Persistence contract for aggregates (pairs with IUnitOfWork)
IIntegrationEvent / IntegrationEventExternal event contract
IIntegrationCommand / IIntegrationCommand<TResult>Cross-service command contract
IIntegrationEventPublisherPublishes integration events (implemented in infra)
IIntegrationCommandDispatcherDispatches integration commands (implemented in infra)
AssemblyScanningGetLoadableTypes — scan guard against ReflectionTypeLoadException

Usage

Register

// Handlers only — no pipeline behaviors:
builder.Services.AddKotoApplication(typeof(Program).Assembly);

// With behaviors (opt-in; registration order = execution order, first is outermost):
builder.Services.AddKotoApplication(
    o => o.AddLoggingBehavior().AddTransactionBehavior(),
    typeof(Program).Assembly);
builder.Services.AddKotoValidation(typeof(Program).Assembly); // ValidationBehavior from Koto.Validation
// Recommended order: Logging → Validation → Transaction.

Implement a command

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)
    {
        // business logic
    }
}

Dispatch

var result = await _dispatcher.SendAsync(new PlaceOrderCommand(customerId, items), ct);

Pipeline behaviors

Behaviors are resolved against the concrete command/query type: an open-generic registration like ValidationBehavior<,> closes over PlaceOrderCommand at dispatch time, so IValidator<PlaceOrderCommand> is discovered. A behavior can also target a single request by implementing IPipelineBehavior<PlaceOrderCommand, Result<OrderId>>.

// Custom open-generic behavior:
builder.Services.AddKotoApplication(
    o => o.AddLoggingBehavior().AddBehavior(typeof(MyMetricsBehavior<,>)),
    typeof(Program).Assembly);

Design notes

  • IDomainEvent — internal; free to change; never crosses service boundaries.
  • IIntegrationEvent — external contract; versioned; published to Kafka via Wolverine.
  • IIntegrationCommand — fire-and-forget command to another service; IIntegrationCommand<TResult> expects a reply.