LiteEventBus 0.4.2
dotnet add package LiteEventBus --version 0.4.2
NuGet\Install-Package LiteEventBus -Version 0.4.2
<PackageReference Include="LiteEventBus" Version="0.4.2" />
<PackageVersion Include="LiteEventBus" Version="0.4.2" />
<PackageReference Include="LiteEventBus" />
paket add LiteEventBus --version 0.4.2
#r "nuget: LiteEventBus, 0.4.2"
#:package LiteEventBus@0.4.2
#addin nuget:?package=LiteEventBus&version=0.4.2
#tool nuget:?package=LiteEventBus&version=0.4.2
LiteEventBus
ℹ️ Disclosure: This application was developed using AI (vibecoding).
Uma biblioteca .NET leve para comunicação Publish/Subscribe em memória, com foco em simplicidade, baixo overhead e integração nativa com Microsoft.Extensions.DependencyInjection.
Documentation:
For detailed guides, examples and API documentation, see the Documentation
Objetivo
LiteEventBus permite que publicadores emitam eventos fortemente tipados e que múltiplos assinantes sejam notificados de forma assíncrona. A biblioteca não implementa Mediator, Command Bus, CQRS, Event Sourcing ou mensageria distribuída.
Instalação
dotnet add package LiteEventBus
Primeiros passos
1. Definir um evento
public sealed record UserRegistered(
Guid UserId,
string Email,
string FullName);
2. Criar subscribers
using LiteEventBus.Abstractions;
public sealed class SendWelcomeEmail : IEventSubscriber<UserRegistered>
{
public Task HandleAsync(UserRegistered @event, CancellationToken cancellationToken)
{
Console.WriteLine($"Welcome email sent to {@event.Email}");
return Task.CompletedTask;
}
}
public sealed class LogAuditEntry : IEventSubscriber<UserRegistered>
{
public Task HandleAsync(UserRegistered @event, CancellationToken cancellationToken)
{
Console.WriteLine($"Audit: user {@event.UserId} registered");
return Task.CompletedTask;
}
}
3. Registrar na DI
using Microsoft.Extensions.DependencyInjection;
var services = new ServiceCollection();
services.AddLiteEventBus();
services.AddSubscriber<UserRegistered, SendWelcomeEmail>();
services.AddSubscriber<UserRegistered, LogAuditEntry>();
var provider = services.BuildServiceProvider();
4. Publicar um evento
var eventBus = provider.GetRequiredService<IEventBus>();
await eventBus.PublishAsync(
new UserRegistered(
Guid.NewGuid(),
"john.doe@example.com",
"John Doe"));
Tratamento de erros
Comportamento padrão (fail-fast)
Por padrão, a publicação é interrompida na primeira exceção. Subscribers registrados após o que falhou não executam:
await eventBus.PublishAsync(new UserRegistered(Guid.NewGuid(), "a@b.com", "A"));
// Se o 2º subscriber lançar exceção, o 3º não executa
ContinueOnError (por chamada)
Para executar todos os subscribers mesmo em caso de erro, use PublishOptions:
var options = new PublishOptions { ContinueOnError = true };
try
{
await eventBus.PublishAsync(new UserRegistered(Guid.NewGuid(), "a@b.com", "A"), options);
}
catch (AggregateException ex)
{
// ex.InnerExceptions contém as exceções de todos os subscribers que falharam
foreach (var inner in ex.InnerExceptions)
{
Console.WriteLine($"Subscriber error: {inner.Message}");
}
}
Configuração global
Defina o comportamento padrão para todas as publicações durante o registro na DI:
services.AddLiteEventBus(options =>
{
options.DefaultContinueOnError = true;
});
A configuração global é usada quando PublishAsync é chamado sem PublishOptions. O valor por chamada sempre sobrescreve o global.
Callback de erro
Registre um callback para ser notificado quando um subscriber falha (útil para logging ou métricas):
services.AddLiteEventBus(options =>
{
options.DefaultContinueOnError = true;
options.OnSubscriberError = async (serviceProvider, @event, exception) =>
{
var logger = serviceProvider.GetRequiredService<ILogger<IEventBus>>();
logger.LogError(exception, "Subscriber falhou ao processar {EventType}", @event.GetType().Name);
};
});
O callback recebe o IServiceProvider do escopo atual, permitindo resolver serviços scoped (loggers, etc.).
Subscribers com dependências scoped
Cada chamada de PublishAsync cria um escopo DI, permitindo que subscribers consumam dependências scoped como DbContext do Entity Framework Core:
using LiteEventBus.Abstractions;
public sealed class UserRegisteredHandler : IEventSubscriber<UserRegistered>
{
private readonly AppDbContext _db;
public UserRegisteredHandler(AppDbContext db)
{
_db = db;
}
public async Task HandleAsync(UserRegistered @event, CancellationToken cancellationToken)
{
_db.Users.Add(new User(@event.UserId, @event.Email, @event.FullName));
await _db.SaveChangesAsync(cancellationToken);
}
}
Registro na DI:
services.AddDbContext<AppDbContext>(...);
services.AddLiteEventBus();
services.AddSubscriber<UserRegistered, UserRegisteredHandler>();
API
IEventSubscriber<TEvent>
Contrato para subscribers. Deve ser implementado por cada assinante de evento.
IEventBus
Contrato para publicação de eventos.
Task PublishAsync<TEvent>(TEvent @event, CancellationToken cancellationToken = default);
Task PublishAsync<TEvent>(TEvent @event, PublishOptions options, CancellationToken cancellationToken = default);
PublishOptions
Configura o comportamento de uma publicação específica.
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
ContinueOnError |
bool |
false |
Quando true, todos subscribers executam mesmo em caso de erro. As exceções são coletadas e um AggregateException é lançado ao final. |
EventBusOptions
Configura o comportamento global do LiteEventBus durante o registro na DI.
| Propriedade | Tipo | Padrão | Descrição |
|---|---|---|---|
DefaultContinueOnError |
bool |
false |
Valor global usado quando PublishAsync é chamado sem PublishOptions. |
OnSubscriberError |
Func<IServiceProvider, object, Exception, Task>? |
null |
Callback invocado quando um subscriber falha e ContinueOnError é true. |
IServiceCollection.AddLiteEventBus()
// Registro padrão
services.AddLiteEventBus();
// Com configuração global
services.AddLiteEventBus(options => { ... });
Registra IEventBus como singleton. É idempotente: chamadas múltiplas não criam registros duplicados.
IServiceCollection.AddSubscriber<TEvent, TSubscriber>()
Registra um subscriber como transient. Ignora silenciosamente registros duplicados do mesmo par (TEvent, TSubscriber).
IServiceCollection.AddSubscriber<TEvent>(handler)
Registra um subscriber lambda como transient. Cinco overloads aceitam diferentes assinaturas:
// Sync sem cancellation token
services.AddSubscriber<UserRegistered>(e => Console.WriteLine(e.FullName));
// Async sem cancellation token
services.AddSubscriber<UserRegistered>(e => Task.CompletedTask);
// Async com cancellation token
services.AddSubscriber<UserRegistered>((e, ct) => Task.CompletedTask);
// Async com cancellation token e IServiceProvider escoped
services.AddSubscriber<UserRegistered>((e, ct, sp) =>
{
var svc = sp.GetRequiredService<MeuServico>();
return svc.HandleAsync(e, ct);
});
// Sync com cancellation token e IServiceProvider escoped
services.AddSubscriber<UserRegistered>((Action<UserRegistered, CancellationToken, IServiceProvider>)((e, ct, sp) =>
{
var svc = sp.GetRequiredService<MeuServico>();
svc.Handle(e);
}));
Overloads lambda não deduplicam — cada chamada registra um novo subscriber.
Comportamento
- Subscribers são executados sequencialmente na ordem de registro.
- Por padrão, a primeira exceção interrompe a publicação e é propagada imediatamente.
- Com
ContinueOnError = true, todos subscribers executam. Exceções são coletadas e umAggregateExceptioné lançado ao final. O callbackOnSubscriberErroré invocado para cada falha. - Cada chamada de
PublishAsynccria um escopo DI. Subscribers podem consumir dependências scoped (DbContext,HttpContext, etc.). - Subscribers são resolvidos via DI como transient a cada chamada de
PublishAsync. - O
IEventBusé registrado como singleton. AddSubscriberignora registros duplicados do mesmo tipo de subscriber para o mesmo evento.ConfigureAwait(false)é utilizado em todo código interno da biblioteca.
Limitações
- Apenas comunicação em memória.
- Sem suporte a mensageria distribuída (RabbitMQ, Kafka, Azure Service Bus, etc.).
- Sem suporte a Mediator, CQRS ou Command Bus.
- Sem pipeline behaviors ou middleware.
- Sem retry, dead letter ou persistência.
- Sem reflection scanning ou source generators.
- Apenas API assíncrona.
Design principles
- KISS — a solução mais simples possível.
- YAGNI — nada além do necessário.
- Zero configuração — apenas registrar na DI.
- Thread-safe — sem estado mutável compartilhado.
- Performance — sem reflection, sem
dynamic, semActivatorno caminho crítico.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.