Nera.Lib.Messaging.Abstractions 2.0.0

dotnet add package Nera.Lib.Messaging.Abstractions --version 2.0.0
                    
NuGet\Install-Package Nera.Lib.Messaging.Abstractions -Version 2.0.0
                    
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="Nera.Lib.Messaging.Abstractions" Version="2.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Nera.Lib.Messaging.Abstractions" Version="2.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Nera.Lib.Messaging.Abstractions" />
                    
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 Nera.Lib.Messaging.Abstractions --version 2.0.0
                    
#r "nuget: Nera.Lib.Messaging.Abstractions, 2.0.0"
                    
#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 Nera.Lib.Messaging.Abstractions@2.0.0
                    
#: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=Nera.Lib.Messaging.Abstractions&version=2.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Nera.Lib.Messaging.Abstractions&version=2.0.0
                    
Install as a Cake Tool

Nera.Lib.Messaging.Abstractions# Nera.Lib.Messaging.Abstractions

Version: 1.0.0 Simplified Messaging Library for Nextera Services - Redesigned to be as simple as MediatR πŸš€

Framework: .NET 9.0

Transport: RabbitMQ via MassTransit 8.5.2## Overview

A comprehensive messaging abstraction library for building event-driven microservices with clear separation between in-service domain events and cross-service integration events.A lightweight messaging library that provides two distinct messaging patterns for microservices:

---- In-Service Events - Simple, MediatR-like events within the same service

  • External Events - Cross-service messaging using MassTransit + RabbitMQ

πŸ“‹ Table of Contents

Key Features

---### 1. Install Package

🎯 Overview```xml

<PackageReference Include="Nera.Lib.Messaging.Abstractions" Version="1.0.4" />

What is this library?```

Nera.Lib.Messaging.Abstractions provides a unified abstraction layer for messaging in distributed systems, supporting:### 2. Register Services

  • In-Service Events (Domain Events) - For communication within a single service boundary```csharp

  • Integration Events - For communication across service boundaries via RabbitMQ// Program.cs or Startup.cs

  • Command/Event Bus - Simplified publishing with automatic header enrichmentservices.AddMessagingServices(); // Enables in-service events

  • Consumer Base Classes - Easy message consumption with built-in context reading

// Optional: Add MassTransit for external messaging

Key Featuresservices.AddMassTransitWithRabbitMq(

consumers => consumers.AddConsumer<MyConsumer>(),

Type-Safe Event System - Clear distinction between IInServiceEvent and IntegrationMessageBase (cfg, ctx) β‡’ cfg.ReceiveEndpoint("my-queue", e β‡’ e.ConfigureConsumer<MyConsumer>(ctx))

Automatic Header Enrichment - TenantId, UserId, CorrelationId, SourceService auto-injected );

Auto-Configuration - Zero configuration with environment variables ```

MassTransit Integration - Production-ready RabbitMQ transport

DRY Code - No duplication, single source of truth ### 3. Create Event & Handler

Strongly Typed - Full C# 12 features with records and init-only properties


---// Define an event

public class UserCreated : IInServiceEvent

## 🧠 Core Concepts{

    public string UserId { get; set; }

### Event Types Hierarchy    public string Email { get; set; }

}

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”// Create a handler

β”‚ IInServiceEvent β”‚ ◄── In-Memory, Same Servicepublic class SendWelcomeEmailHandler : IInServiceEventHandler<UserCreated>

β”‚ (Domain Events) β”‚ Uses IInServiceEventBus{

β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ public async Task Handle(UserCreated @event, CancellationToken cancellationToken)

                     β”‚    {

                     β–Ό        // Handle the event

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” await SendWelcomeEmail(@event.Email);

β”‚ IntegrationMessageBase β”‚ ◄── RabbitMQ, Cross-Service }

β”‚ (Integration Events) β”‚ Uses IEventBus}

β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜```


### 4. Publish Events

### 1️⃣ **In-Service Events (Domain Events)**

```csharp

