Skip to content

An opinionated mediator library for clean application flow and CQRS-based projects.

License

Notifications You must be signed in to change notification settings

berk2k/FlowMediator

Repository files navigation

FlowMediator

NuGet NuGet Downloads Build

FlowMediator is a lightweight, opinionated mediator library for .NET 8 and .NET 9.

It focuses on explicit application flow, not generic messaging.


Why FlowMediator?

FlowMediator was built around a simple idea:

Not everything is a request.

Commands and queries represent intent.
Events represent facts that already happened.

FlowMediator enforces this distinction explicitly.


Core Concepts

Concept Description
SendAsync Commands & Queries (single handler, pipeline-enabled)
PublishAsync Events (multiple handlers, side-effect oriented)
Pipeline Applies only to SendAsync
Events Never treated as requests

Installation

dotnet add package FlowMediator

Request / Response Example

Define a Request & Handler

public record GetUserByIdQuery(int Id) : IRequest<UserDto>;

public class GetUserByIdQueryHandler 
    : IRequestHandler<GetUserByIdQuery, UserDto>
{
    private readonly IUserRepository _repository;

    public GetUserByIdQueryHandler(IUserRepository repository)
    {
        _repository = repository;
    }

    public async Task<UserDto> Handle(
        GetUserByIdQuery request,
        CancellationToken cancellationToken)
    {
        var user = await _repository.GetByIdAsync(request.Id);
        return user ?? throw new Exception("User not found");
    }
}

Register in DI

services.AddFlowMediator(typeof(GetUserByIdQuery).Assembly);
services.AddScoped<IUserRepository, UserRepository>();

services.AddTransient(
    typeof(IPipelineBehavior<,>),
    typeof(LoggingBehavior<,>)
);

Use Mediator

var mediator = provider.GetRequiredService<IMediator>();
var user = await mediator.SendAsync(new GetUserByIdQuery(1));
Console.WriteLine(user.Name);

Events (v2 Model)

Events are published, not sent.

  • Event handlers are executed sequentially (in-process) by default for predictability.
  • Handler order is not guaranteed unless you explicitly control registration/ordering.
  • If any handler throws, dispatch stops and the exception is rethrown (remaining handlers won’t run).
public class UserCreatedEvent : IEvent
{
    public Guid EventId { get; } = Guid.NewGuid();
    public DateTime OccurredOn { get; } = DateTime.UtcNow;
}

await mediator.PublishAsync(new UserCreatedEvent());

Event Dispatch Semantics

  • PublishAsync executes handlers in-process and sequentially by default.
  • Handler order is not guaranteed unless explicitly controlled.
  • No pipeline behaviors are applied to events.
  • Exceptions: If an event handler throws, PublishAsync stops and the exception is re-thrown (remaining handlers won’t run).
  • Events are not durable messages; for reliable cross-process delivery use Outbox + worker / broker.
  • Keep handlers fast; long-running work should go to background processing.

Event Handler

public class UserCreatedEventHandler
    : IEventHandler<UserCreatedEvent>
{
    public Task Handle(
        UserCreatedEvent @event,
        CancellationToken cancellationToken)
    {
        Console.WriteLine("User created");
        return Task.CompletedTask;
    }
}

Pipelines

Pipelines apply only to SendAsync.

They are ideal for:

  • Logging
  • Validation
  • Transactions

Events are executed outside the pipeline.

When to Use FlowMediator

  • Clean application flow
  • Explicit CQRS
  • Domain-driven design
  • Predictable execution

Roadmap

  • FlowContext (CorrelationId, UserId, Metadata)
  • Step-based execution model
  • Observability and retry
  • Command / Query specialization

⚠️ Disclaimer

FlowMediator focuses on application flow and execution semantics.

It does not provide:

  • Authorization or authentication
  • Security policies
  • Exception handling strategies
  • Infrastructure-level concerns

These responsibilities intentionally remain in the application layer.

FlowMediator is designed to be explicit and predictable,
not a full application framework.

Guarantees & Non-Goals

Guarantees

  • SendAsync is request/response: single handler, pipeline-enabled.
  • PublishAsync is event notification: multiple handlers, no response.

Non-Goals

  • Durable messaging / exactly-once delivery guarantees
  • Distributed transactions
  • Automatic retries without idempotency guarantees

🤝 Contributing

Contributions, bug reports, and feature requests are welcome.

📜 License

Licensed under the MIT License.

About

An opinionated mediator library for clean application flow and CQRS-based projects.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages