A zero-dependency mediator — no mandatory container

A mediator with zero
dependencies. And it's MIT.

Matios.Forge is a family of BCL-only .NET packages — one building block for each ring of Clean Architecture. Mediator, specification, state machine, strategy, repository and more. MIT, à la carte.

dotnet add package Matios.Forge.Mesh
That's it — the mediator needs nothing else. No DI container required.
mediator.Send(new GetUser("42"))
LoggingBehaviorIPipelineBehavior<,>
ValidationBehaviornext() ↴
GetUserHandlerIRequestHandler<GetUser, User>
↩ returns Result<User> — exactly one handler
The idea

Patterns with proportional weight.
One package per ring.

Not a framework you adopt whole. Each Clean Architecture concern is its own tiny, dependency-free package — take only what you need. Real machinery ships as code; trivial patterns ship as documented recipes, not empty classes.

0

Zero dependencies

Every module is BCL-only. The single exception is the opt-in DI adapter. No transitive surprises.

No mandatory container

The mediator wires up with an explicit, zero-reflection builder. Dependency injection is optional, not required.

§

MIT, à la carte

Install one module or all eight. No license tier, no revenue threshold, no all-or-nothing framework.

Quick start · Mesh

Request in, one handler out

Define a request and its handler. Wire it with a container — or without one. Add pipeline behaviors. Publish notifications to many.

using Matios.Forge.Mesh;

public sealed record GetUser(string Id) : IRequest<User>;

public sealed class GetUserHandler(IUserRepository repo)
    : IRequestHandler<GetUser, User>
{
    public Task<User> Handle(GetUser request, CancellationToken ct)
        => repo.Find(request.Id, ct);
}
// No DI container, no reflection — explicit and immutable.
IMediator mediator = new MediatorBuilder()
    .AddBehavior<GetUser, User>(new LoggingBehavior())
    .AddRequestHandler<GetUser, User>(new GetUserHandler(repo))
    .Build();                       // immutable, thread-safe

User user = await mediator.Send(new GetUser("42"));
// Prefer a container? The DI adapter scans and registers for you.
services.AddForgeMesh(cfg =>
    cfg.RegisterServicesFromAssemblyContaining<Program>());

IMediator mediator = provider.GetRequiredService<IMediator>();
// A notification goes to 0..N handlers. Business errors are values, not throws.
public sealed record OrderPlaced(Guid Id) : INotification;

public Task<Result<Guid>> Handle(PlaceOrder cmd, CancellationToken ct)
    => cmd.Amount <= 0
        ? Task.FromResult(Result.Failure<Guid>(Error.Of("order.invalid_amount")))
        : /* create, persist, publish, Result.Success(id) */;
Performance

Dispatch in nanoseconds, allocation-free

Handlers are resolved by type (O(1)) and the compiled, strongly-typed pipeline is invoked directly — no reflection on the hot path. The mediator itself adds almost nothing.

~15 ns
per Send
handler resolve + invoke
0 bytes
allocated by Publish
(synchronous handlers)
no
reflection on the hot path
compiled, strongly-typed
Scenario Latency (ns/op) Alloc (bytes/op)
Send (no behavior)~1572
Send (1 behavior)~31176
Publish (3 handlers)~210

BenchmarkDotNet on .NET 10 Release, one machine (i9-13950HX). The 72 bytes on Send are the handler's own returned Task — the mediator adds ~0. Hardware-dependent; treat as a relative reference.

The family

Eight modules, one per concern

Each is its own package. Start with the mediator; add the rest as your architecture needs them.

.Mesh
Use casesThe mediatorRequest/response, notifications, pipeline behaviors, Command with undo/redo. The flagship.
dotnet add package Matios.Forge.Mesh
.Core
Cross-cuttingResult, Error, GuardResult<T>, ValueObject, Entity<TId> — the shared primitives.
dotnet add package Matios.Forge.Core
.Spec
EntitiesSpecificationsComposable rules with And / Or / Not and operators.
dotnet add package Matios.Forge.Spec
.State
Use casesState machinePermit, guards, OnEntry/OnExit, Fire / CanFire / PermittedTriggers.
dotnet add package Matios.Forge.State
.Strategy
Use casesStrategy registryKeyed strategies with an optional default.
dotnet add package Matios.Forge.Strategy
.Repository
AdaptersRepository + UoWIReadRepository / IRepository / IUnitOfWork + an in-memory impl.
dotnet add package Matios.Forge.Repository
.Structural
Cross-cuttingStructural patternsDecorator, Composite and Memento building blocks.
dotnet add package Matios.Forge.Structural
.DI
FrameworksDI adapterAddForgeMesh: assembly scan + MS DI. The one module with a dependency — and it brings .Mesh in automatically.
dotnet add package Matios.Forge.DependencyInjection
Status & roadmap

All eight modules — on NuGet

Every module is implemented, tested and published, with an end-to-end sample crossing every ring.

v1.0 · now
Eight modules, on NuGetMediator, spec, state, strategy, repository, structural, core and the DI adapter — plus a working end-to-end orders sample. dotnet add package Matios.Forge.Mesh.
Streaming & more behaviorsOptional streaming requests and pre/post processors on the mediator. Issues and feedback welcome on GitHub.

Add it to your project

Open source · MIT · .NET 10 · zero dependencies

dotnet add package Matios.Forge.Mesh