Skip to main content

Introduction to NetMediate

GenDI-first ecosystem: Throughout the docs, prefer NetMediate.SourceGeneration in the startup project and GenDI's [Injectable] + [Inject] style for your own services so you can control lifetime, group, order, and key. Use [Injectable<TService>] only when you need to force a specific non-generic contract and contract discovery does not already find [ServiceInjection]. Concrete non-generic classes that implement closed generic contracts can still use [Injectable]. Only generic/open service implementations (for example AuditBehavior<TMessage, TResponse>) should be registered manually in builder.Services for the AOT-oriented path.

Welcome to NetMediate, a lightweight and efficient .NET implementation of the Mediator pattern for in-process messaging and communication between components.

What is NetMediate?

NetMediate is a mediator pattern library for .NET that enables decoupled communication between components in your application. It provides a simple and flexible way to send commands, publish notifications, make requests, and handle streaming responses while maintaining clean architecture principles.

Key Features

🚀 Commands

Send one-way messages to all registered handlers sequentially. Perfect for triggering side-effects across multiple consumers.

📨 Notifications

Publish messages to multiple handlers — handlers are started concurrently and the dispatch is fire-and-forget (the pipeline task is discarded and the caller gets Task.CompletedTask immediately). Handler and behavior exceptions are logged by the executor but do not propagate to the caller. Batch notifications are dispatched sequentially in a loop. Ideal for event-driven architectures.

🔄 Requests

Send a message to a single handler and receive a typed response. Great for queries and request-response patterns.

📡 Streaming

Handle requests that return multiple responses over time via IAsyncEnumerable. Perfect for real-time data feeds.

🔌 Pipeline Behaviors

Interceptors with pre/post flow for every message kind. Implement cross-cutting concerns like logging, validation, and caching.

🔑 Keyed Handler Routing

Register handlers under named keys and dispatch to specific subsets at runtime — fully NativeAOT + Trimming compatible via GenDI keyed-service resolution.

🌊 Streaming Fan-Out

Multiple IStreamHandler registrations supported — their items are merged sequentially.

⏹️ Cancellation Support

Full cancellation token support across all operations.

🌐 Broad Runtime Compatibility

Multi-targeted for net10.0, netstandard2.0, and netstandard2.1.

Why this version is a productivity upgrade

  • Less setup friction: NetMediate.Core + NetMediate.SourceGeneration keep contracts and startup wiring clean while still enabling the runtime automatically.
  • Better code organization: generated typed dispatch methods keep mediator calls explicit and easier to maintain.
  • Scales with teams: compile-time registration and transitive generator propagation improve consistency across multi-project solutions.

Why Use the Mediator Pattern?

The Mediator pattern helps you:

  • Decouple Components: Reduce direct dependencies between classes
  • Single Responsibility: Each handler focuses on one specific task
  • Testability: Handlers can be easily unit tested in isolation
  • Maintainability: Changes to one handler don't affect others
  • Flexibility: Easy to add or remove handlers without affecting the rest of your code

Architecture Overview

┌─────────────┐
│ Client │
└──────┬──────┘

├─── Send Command ────►┌──────────────┐ ┌─────────────────┐
│ │ │ │ Command │
├─── Notify ──────────►│ ├─────►│ Notification │
│ │ IMediator │ │ Request │
├─── Request ─────────►│ │ │ Stream │
│ | | │ Handlers │
└─── RequestStream ───►└──────────────┘ └─────────────────┘


┌─────────────────┐
│ Pipeline │
│ Behaviors │
| Via Decorators |
└─────────────────┘

Message Types

NetMediate supports four types of messages, each with a specific purpose:

Message KindHandler InterfaceDispatch Semantics
CommandICommandHandler<TMessage>All registered handlers, sequential in registration order
RequestIRequestHandler<TMessage, TResponse>First registered handler only; returns TResponse
NotificationINotificationHandler<TMessage>All handlers started concurrently (fire-and-forget); handler exceptions are logged but do not propagate
StreamIStreamHandler<TMessage, TResponse>All registered handlers, items merged sequentially

No Marker Interfaces Required

Unlike some mediator implementations, NetMediate does not require marker interfaces on your message types. Any plain class or record can be a message:

public record CreateUserCommand(string Email, string Name); // ✅ Works!
public record UserCreatedNotification(string UserId); // ✅ Works!
public record GetUserQuery(string UserId); // ✅ Works!

Getting Started

Ready to dive in? Head over to the Installation Guide to get NetMediate set up in your project, or jump straight to the Quick Start for a working example.

Package Ecosystem

NetMediate consists of several packages:

  • NetMediate.Core - Contracts package for shared projects and reusable message definitions
  • NetMediate - Runtime mediator implementation consumed indirectly by the generator package or directly when needed
  • NetMediate.SourceGeneration - Source generator package for startup projects; injects NetMediate + GenDI.SourceGenerator through buildTransitive
  • NetMediate.Moq - Testing utilities for Moq

Community and Support

License

NetMediate is licensed under the MIT License. See the LICENSE file for details.