- **Purpose:** Communication within a single service (in-memory)public class UserService

- **Interface:** `IInServiceEvent` (marker interface){

- **Bus:** `IInServiceEventBus`    private readonly IInServiceEventBus _eventBus;

- **Transport:** In-memory (no serialization)

- **Use Case:** Entity state changes, domain logic coordination    public async Task CreateUser(CreateUserRequest request)

    {

**Example:**        var user = await CreateUserLogic(request);

```csharp        

public sealed record OrganizationCreatedDomainEvent(Guid OrganizationId) : IInServiceEvent;        // Publish event - all handlers will be called automatically

```        await _eventBus.Publish(new UserCreated 

        { 

### 2️⃣ **Integration Events**            UserId = user.Id, 

            Email = user.Email 

- **Purpose:** Communication between services (message broker)        });

- **Base Class:** `IntegrationMessageBase`    }

- **Bus:** `IEventBus`}

- **Transport:** RabbitMQ via MassTransit```

- **Use Case:** Cross-service notifications, data synchronization

## Architecture

**Example:**

```csharp```text

public sealed record OrganizationCreatedIntegrationEvent : IInServiceEventβ”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”

{β”‚                    Your Service                                 β”‚

    public required Guid OrganizationId { get; init; }β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€

    public required string Name { get; init; }β”‚  In-Service Events (Fast, Same Process)                        β”‚

    public required string Code { get; init; }β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚

}β”‚  β”‚  Publisher  │───▢│  IInServiceEventBus                 β”‚     β”‚

```β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚  β”œβ”€ Handler 1                       β”‚     β”‚

β”‚                     β”‚  β”œβ”€ Handler 2                       β”‚     β”‚

---β”‚                     β”‚  └─ Handler N                       β”‚     β”‚

β”‚                     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚

## πŸ—οΈ Architectureβ”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€

β”‚  External Events (Reliable, Cross-Service)                     β”‚

### Folder Structureβ”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”‚

β”‚  β”‚  Publisher  │───▢│  MassTransit + RabbitMQ             β”‚     β”‚

```β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β”‚

Nera.Lib.Messaging.Abstractions/β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”œβ”€β”€ πŸ“ Abstractions/              # Core interfaces & base classes                                    β”‚

β”‚   β”œβ”€β”€ IEventBus.cs              # Cross-service event publishing                                    β–Ό

β”‚   β”œβ”€β”€ IInServiceEventBus.cs     # In-memory event publishingβ”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”

β”‚   β”œβ”€β”€ IMessageContextAccessor.cs# Context (TenantId, UserId, etc.)β”‚                Other Services                                   β”‚

β”‚   β”œβ”€β”€ ConsumerBase.cs           # Base for MassTransit consumersβ”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                       β”‚

β”‚   └── InServiceEventHandlerBase.cs # Base for domain event handlersβ”‚  β”‚  MassTransit Consumers              β”‚                       β”‚

β”‚β”‚  β”‚  β”œβ”€ Consumer 1                      β”‚                       β”‚

β”œβ”€β”€ πŸ“ Events/                    # Event type definitionsβ”‚  β”‚  β”œβ”€ Consumer 2                      β”‚                       β”‚

β”‚   β”œβ”€β”€ IInServiceEvent.cs        # Marker for domain eventsβ”‚  β”‚  └─ Consumer N                      β”‚                       β”‚

β”‚   └── IIntegrationMessage.cs    # Base for integration eventsβ”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                       β”‚

β”‚β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”œβ”€β”€ πŸ“ Implementation/            # Concrete implementations```

β”‚   β”œβ”€β”€ MassTransitEventBus.cs    # RabbitMQ event bus

β”‚   β”œβ”€β”€ InServiceEventBus.cs      # In-memory event bus## Comparison

β”‚   └── InServiceEventExtensions.cs # Handler registration

β”‚| Feature | In-Service Events | External Events |

β”œβ”€β”€ πŸ“ Configuration/             # Setup & configuration|---------|------------------|-----------------|

