-
Notifications
You must be signed in to change notification settings - Fork 12
Getting Started
This guide will walk you through installing LiteBus and implementing your first command, from definition to execution.
LiteBus is DI-agnostic and provides extension packages for specific DI containers. Choose the set of packages that matches your application's container.
Install the extension packages for the modules you need. These packages include all necessary dependencies.
# 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.DependencyInjectionInstall the Autofac-specific extension packages.
# For Commands
dotnet add package LiteBus.Commands.Extensions.Autofac
# For Queries
dotnet add package LiteBus.Queries.Extensions.Autofac
# For Events
dotnet add package LiteBus.Events.Extensions.AutofacRegister LiteBus and its modules in your application's startup file (e.g., Program.cs).
The AddCommandModule, AddQueryModule, and AddEventModule extensions automatically register the core MessageModule if it hasn't been added yet, simplifying configuration.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddLiteBus(liteBus =>
{
// Register the Command Module and scan the current assembly for handlers
liteBus.AddCommandModule(module =>
{
module.RegisterFromAssembly(typeof(Program).Assembly);
});
// Register the Query Module
liteBus.AddQueryModule(module =>
{
module.RegisterFromAssembly(typeof(Program).Assembly);
});
// Register the Event Module
liteBus.AddEventModule(module =>
{
module.RegisterFromAssembly(typeof(Program).Assembly);
});
});
// ... rest of your configurationvar builder = new ContainerBuilder();
builder.AddLiteBus(liteBus =>
{
liteBus.AddCommandModule(module =>
{
module.RegisterFromAssembly(typeof(Program).Assembly);
});
});
var container = builder.Build();Let's implement a complete pipeline for creating a product.
The command carries the data, and it specifies what it will return.
// The data transfer object for the result
public sealed record ProductDto(Guid Id, string Name, decimal Price);
// The command to create a product, which returns a ProductDto
public sealed record CreateProductCommand(string Name, decimal Price) : ICommand<ProductDto>;This class contains the core business logic.
public sealed class CreateProductCommandHandler : ICommandHandler<CreateProductCommand, ProductDto>
{
private readonly IProductRepository _repository;
public CreateProductCommandHandler(IProductRepository repository)
{
_repository = repository;
}
public async Task<ProductDto> HandleAsync(CreateProductCommand command, CancellationToken cancellationToken = default)
{
var product = new Product(command.Name, command.Price);
await _repository.AddAsync(product, cancellationToken);
return new ProductDto(product.Id, product.Name, product.Price);
}
}This pre-handler runs before the main handler to validate the input.
public sealed class CreateProductValidator : ICommandValidator<CreateProductCommand>
{
public Task ValidateAsync(CreateProductCommand command, CancellationToken cancellationToken = default)
{
if (string.IsNullOrWhiteSpace(command.Name))
{
throw new ValidationException("Product name cannot be empty.");
}
if (command.Price <= 0)
{
throw new ValidationException("Price must be positive.");
}
return Task.CompletedTask;
}
}This post-handler runs after the main handler to perform side effects, like publishing an event.
public sealed class ProductCreationNotifier : ICommandPostHandler<CreateProductCommand, ProductDto>
{
private readonly IEventPublisher _eventPublisher;
public ProductCreationNotifier(IEventPublisher eventPublisher)
{
_eventPublisher = eventPublisher;
}
public Task PostHandleAsync(CreateProductCommand command, ProductDto? result, CancellationToken cancellationToken = default)
{
if (result is not null)
{
// Publish an event to notify other parts of the system
return _eventPublisher.PublishAsync(new ProductCreatedEvent(result.Id), cancellationToken);
}
return Task.CompletedTask;
}
}Inject ICommandMediator into your controller or service and send the command.
[ApiController]
[Route("products")]
public class ProductsController : ControllerBase
{
private readonly ICommandMediator _commandMediator;
public ProductsController(ICommandMediator commandMediator)
{
_commandMediator = commandMediator;
}
[HttpPost]
public async Task<ActionResult<ProductDto>> Create(CreateProductCommand command)
{
var productDto = await _commandMediator.SendAsync(command);
return CreatedAtAction(nameof(GetById), new { id = productDto.Id }, productDto);
}
}When you send the CreateProductCommand, LiteBus will automatically execute the validator, then the handler, and finally the notifier in a single, transactional pipeline.