Indiko.Blocks.EventBus.InMemory 2.7.8

dotnet add package Indiko.Blocks.EventBus.InMemory --version 2.7.8
                    
NuGet\Install-Package Indiko.Blocks.EventBus.InMemory -Version 2.7.8
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Indiko.Blocks.EventBus.InMemory" Version="2.7.8" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Indiko.Blocks.EventBus.InMemory" Version="2.7.8" />
                    
Directory.Packages.props
<PackageReference Include="Indiko.Blocks.EventBus.InMemory" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Indiko.Blocks.EventBus.InMemory --version 2.7.8
                    
#r "nuget: Indiko.Blocks.EventBus.InMemory, 2.7.8"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Indiko.Blocks.EventBus.InMemory@2.7.8
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Indiko.Blocks.EventBus.InMemory&version=2.7.8
                    
Install as a Cake Addin
#tool nuget:?package=Indiko.Blocks.EventBus.InMemory&version=2.7.8
                    
Install as a Cake Tool

Indiko.Blocks.EventBus.InMemory

In-memory event bus implementation for local, in-process event-driven communication.

Overview

This package provides a lightweight, in-memory implementation of the event bus abstractions, perfect for single-instance applications, testing, and development scenarios.

Features

  • Zero Dependencies: No external message broker required
  • In-Process Communication: Events published and consumed within the same process
  • Concurrent Handling: Parallel execution of multiple event handlers
  • Thread-Safe: Concurrent dictionary-based handler storage
  • Fast: Minimal overhead for event delivery
  • Testing-Friendly: Ideal for unit and integration tests
  • Development: Perfect for local development without infrastructure

Installation

dotnet add package Indiko.Blocks.EventBus.InMemory

Quick Start

Configure Services

using Indiko.Blocks.EventBus.InMemory;

public class Startup : WebStartup
{
    public override void ConfigureServices(IServiceCollection services)
    {
        base.ConfigureServices(services);
        
        // Register in-memory event bus
        services.AddInMemoryEventBus();
        
        // Register event handlers
        services.AddScoped<IEventHandler<UserCreatedEvent>, SendWelcomeEmailHandler>();
        services.AddScoped<IEventHandler<UserCreatedEvent>, CreateUserProfileHandler>();
    }
}

Define Events

public class UserCreatedEvent : IEvent
{
    public Guid UserId { get; set; }
    public string Email { get; set; }
    public DateTime CreatedAt { get; set; }
}

public class OrderPlacedEvent : IEvent
{
    public Guid OrderId { get; set; }
    public Guid UserId { get; set; }
    public decimal TotalAmount { get; set; }
    public DateTime OrderDate { get; set; }
}

Implement Handlers

public class SendWelcomeEmailHandler : IEventHandler<UserCreatedEvent>
{
    private readonly IEmailService _emailService;
    private readonly ILogger<SendWelcomeEmailHandler> _logger;

    public SendWelcomeEmailHandler(IEmailService emailService, ILogger<SendWelcomeEmailHandler> logger)
    {
        _emailService = emailService;
        _logger = logger;
    }

    public async Task HandleAsync(UserCreatedEvent @event, CancellationToken cancellationToken = default)
    {
        _logger.LogInformation($"Sending welcome email to {event.Email}");
        await _emailService.SendWelcomeEmailAsync(@event.Email, cancellationToken);
    }
}

public class CreateUserProfileHandler : IEventHandler<UserCreatedEvent>
{
    private readonly IProfileService _profileService;

    public async Task HandleAsync(UserCreatedEvent @event, CancellationToken cancellationToken = default)
    {
        await _profileService.CreateDefaultProfileAsync(@event.UserId, cancellationToken);
    }
}

Publish Events

public class UserService
{
    private readonly IEventBus _eventBus;
    private readonly IUserRepository _userRepository;

    public UserService(IEventBus eventBus, IUserRepository userRepository)
    {
        _eventBus = eventBus;
        _userRepository = userRepository;
    }