β”‚   β”œβ”€β”€ MessagingOptions.cs       # Service name config| **Use Case** | Same service logic | Cross-service communication |

β”‚   β”œβ”€β”€ MessageHeaders.cs         # Standard header names| **Performance** | Very fast (in-memory) | Network calls |

β”‚   └── ServiceCollectionExtensions.cs # DI registration| **Reliability** | Process dependent | Durable, retries |

β”‚| **Scope** | Single service | Multiple services |

β”œβ”€β”€ πŸ“ docs/                      # Documentation| **Interface** | `IInServiceEvent` | `IIntegrationEvent` |

β”‚   β”œβ”€β”€ EVENT-TYPES-GUIDE.md      # Event types explained| **Bus** | `IInServiceEventBus` | `IEventBus` (MassTransit) |

β”‚   β”œβ”€β”€ USAGE-GUIDE.md            # How to use the library

β”‚   β”œβ”€β”€ OPTIMIZATION-SUMMARY.md   # Performance improvements## Migration from Old Version

β”‚   └── SUMMARY-IMPROVEMENTS.md   # Changelog

β”‚### Before (Complex)

└── πŸ“ examples/                  # Code examples

    β”œβ”€β”€ EXAMPLE-USAGE.cs          # Basic usage```csharp

    └── IMPROVED-BOOTSTRAP-EXAMPLE.cs # Full setup// Complex registration

```services.AddInServiceEventHandlers(Assembly.GetExecutingAssembly());



### Component Diagram// Manual handler registration with reflection issues

public class Handler : InServiceEventHandlerBase<Event>

```{

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    // Required specific base class

β”‚                       Your Service                            β”‚}

β”‚                                                               β”‚

β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”           β”‚// Event with required properties

β”‚  β”‚   Entity        β”‚         β”‚  Domain Event    β”‚           β”‚public class Event : InServiceEventBase

β”‚  β”‚ (AggregateRoot) │─raises──►│   Handler        β”‚           β”‚{

β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β”‚    public Guid Id { get; set; } // Required

β”‚          β”‚                            β”‚                       β”‚    public DateTimeOffset OccurredOn { get; set; } // Required

β”‚          β”‚ AddDomainEvent()           β”‚ Handle()             β”‚}

β”‚          β–Ό                            β–Ό                       β”‚```

β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”‚

β”‚  β”‚     IInServiceEventBus (In-Memory)          β”‚            β”‚### After (Simple)

β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”‚

β”‚                     β”‚                                         β”‚```csharp

β”‚                     β”‚ Convert to IntegrationEvent            β”‚// Simple registration - auto-discovery

β”‚                     β–Ό                                         β”‚services.AddMessagingServices();

β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”‚

β”‚  β”‚     IEventBus (MassTransit β†’ RabbitMQ)      β”‚            β”‚// Flexible handler implementation

β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”‚public class Handler : IInServiceEventHandler<Event>

β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜{

                      β”‚    // Or optionally inherit from base class for logging

                      β”‚ Publish}

                      β–Ό

              β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”// Simple event definition

              β”‚   RabbitMQ    β”‚public class Event : IInServiceEvent

              β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜{

                      β”‚    // Only your business properties

                      β”‚ Subscribe}

                      β–Ό```

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”

β”‚                    Other Services                             β”‚## Documentation

β”‚                                                               β”‚

β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”            β”‚- [Detailed Usage Guide](USAGE-GUIDE.md)

β”‚  β”‚   ConsumerBase (MassTransit Consumer)       β”‚            β”‚- [Bootstrap Examples](IMPROVED-BOOTSTRAP-EXAMPLE.cs)

β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜            β”‚- [Code Examples](EXAMPLE-USAGE.cs)

β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

```## Requirements



---- .NET 9.0+

- MassTransit 8.0+ (for external events)

## πŸš€ Getting Started- RabbitMQ (for external events)



### Installation## License



Add the package reference to your project:Internal Nextera library - Not for public distribution



