DEV Community

Anton Grebenkin
Anton Grebenkin

Posted on

FlashEvents: A Free, Simple Alternative to MediatR Notification Publisher

The Situation

Let's be honest, many of us in the .NET world have relied on MediatR. It's a fantastic library that has shaped how we think about CQRS and in-process messaging. However, with the recent introduction of a licensing model, many developers are looking for free, open-source alternatives for its notification (publish/subscribe) capabilities.

I was in the same boat. I love the pub/sub pattern for decoupling components, but I needed a solution that was not only free but also incredibly fast and architecturally sound, especially for applications using services with specific lifetimes, like Entity Framework's DbContext.

That's why I created FlashEvents.

FlashEvents is a high-performance, in-memory event publishing library for .NET designed with two core principles in mind: simplicity and speed.

What Makes FlashEvents Different?

FlashEvents focuses exclusively on the "notification" or "event publishing" part of the pattern. Its key features are designed to solve common problems gracefully:

  • 🚀 Blazing Fast Performance: It's optimized for low-latency and minimal memory allocations. (We'll get to the benchmarks later!)
  • Parallel Execution by Default: When you publish an event, all its handlers run concurrently using Task.WhenAll. This maximizes throughput without any special configuration.
  • 🛡️ Scoped Handler Isolation: This is the killer feature. Each event handler is resolved and executed in its own IServiceScope. This completely prevents issues with shared state. If one handler uses a DbContext, it gets its own instance. Another handler can use its own DbContext or other scoped services without any conflicts. This is a huge win for reliability.
  • 🔧 Simple & Fluent API: Setting it up is a breeze with clean dependency injection extensions.
  • 🔍 Automatic Handler Discovery: A single line of code can register all your event handlers from an assembly.

Let's See It in Action

Here’s how you can integrate FlashEvents into your project in just a few minutes.

1. Define Your Event

using FlashEvents.Abstractions;

public record OrderCreatedEvent(int OrderId, string CustomerEmail) : IEvent;
Enter fullscreen mode Exit fullscreen mode

2. Create Event Handlers

using FlashEvents.Abstractions;

// Handler 1: Sends a welcome email
public class SendWelcomeEmailHandler : IEventHandler<OrderCreatedEvent>
{
    private readonly IEmailService _emailService; // Can be transient or singleton

    public SendWelcomeEmailHandler(IEmailService emailService) { /* ... */ }

    public async Task Handle(OrderCreatedEvent @event, CancellationToken ct)
    {
        // ... send email ...
    }
}

// Handler 2: Updates analytics using a scoped service
public class UpdateAnalyticsHandler : IEventHandler<OrderCreatedEvent>
{
    private readonly IAnalyticsService _analyticsService; // Scoped service

    public UpdateAnalyticsHandler(IAnalyticsService analyticsService) { /* ... */ }

    public async Task Handle(OrderCreatedEvent @event, CancellationToken ct)
    {
        // ... update analytics ...
    }
}
Enter fullscreen mode Exit fullscreen mode

3. Configure Dependency Injection

using FlashEvents;
using System.Reflection;

var builder = WebApplication.CreateBuilder(args);

// 1. Add FlashEvents services
builder.Services.AddEventPublisher();

// 2. Automatically discover and register all handlers
builder.Services.AddEventHandlersFromAssembly(Assembly.GetExecutingAssembly());

// 3. Register your other application services
builder.Services.AddTransient<IEmailService, EmailService>();
builder.Services.AddScoped<IanalyticsService, AnalyticsService>(); // Our scoped service

var app = builder.Build();

// ...
Enter fullscreen mode Exit fullscreen mode

4. Publish an Event

using FlashEvents.Abstractions;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("[controller]")]
public class OrdersController : ControllerBase
{
    private readonly IEventPublisher _eventPublisher;

    public OrdersController(IEventPublisher eventPublisher)
    {
        _eventPublisher = eventPublisher;
    }

    [HttpPost]
    public async Task<IActionResult> CreateOrder()
    {
        // ... logic to create an order ...
        var orderEvent = new OrderCreatedEvent(123, "test@example.com");

        // Publish it. FlashEvents handles the rest.
        await _eventPublisher.PublishAsync(orderEvent);

        return Ok("Order created and events are being handled.");
    }
}
Enter fullscreen mode Exit fullscreen mode

But Is It Fast? The Benchmarks

Talk is cheap. Let's look at the numbers against MediatR v12.5.0.

System: i5-13400F, .NET 8


Scenario 1: Single Handler (vs. MediatR's Parallel Publisher)

Method Mean Error StdDev Ratio Allocated Alloc Ratio
FlashEvents_Publish 205.1 ns 1.79 ns 1.68 ns 1.00 520 B 1.00
MediatR_Publish 141.19 ns 1.114 ns 0.988 ns 1.55 664 B 3.77

Scenario 2: Two Handlers (vs. MediatR's Parallel Publisher)

This test highlights the parallel execution for two handlers.

Method Mean Error StdDev Ratio Allocated Alloc Ratio
FlashEvents_Publish 269.3 ns 3.34 ns 2.79 ns 1.00 712 B 1.00
MediatR_Publish 171.4 ns 0.95 ns 0.79 ns 0.64 824 B 1.16

Why Choose FlashEvents?

If you need the request/response or pipeline behaviors from MediatR, then MediatR is still the tool for the job.

But if you are primarily using its notification publisher feature, FlashEvents offers a compelling alternative:

  1. Free & Open Source: It's licensed under MIT. No fees, no strings attached.
  2. Architectural Safety: Built-in scoped execution for handlers is a huge advantage that prevents a common class of bugs related to dependency lifetimes.
  3. Simplicity: It does one thing and does it well. The API surface is minimal and easy to learn.
  4. Performance: It's extremely lightweight and holds its own against the industry standard, especially regarding memory allocations.

Give It a Try!

I built FlashEvents to solve a problem I was facing, and I hope it can help you too. It's available on NuGet, and the source code is on GitHub.

Top comments (0)