Простая, потокобезопасная шина событий для .NET с поддержкой синхронной и асинхронной обработки, отмены подписок, логгирования и CancellationToken. Идеальна для DDD и Clean Architecture.
$ dotnet add package DT.SimpleEventBusSimpleEventBus — это лёгкая, потокобезопасная шина событий для .NET, предназначенная для реализации паттерна Publish-Subscribe в приложениях любой сложности.
Идеально подходит для DDD (Domain-Driven Design) и Clean Architecture, так как не навязывает зависимостей домену и позволяет гибко управлять реакциями на события.
Unsubscribe(Guid)CancellationToken в асинхронных операцияхILogger<T> (опционально)record, class)Установите пакет через .NET CLI:
dotnet add package SimpleEventBus
Или через Package Manager Console:
Install-Package SimpleEventBus
// Domain/Events/OrderCompleted.cs
namespace YourApp.Domain.Events;
// Чистый record — без зависимостей!
public record OrderCompleted(Guid OrderId, DateTime CompletedAt);// Application/Handlers/OrderCompletedHandler.cs
using SimpleEventBus;
using YourApp.Domain.Events;
namespace YourApp.Application.Handlers;
public class OrderCompletedHandler
{
public OrderCompletedHandler(IEventBus eventBus)
{
// 🔥 Подписка прямо в конструкторе!
eventBus.SubscribeAsync<OrderCompleted>(HandleAsync);
}
private async Task HandleAsync(OrderCompleted @event, CancellationToken ct = default)
{
// Реакция на событие: отправка email, обновление кэша и т.д.
await Console.Out.WriteLineAsync($"📨 Заказ {@event.OrderId} завершён!");
}
}// Infrastructure/UnitOfWork.cs
using SimpleEventBus;
using YourApp.Domain;
public class UnitOfWork
{
private readonly IEventBus _eventBus;
// ... другие зависимости
public UnitOfWork(IEventBus eventBus)
{
_eventBus = eventBus;
}
public async Task SaveChangesAsync(CancellationToken ct = default)
{
// 1. Сохраняем изменения в БД
// await _dbContext.SaveChangesAsync(ct);
// 2. Публикуем все накопленные domain events
foreach (var domainEvent in GetPendingDomainEvents())
{
await _eventBus.PublishAsync(domainEvent, ct);
}
}
}var builder = WebApplication.CreateBuilder(args);
// Регистрация EventBus как Singleton
builder.Services.AddEventBus(options =>
{
options.EnableLogging = true; // по умолчанию true
});
// Регистрация обработчиков (как Singleton!)
builder.Services.AddSingleton<OrderCompletedHandler>();
// ... остальные сервисы
var app = builder.Build();
app.Run();Работа с Scoped-зависимостями в обработчике Если обработчик должен использовать Scoped-сервисы (например, DbContext), используйте IServiceScopeFactory:
public class OrderCompletedHandler
{
public OrderCompletedHandler(IEventBus eventBus, IServiceScopeFactory scopeFactory)
{
eventBus.SubscribeAsync<OrderCompleted>(async (@event, ct) =>
{
using var scope = scopeFactory.CreateScope();
var emailService = scope.ServiceProvider.GetRequiredService<IEmailService>();
await emailService.SendAsync(@event.OrderId, ct);
});
}
}Регистрация:
builder.Services.AddSingleton<OrderCompletedHandler>();
builder.Services.AddScoped<IEmailService, SmtpEmailService>();Отключение логгирования
builder.Services.AddEventBus(options => options.EnableLogging = false);Отмена подписки (редко нужно)
var subscriptionId = eventBus.Subscribe<MyEvent>(Handler);
// ...
eventBus.Unsubscribe(subscriptionId);