Migration path · MediatR
Keep mediator intent while replacing handlers in controlled slices.
MyServiceBus provides MediatR-familiar Send and Publish semantics, inferred query responses, dedicated handler interfaces, and generated registration. It is a semantic migration path—not a binary-compatible replacement package.
UsingMediator() dispatches inside the current process. Depend on the smaller IMediator contract and keep bus endpoints out of application components unless a workflow deliberately crosses a process boundary.
API migration map
| Intent | MediatR | MyServiceBus | Call or action |
|---|---|---|---|
| Command without result | IRequest + IRequestHandler<T> | IRequest + IHandler<T> | mediator.Send(command) |
| Query with result | IRequest<TResult> + IRequestHandler<T, TResult> | IRequest<TResult> + IHandler<T, TResult> | result = await mediator.Send(query) |
| Notification | INotification + INotificationHandler<T> | INotification + IHandler<T> | mediator.Publish(notification) |
| Registration | AddMediatR(...) | AddServiceBus + AddHandler + UsingMediator | Generated registration is optional. |
| Pipeline behavior | IPipelineBehavior<TRequest, TResponse> | Consume pipeline/filter configuration | Port ordering and lifetime assumptions explicitly. |
1. Preserve the contract intent
Commands and queries use exactly-one-handler Send. Notifications use fan-out Publish. Implementing MyServiceBus IRequest<TResponse> keeps the familiar inferred result call, so a query remains OrderView order = await mediator.Send(new GetOrder(orderId));.
2. Port one vertical slice
- Change the request and notification marker namespaces deliberately; the familiar names can otherwise hide an accidental mixed dependency.
- Port the corresponding request handler, notification handlers, validation, and tests together.
- Register the new handlers with
AddHandlerand resolve the MyServiceBusIMediator. - Keep the old MediatR registration for untouched slices until their callers have moved.
- Avoid registering the same command in both mediators behind one ambiguous application abstraction.
3. Rebuild pipeline behavior intentionally
MediatR pipeline behaviors are not copied automatically. Map validation, logging, authorization, transactions, and exception policy to MyServiceBus consume pipeline filters or application decorators, then assert ordering, scope, cancellation, and exception behavior. The filters and middleware guide shows the equivalent C# and Java mediator registration. A handler test passing does not prove the surrounding pipeline has equivalent semantics.
4. Choose generated or explicit registration
Explicit AddHandler registration is a clear migration baseline. Add the C# source-generator package and AddGeneratedConsumers() when compile-time registration and direct generated dispatch are useful. This removes reflection from handler discovery and invocation; it does not imply that every dependency or serializer in the application is reflection-free.
5. Verify the semantic differences
Sendfails when zero or multiple compatible handlers are registered.Publishinvokes every compatible local handler and waits for their pipelines.- Cancellation and terminal exceptions reach the caller.
- Handler and dependency scopes match the application's expectations.
- Any notification concurrency or ordering assumption is tested rather than inferred from familiarity.
Handler and consumer shapes share the same runtime, so a contract can later move behind RabbitMQ or Azure Service Bus. That is an architectural change with delivery, serialization, idempotency, and failure implications—not an automatic mediator toggle.
Java has the equivalent Mediator.send/publish, typed request, and handler experience. The JVM requires responseType() on a typed request because generic response information is erased at runtime.