```bash---

dotnet add reference /path/to/Nera.Lib.Messaging.Abstractions/Nera.Lib.Messaging.Abstractions.csproj

```**Simple, Reliable, Type-Safe Messaging for Nextera Services** πŸ’ͺ

### Environment Variables

Configure RabbitMQ connection via environment variables (no code changes needed):

```bash
ENV_RABBIT_MQ_HOST=localhost
ENV_RABBIT_MQ_PORT=5672
ENV_RABBIT_MQ_USERNAME=guest
ENV_RABBIT_MQ_PASSWORD=guest
ENV_RABBIT_MQ_VIRTUAL_HOST=/

Basic Setup

Program.cs / Startup.cs:

using Nera.Lib.Messaging.Abstractions.Configuration;
using Nera.Lib.Messaging.Abstractions.Implementation;

// 1. Add messaging services (scans current assembly for handlers/consumers)
builder.Services.AddMessagingServices();

// 2. Add MassTransit with RabbitMQ (auto-configured from environment)
builder.Services.AddMassTransitWithRabbitMq(
    serviceName: "nera-organization-service",
    configure: cfg =>
    {
        // Optional: Add specific consumers
        cfg.AddConsumer<OrganizationCreatedConsumer>();
    });

// 3. Register in-service event handlers
builder.Services.AddInServiceEventHandlers(typeof(Program).Assembly);

That's it! πŸŽ‰ No complex configuration needed.


πŸ“š Usage Guide

1. Define Events

Domain Event (In-Service)
using Nera.Lib.Messaging.Abstractions.Events;

public sealed record OrganizationCreatedDomainEvent(
    Guid OrganizationId
) : IInServiceEvent;

Key Points:

  • Implements IInServiceEvent marker interface
  • Minimal properties (just IDs)
  • Record type with primary constructor
  • In-memory only (fast, no serialization)
Integration Event (Cross-Service)
using Nera.Lib.Messaging.Abstractions.Events;

public sealed record OrganizationCreatedIntegrationEvent : IInServiceEvent
{
    public required Guid OrganizationId { get; init; }
    public required string Name { get; init; }
    public required string Code { get; init; }
    public required bool IsActive { get; init; }
    // Full data for other services
}

Key Points:

  • Inherits from IntegrationMessageBase
  • Contains complete data needed by consumers
  • Auto-serialized to JSON for RabbitMQ
  • Headers auto-enriched (TenantId, UserId, etc.)

2. Raise Domain Events in Entities

using Nera.Lib.Database.Aggregates;

public class OrganizationEntity : AggregateRoot<Guid>
{
    public string Name { get; private set; } = string.Empty;
    public string Code { get; private set; } = string.Empty;

    public static OrganizationEntity Create(string name, string code)
    {
        var entity = new OrganizationEntity
        {
            Id = Guid.NewGuid(),
            Name = name,
            Code = code
        };

        // Raise domain event
        entity.AddDomainEvent(new OrganizationCreatedDomainEvent(entity.Id));

        return entity;
    }

    public void Update(string name)
    {
        Name = name;
        
        // Raise domain event
        AddDomainEvent(new OrganizationUpdatedDomainEvent(Id));
    }
}

How it works:

  1. Entity extends AggregateRoot<TKey>
  2. Call AddDomainEvent() when state changes
  3. Events automatically dispatched on SaveChangesAsync() by DispatchDomainEventsInterceptor

3. Handle Domain Events

using Nera.Lib.Messaging.Abstractions.Abstractions;
using Nera.Lib.Messaging.Abstractions.Events;

public class OrganizationCreatedDomainEventHandler : IInServiceEventHandler<OrganizationCreatedDomainEvent>
{
    private readonly IOrganizationRepository _repository;
    private readonly IEventBus _eventBus;
    private readonly ILogger<OrganizationCreatedDomainEventHandler> _logger;

    public OrganizationCreatedDomainEventHandler(
        IOrganizationRepository repository,
        IEventBus eventBus,
        ILogger<OrganizationCreatedDomainEventHandler> logger)
    {
        _repository = repository;
        _eventBus = eventBus;
        _logger = logger;
    }

    public async Task Handle(OrganizationCreatedDomainEvent @event, CancellationToken cancellationToken)
    {
        _logger.LogInformation("Handling domain event for Organization {Id}", @event.OrganizationId);

        // 1. Query repository for full data
        var organization = await _repository.GetByIdAsync(@event.OrganizationId, cancellationToken);
        if (organization == null)
        {
            _logger.LogWarning("Organization {Id} not found", @event.OrganizationId);
            return;
        }

        // 2. Convert to integration event
        var integrationEvent = new OrganizationCreatedIntegrationEvent
        {
            OrganizationId = organization.Id,
            Name = organization.Name,
            Code = organization.Code,
            IsActive = organization.IsActive
        };

        // 3. Publish to RabbitMQ
        await _eventBus.Publish(integrationEvent, cancellationToken);

        _logger.LogInformation("Published integration event for Organization {Id}", organization.Id);
    }
}

Pattern:

  1. Receive minimal domain event (just ID)
  2. Query repository for complete data
  3. Map to integration event with full payload
  4. Publish to RabbitMQ for other services

4. Consume Integration Events

using Nera.Lib.Messaging.Abstractions.Abstractions;
using MassTransit;

public class OrganizationCreatedConsumer : ConsumerBase<OrganizationCreatedIntegrationEvent>
{
    public OrganizationCreatedConsumer(ILogger<OrganizationCreatedConsumer> logger) 
        : base(logger)
    {
    }

    public override async Task Consume(ConsumeContext<OrganizationCreatedIntegrationEvent> context)
    {
        var message = context.Message;
        
        // Read standard headers
        var (tenantId, userId, correlationId, sourceService) = ReadHeaders(context);

        Logger.LogInformation(
            "Received OrganizationCreated: {Id} from {Source} (Tenant: {Tenant})",
            message.OrganizationId,
            sourceService,
            tenantId);

        // Process the event
        // ... your business logic ...

        await Task.CompletedTask;
    }
}

Best Practices

1. Event Design

DO:
  • Keep domain events minimal (just IDs)
  • Make integration events complete (full data)
  • Use records with init-only properties
  • Use required keyword for mandatory fields
// Good - Domain Event
public sealed record UserCreatedDomainEvent(Guid UserId) : IInServiceEvent;

// Good - Integration Event
public sealed record UserCreatedIntegrationEvent : IInServiceEvent
{
    public required Guid UserId { get; init; }
    public required string Email { get; init; }
    public required string FullName { get; init; }
}
❌ DON'T:
  • Put full data in domain events
  • Make domain events implement IntegrationMessageBase
  • Use mutable properties
// ❌ Bad - Too much data in domain event
public sealed record UserCreatedDomainEvent(
    Guid UserId, 
    string Email, 
    string FullName,
    string Address,
    string Phone
) : IInServiceEvent;

// ❌ Bad - Mutable
public class UserCreatedEvent
{
    public Guid UserId { get; set; }  // Should be init
}

2. Handler Pattern

DO:
  • Query repository in domain event handlers
  • Convert to integration event with full data
  • Log at appropriate levels
  • Handle null cases gracefully
public async Task Handle(OrganizationCreatedDomainEvent @event, CancellationToken ct)
{
    // Query for full data
    var org = await _repository.GetByIdAsync(@event.OrganizationId, ct);
    if (org == null) return;

    // Map to integration event
    var integrationEvent = new OrganizationCreatedIntegrationEvent
    {
        OrganizationId = org.Id,
        Name = org.Name,
        Code = org.Code
    };

    // Publish
    await _eventBus.Publish(integrationEvent, ct);
}
❌ DON'T:
  • Pass full entities in domain events
  • Skip null checks
  • Publish domain events to RabbitMQ
// ❌ Bad
public async Task Handle(OrganizationEntity entity, CancellationToken ct)
{
    // Wrong: Publishing domain event to RabbitMQ
    await _eventBus.Publish(new OrganizationCreatedDomainEvent(entity.Id), ct);
}

3. Naming Conventions

Domain Events:       {Entity}{Action}DomainEvent
Integration Events:  {Entity}{Action}IntegrationEvent
Handlers:            {Entity}{Action}DomainEventHandler
Consumers:           {Entity}{Action}Consumer

Examples:

  • OrganizationCreatedDomainEvent
  • OrganizationCreatedIntegrationEvent
  • OrganizationCreatedDomainEventHandler
  • OrganizationCreatedConsumer

πŸ”§ API Reference

Abstractions

IInServiceEventBus

In-memory event bus for domain events.

Task Publish<T>(T @event, CancellationToken cancellationToken = default) 
    where T : class, IInServiceEvent;
IEventBus

RabbitMQ event bus for integration events.

Task Publish<T>(T message, CancellationToken cancellationToken = default) 
    where T : class;

Task Publish<T>(T message, Uri endpointUri, CancellationToken cancellationToken = default) 
    where T : class;
IMessageContextAccessor

Access to message context (headers).

string? TenantId { get; }
string? UserId { get; }
string? CorrelationId { get; }
string? SourceService { get; }

Base Classes

ConsumerBase<T>

Base class for MassTransit consumers with header reading.

protected (string?, string?, string?, string?) ReadHeaders(ConsumeContext<T> context);
protected ILogger Logger { get; }
InServiceEventHandlerBase<T>

Base class for domain event handlers with logging.

protected ILogger Logger { get; }
public abstract Task Handle(T @event, CancellationToken cancellationToken);

πŸ“– Examples

See the /examples folder for complete working examples:


πŸ› Troubleshooting

Issue: "MessageHeaders ambiguous reference"

Problem: Conflict between Nera.Lib.Messaging.Abstractions.Configuration.MessageHeaders and MassTransit.MessageHeaders.

Solution: Use alias in your file:

using NeraMessageHeaders = Nera.Lib.Messaging.Abstractions.Configuration.MessageHeaders;

// Then use:
context.Headers.Get<string>(NeraMessageHeaders.TenantId);

Issue: "Cannot connect to RabbitMQ"

Check environment variables:

echo $ENV_RABBIT_MQ_HOST
echo $ENV_RABBIT_MQ_PORT

Verify RabbitMQ is running:

docker ps | grep rabbitmq

Issue: "Handler not called"

Check registration:

// Make sure you called this
builder.Services.AddInServiceEventHandlers(typeof(Program).Assembly);

Check handler signature:

// Must implement IInServiceEventHandler<T>
public class MyHandler : IInServiceEventHandler<MyEvent>
{
    public async Task Handle(MyEvent @event, CancellationToken ct) { ... }
}

πŸ“ Additional Documentation


🀝 Contributing

Please follow the existing patterns when contributing:

  1. Keep domain events minimal (IDs only)
  2. Keep integration events complete (full data)
  3. Add XML documentation for public APIs
  4. Update tests for new features
  5. Follow naming conventions

πŸ“„ License

Internal library for Nextera Systems.


πŸ†˜ Support

For questions or issues:

  1. Check Troubleshooting section
  2. Review docs/ folder
  3. Contact the platform team

Built with ❀️ by the Nextera Platform Team

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on Nera.Lib.Messaging.Abstractions:

Package Downloads
Nera.Lib.Database

Database access layer with Entity Framework Core, Repository pattern, Specification pattern, and advanced querying capabilities for Nera applications

Nera.Lib.Messaging.Sender

A robust messaging sender library for Nera applications providing event-driven communication capabilities with logging, retry policies, and template variable substitution

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.0.0 852 10/18/2025
1.0.4 368 10/8/2025
1.0.3 183 10/5/2025
1.0.2 303 9/28/2025
1.0.1 129 9/27/2025
1.0.0 688 9/9/2025