RA.Utilities.Feature
This package is deprecated and has been moved to RA.Utilities.Dispatcher.
The main goal of this package is to help you organize your application's business logic into self-contained "features" or "slices." Instead of having logic for a single operation scattered across multiple layers (e.g., a controller, a service, a repository), all the code for one use case—from the incoming request to the final response—is located together.
This solves a common problem with traditional layered architectures where classes can become bloated and tightly coupled, making the codebase difficult to navigate and maintain.
✨ Key Features and How They Help
The package provides several key components to achieve this clean architecture:
- Custom Mediator (
IMediator):
- Purpose: A lightweight, reflection-free mediator implementation — no external MediatR dependency.
- Benefit: Strongly-typed generic dispatch for both request/response (
Send) and publish/subscribe (Publish) patterns.Sendreturns the response value directly — errors surface as typed exceptions.
- Base Handlers (
RequestHandler<TRequest, TResponse>):
- Purpose: Abstract base classes for your CQRS command and query handlers.
- Benefit: They remove boilerplate: implement the single abstract
HandleAsyncmethod and return values (or throw typed exceptions) directly — no explicit interface plumbing.
- Validation Pipeline Behavior (
ValidationBehavior<TRequest, TResponse>):
- Purpose: A pipeline behavior that automatically validates incoming commands and queries before they reach your business logic.
- Benefit: Uses
FluentValidation. If validation fails, it short-circuits by throwing aBadRequestExceptioncarrying the structured validation errors. Your handlers only ever deal with valid data.
- Notification System (
INotification):
- Purpose: Publish/subscribe eventing — fire-and-forget notifications handled by zero or more subscribers.
- Benefit: Built-in notification behaviors for logging, metrics, and retry. Each handler is independently wrapped, so one failing handler doesn't prevent others from executing.
- Typed Pipeline Context (
PipelineContext<T>):
- Purpose: Pass strongly-typed data through the entire pipeline — no dictionaries, no magic strings, no boxing.
- Benefit: Behaviors and handlers read/write properties on a user-defined
T. Each call gets its own isolated instance. Fully opt-in — existingSend/Publishcalls compile unchanged.
- Zero-Configuration Handler and Behavior Registration (
Auto Registration):
- Purpose: A source generator shipped in the package discovers all
IRequestHandler<,>/IRequestHandler<>/INotificationHandler<>implementations and closed pipeline behaviors, and registers them whenAddMediator()is called. - Benefit: Plain handlers and closed behaviors need no explicit
AddFeature/AddNotification/AddDecorationcalls — only validators and generic behaviors still require explicit registration.
- Source-Generated Mediator (
Generated Mediator):
- Purpose: The same generator emits the
IMediatorimplementation itself — a per-assemblyMediatorwith monomorphizedSendoverloads that inject the known handler and behaviors by concrete type and call them directly (no interface lookups, noGetServices, no casts), plus fast dictionary dispatch above 8 messages and object-based dispatch. - Benefit: At equal (scoped) handler lifetimes, benchmarks show the generated mediator 14-18% faster than MediatR with 9-14% fewer allocations in every comparable send shape. Inject
IMediatoror the concreteMediatorfor the fastest path.
- Exception-Based Errors (
RA.Utilities.Core.Exceptions):
- Purpose: Handlers communicate failures by throwing typed exceptions (
NotFoundException,ConflictException,BadRequestException, ...). - Benefit: A single
GlobalExceptionHandlerat the API layer maps them to consistent HTTP responses — noResultunwrapping at call sites. (TheResult<T>monad still exists inRA.Utilities.Core.Resultsfor code that wants it.)
- Benchmarked against MediatR (
Benchmarks):
- Purpose: A BenchmarkDotNet harness comparing the generated mediator against MediatR 12.5 at equal scoped lifetimes, scenario for scenario.
- Benefit: Published numbers for the send shapes — 12-18% faster with 9-14% fewer allocations — plus the harness to reproduce them locally.
🌟 Benefits
The package has been fully rewritten to avoid reflection; handlers are discovered by a source generator and resolved through strongly-typed generic DI registration instead of dynamic type lookups or runtime assembly scanning.
- ✅ No runtime reflection
- ✅ Purely generic and compile-time safe
- ✅ Zero-config handler registration via the in-package source generator — plain handlers need no registration code
- ✅ Simplified DI registration via fluent builders (
FeatureBuilderExtensions) for behaviors and validators - ✅ Custom mediator supports both request/response and publish/subscribe
- ✅ Multiple notification handlers per notification type
- ✅ Multiple notification pipeline behaviors (logging, metrics, retry)
- ✅ Fluent builder style consistent across requests and notifications
- ✅ Abstract
RequestHandlerbase classes — oneHandleAsyncoverride per handler - ✅ Exception-based errors — typed exceptions map to HTTP responses via
GlobalExceptionHandler - ✅ Typed
PipelineContext<T>for passing data through the pipeline — no dictionaries
🔧 Design Overview
| Namespace | Purpose |
|---|---|
Abstractions | Core interfaces: IMediator, IRequest, IRequestHandler, INotification, IPipelineBehavior, INotificationBehavior |
Handlers | Abstract base classes: RequestHandler<TRequest>, RequestHandler<TRequest, TResponse> |
Behaviors | Built-in pipeline and notification behaviors: logging, validation, metrics, retry |
Extensions | DI registration extensions: AddMediator(), AddFeature(), AddNotification() |
Models | Delegates: RequestHandlerDelegate, NotificationHandlerDelegate |
🛠️ Installation
You can install the package via the .NET CLI:
dotnet add package RA.Utilities.Feature
Or through the NuGet Package Manager console:
Install-Package RA.Utilities.Feature
🔗 Dependencies
RA.Utilities.Core.ExceptionsRA.Utilities.Core.ResultsRA.Utilities.Application.ValidationFluentValidationMicrosoft.Extensions.DependencyInjection.AbstractionsMicrosoft.Extensions.Logging.Abstractions
Abstractions
8 items
Behaviors
5 items
Models
3 items
Handlers
1 item
Generated Mediator
The source generator in the RA.Utilities.Feature package generates the IMediator implementation itself — the generated Mediator is the only mediator in the package.
Extensions
2 items
Exceptions
1 item
Benchmarks
The RA.Utilities.Feature.Benchmarks project compares the generated mediator against MediatR 14.2 using BenchmarkDotNet, scenario for scenario. Earlier revisions ran the same suite against MediatR 12.5 (and, historically, against the pre-v11.1.0 runtime mediator); the tables below are the current dataset.
Generated
2 items
Auto Registration
Starting with v10.2.0 handlers no longer need to be registered one by one.
Troubleshooting & FAQ
Common Issues
Migration Guides
v10.x → v10.2.0