    public async Task<User> CreateUserAsync(string email, string password)
    {
        var user = new User
        {
            Id = Guid.NewGuid(),
            Email = email,
            CreatedAt = DateTime.UtcNow
        };
        
        await _userRepository.AddAsync(user);
        
        // Publish event - handlers execute concurrently
        await _eventBus.PublishAsync(new UserCreatedEvent
        {
            UserId = user.Id,
            Email = user.Email,
            CreatedAt = user.CreatedAt
        });
        
        return user;
    }
}

How It Works

Concurrent Handler Execution

The in-memory event bus executes all registered handlers for an event type concurrently:

public async ValueTask PublishAsync<TEvent>(TEvent @event, CancellationToken cancellationToken = default)
{
    if (_eventHandlers.TryGetValue(typeof(TEvent), out var handlers))
    {
        // Execute all handlers concurrently
        var tasks = handlers.Cast<IEventHandler<TEvent>>()
                            .Select(handler => handler.HandleAsync(@event, cancellationToken));

        await Task.WhenAll(tasks); // Wait for all handlers to complete
    }
}

Thread-Safe Handler Registration

Handler registration is protected with locks to ensure thread safety:

public void RegisterEventHandler<TEvent>(IEventHandler<TEvent> eventHandler)
{
    lock (_lock)
    {
        _eventHandlers.AddOrUpdate(
            typeof(TEvent),
            [eventHandler],
            (key, existingHandlers) =>
            {
                existingHandlers.Add(eventHandler);
                return existingHandlers;
            }
        );
    }
}

Dynamic Handler Registration

Register at Runtime

public class EventHandlerRegistry
{
    private readonly IEventBus _eventBus;
    private readonly IServiceProvider _serviceProvider;

    public void RegisterHandlers()
    {
        // Get handler from DI container
        var emailHandler = _serviceProvider.GetRequiredService<IEventHandler<UserCreatedEvent>>();
        
        // Register dynamically
        _eventBus.RegisterEventHandler(emailHandler);
    }

    public void RegisterMultipleHandlers()
    {
        var handler1 = _serviceProvider.GetRequiredService<SendWelcomeEmailHandler>();
        var handler2 = _serviceProvider.GetRequiredService<CreateUserProfileHandler>();
        
        // Register multiple handlers at once
        _eventBus.RegisterEventHandlers<UserCreatedEvent>(handler1, handler2);
    }
}

Unregister Handlers

// Unregister all handlers for specific event type
_eventBus.UnRegisterEventHandler<UserCreatedEvent>();

// Unregister all handlers for all events
_eventBus.UnRegisterAllEventHandlers();

Testing

Unit Testing

[Fact]
public async Task UserCreation_Should_TriggerWelcomeEmail()
{
    // Arrange
    var emailServiceMock = new Mock<IEmailService>();
    var eventBus = new InMemoryEventBus();
    var handler = new SendWelcomeEmailHandler(emailServiceMock.Object, Mock.Of<ILogger>());
    
    eventBus.RegisterEventHandler(handler);
    
    var @event = new UserCreatedEvent
    {
        UserId = Guid.NewGuid(),
        Email = "test@example.com"
    };
    
    // Act
    await eventBus.PublishAsync(@event);
    
    // Assert
    emailServiceMock.Verify(
        x => x.SendWelcomeEmailAsync("test@example.com", It.IsAny<CancellationToken>()), 
        Times.Once
    );
}

Integration Testing

public class EventBusIntegrationTests : IClassFixture<WebApplicationFactory<Startup>>
{
    private readonly WebApplicationFactory<Startup> _factory;

    public EventBusIntegrationTests(WebApplicationFactory<Startup> factory)
    {
        _factory = factory;
    }

    [Fact]
    public async Task PublishEvent_Should_ExecuteAllHandlers()
    {
        // Arrange
        var serviceProvider = _factory.Services;
        var eventBus = serviceProvider.GetRequiredService<IEventBus>();
        
        var @event = new UserCreatedEvent
        {
            UserId = Guid.NewGuid(),
            Email = "test@example.com",
            CreatedAt = DateTime.UtcNow
        };
        
        // Act
        await eventBus.PublishAsync(@event);
        
        // Assert
        // Verify side effects of handlers
        // (e.g., database records created, emails sent, etc.)
    }
}

