LiteBus
LiteBus is an easy-to-use and ambitious in-process mediator providing the foundation to implement Command Query Separation (CQS). It is implemented with minimal reflection and instead utilizes covariance and contravariance to provide its core functionality.
Install / Use
/learn @litenova/LiteBusREADME
It is, and always will be, governed by the MIT license. LiteBus helps you implement Command Query Separation (CQS) and Domain-Driven Design (DDD) patterns by providing a clean, decoupled architecture for your application's business logic.
Why LiteBus?
- Truly Semantic: Go beyond generic requests. With first-class contracts like
ICommand<TResult>,IQuery<TResult>, andIEvent, your code becomes self-documenting. You can even publish clean POCOs as domain events. - High Performance: Designed for minimal overhead. Handler metadata is cached on startup, and dependencies are resolved lazily. Large datasets are handled efficiently with
IAsyncEnumerable<T>streaming. - Granular Pipeline Control: Go beyond simple "behaviors". LiteBus provides a full pipeline with distinct, type-safe
Pre-Handlers,Post-Handlers, andError-Handlersfor each message. - Open Generic Handlers: Write a single pre/post/error handler once and have it automatically apply to every message type matching its constraints — perfect for cross-cutting concerns like logging, validation, and metrics.
- Advanced Event Concurrency: Take full control of event processing. Configure
SequentialorParallelexecution for both priority groups and for handlers within the same group to fine-tune throughput. - Resilient & Durable: Guarantee at-least-once execution for critical commands with a built-in durable Command Inbox.
- DI-Agnostic by Design: Decoupled from any specific DI container. First-class integration for Microsoft DI and Autofac is provided, with a simple adapter pattern to support others.
Quick Start
1. Install Packages
Install the modules you need. The core messaging infrastructure is included automatically.
# For Commands
dotnet add package LiteBus.Commands.Extensions.Microsoft.DependencyInjection
# For Queries
dotnet add package LiteBus.Queries.Extensions.Microsoft.DependencyInjection
# For Events
dotnet add package LiteBus.Events.Extensions.Microsoft.DependencyInjection
2. Define Your Messages and Handlers
Command: Create a Product
// The Command
public sealed record CreateProductCommand(string Name, decimal Price) : ICommand<Guid>;
// The Handler
public sealed class CreateProductCommandHandler : ICommandHandler<CreateProductCommand, Guid>
{
public Task<Guid> HandleAsync(CreateProductCommand command, CancellationToken cancellationToken)
{
var productId = Guid.NewGuid(); // Your business logic here...
Console.WriteLine($"Product '{command.Name}' created with ID: {productId}");
return Task.FromResult(productId);
}
}
Query: Get a Product by ID
// The Query
public sealed record GetProductByIdQuery(Guid Id) : IQuery<ProductDto>;
// The DTO
public sealed record ProductDto(Guid Id, string Name, decimal Price);
// The Handler
public sealed class GetProductByIdQueryHandler : IQueryHandler<GetProductByIdQuery, ProductDto>
{
public Task<ProductDto> HandleAsync(GetProductByIdQuery query, CancellationToken cancellationToken)
{
// Your data retrieval logic here...
var product = new ProductDto(query.Id, "Sample Product", 99.99m);
return Task.FromResult(product);
}
}
Event: A Product was Created
// The Event (can be a simple POCO)
public sealed record ProductCreatedEvent(Guid ProductId, string Name);
// The Handler
public sealed class ProductCreatedEventHandler : IEventHandler<ProductCreatedEvent>
{
public Task HandleAsync(ProductCreatedEvent @event, CancellationToken cancellationToken)
{
// Your side-effect logic here (e.g., send an email, update a projection)
Console.WriteLine($"Handling side effects for new product '{@event.Name}'...");
return Task.CompletedTask;
}
}
3. Configure and Mediate
Register LiteBus and its modules in Program.cs, then inject the mediators into your services or controllers.
// In Program.cs
builder.Services.AddLiteBus(liteBus =>
{
var appAssembly = typeof(Program).Assembly;
// Scan the assembly for all command/query/event handlers
liteBus.AddCommandModule(module => module.RegisterFromAssembly(appAssembly));
liteBus.AddQueryModule(module => module.RegisterFromAssembly(appAssembly));
liteBus.AddEventModule(module => module.RegisterFromAssembly(appAssembly));
});
// In your API Controller or Service
public class ProductController : ControllerBase
{
private readonly ICommandMediator _commandMediator;
private readonly IQueryMediator _queryMediator;
private readonly IEventMediator _eventMediator;
public ProductController(ICommandMediator cmd, IQueryMediator qry, IEventMediator evt)
{
_commandMediator = cmd;
_queryMediator = qry;
_eventMediator = evt;
}
[HttpPost]
public async Task<IActionResult> Create(CreateProductCommand command)
{
// 1. Send a command to create the product
var productId = await _commandMediator.SendAsync(command);
// 2. Publish an event to handle side effects
await _eventMediator.PublishAsync(new ProductCreatedEvent(productId, command.Name));
// 3. Query for the newly created product to return it
var productDto = await _queryMediator.QueryAsync(new GetProductByIdQuery(productId));
return Ok(productDto);
}
}
Key Features
A Semantic & Granular Pipeline
LiteBus provides a rich set of interfaces that make your pipeline explicit and powerful. Each message type (Command, Query, Event) has its own set of Pre-Handlers, Post-Handlers, and Error-Handlers.
This allows for fine-grained control, such as running validation logic, enriching a message, or logging results at specific stages of the pipeline. You can also share data between handlers via the AmbientExecutionContext.
// A semantic validator that runs before the main handler
public sealed class PlaceOrderValidator : ICommandValidator<PlaceOrderCommand> // or ICommandPreHandler<PlaceOrderCommand>
{
public Task ValidateAsync(PlaceOrderCommand command, CancellationToken cancellationToken)
{
if (command.LineItems.Count == 0)
{
throw new ValidationException("At least one line item is required.");
}
return Task.CompletedTask;
}
}
// A post-handler that runs after the command is successfully handled
public sealed class PlaceOrderNotifier : ICommandPostHandler<PlaceOrderCommand, Guid>
{
public Task PostHandleAsync(PlaceOrderCommand command, Guid orderId, CancellationToken cancellationToken)
{
// Publish an OrderPlacedEvent with the result from the command handler
return _eventPublisher.PublishAsync(new OrderPlacedEvent(orderId));
}
}
The mediator also supports polymorphic dispatch, allowing handlers for a base message type to process any derived messages.
Open Generic Handlers for Cross-Cutting Concerns
Write a single handler that automatically applies to every command, query, or event. No changes to existing messages required.
// This pre-handler runs before EVERY command — registered once, applied everywhere
public sealed class CommandLogger<T> : ICommandPreHandler<T> where T : ICommand
{
public Task PreHandleAsync(T message, CancellationToken cancellationToken)
{
Console.WriteLine($"Executing: {typeof(T).Name}");
return Task.CompletedTask;
}
}
// RegisterFromAssembly automatically discovers open generic handlers in the assembly
builder.Services.AddLiteBus(liteBus =>
{
liteBus.AddCommandModule(module =>
{
module.RegisterFromAssembly(typeof(Program).Assembly); // picks up CommandLogger<> too
});
});
// Or register explicitly if the handler is in a different assembly
builder.Services.AddLiteBus(liteBus =>
{
liteBus.AddCommandModule(module =>
{
module.Register(typeof(CommandLogger<>)); // from an external library
module.RegisterFromAssembly(typeof(Program).Assembly);
});
});
RegisterFromAssembly automatically discovers open generic handlers in the scanned assembly — no separate Register(typeof(...)) call is needed. Use explicit registration only when the handler lives in a different assembly. LiteBus closes the generic at startup for each concrete message type. Generic constraints (where T : ICommand, class, struct, new()) are fully respected. Registration order does not matter.
Advanced Eventing with Concurrency Control
Define execution priority and concurrency for event handlers to manage complex workflows.
// This handler runs first
[HandlerPriority(1)]
public class ValidateOrderHandler : IEventHandler<Ord
Related Skills
node-connect
341.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
84.5kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
341.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
commit-push-pr
84.5kCommit, push, and open a PR