Performance Characteristics

Advantages

  • Low Latency: No network overhead
  • High Throughput: Direct in-memory method calls
  • No Serialization: No message serialization/deserialization
  • Simple Debugging: Easy to step through handler execution

Limitations

  • Single Process: Events don't cross process boundaries
  • No Persistence: Events are lost if process crashes
  • No Retry: Failed handlers require manual retry logic
  • Memory Bound: All handlers execute in the same memory space

Use Cases

1. Development Environment

public void ConfigureServices(IServiceCollection services)
{
    if (Environment.IsDevelopment())
    {
        // Use in-memory for local development
        services.AddInMemoryEventBus();
    }
    else
    {
        // Use RabbitMQ for production
        services.AddRabbitMQEventBus(Configuration);
    }
}

2. Unit Testing

Perfect for testing event-driven logic without external dependencies.

3. Monolithic Applications

For applications that don't require distributed event processing.

4. Domain Events

Internal domain events within a bounded context.

Error Handling

Handler Exceptions

public class RobustHandler : IEventHandler<UserCreatedEvent>
{
    public async Task HandleAsync(UserCreatedEvent @event, CancellationToken cancellationToken)
    {
        try
        {
            // Handler logic
            await DoWorkAsync(@event);
        }
        catch (Exception ex)
        {
            // Log error
            _logger.LogError(ex, $"Handler failed for user {event.UserId}");
            
            // Decide whether to throw or swallow
            // Throwing will cause PublishAsync to fail
            throw;
        }
    }
}

Publisher Error Handling

public async Task CreateUserWithErrorHandlingAsync(string email)
{
    var user = await CreateUserAsync(email);
    
    try
    {
        await _eventBus.PublishAsync(new UserCreatedEvent { ... });
    }
    catch (Exception ex)
    {
        // One or more handlers failed
        _logger.LogError(ex, "Event publishing failed");
        
        // Decide on compensation logic
        // (e.g., mark user for manual review, retry later, etc.)
    }
}

Comparison with Other Implementations

Feature InMemory RabbitMQ
Setup Complexity Minimal Requires broker
Latency < 1ms 1-10ms
Persistence No Yes
Distributed No Yes
Scalability Single process Multiple processes
Reliability Process-bound Durable messages
Best For Dev/Test/Monoliths Production/Microservices

Migration Path

From InMemory to RabbitMQ

Your code remains the same when switching implementations:

// Development
services.AddInMemoryEventBus();

// Production
services.AddRabbitMQEventBus(options =>
{
    options.ConnectionString = Configuration.GetConnectionString("RabbitMQ");
});

All your events and handlers work without modification!

Target Framework

  • .NET 10

Dependencies

  • Indiko.Blocks.EventBus.Abstractions

License

See LICENSE file in the repository root.

  • Indiko.Blocks.EventBus.Abstractions - Core event bus abstractions
  • Indiko.Blocks.EventBus.RabbitMQ - Distributed event bus with RabbitMQ
  • Indiko.Blocks.Mediation.Abstractions - CQRS and mediator pattern
Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.7.8 0 5/7/2026
2.7.7 0 5/7/2026
2.7.6 99 4/23/2026
2.7.5 85 4/23/2026
2.7.4 84 4/23/2026
2.7.3 89 4/23/2026
2.7.2 94 4/23/2026
2.7.1 85 4/23/2026
2.7.0 90 4/23/2026
2.6.4 94 4/21/2026
2.6.3 87 4/21/2026
2.6.2 86 4/21/2026
2.6.1 86 4/18/2026
2.6.0 90 4/17/2026
2.5.1 87 4/14/2026
2.5.0 115 3/30/2026
2.2.18 97 3/8/2026
2.2.17 92 3/8/2026
2.2.16 94 3/8/2026
2.2.15 94 3/7/2026
Loading failed