website/src/docs/mocha/v16/handler-registration.md
builder.Services
.AddMessageBus()
.AddOrderService(); // source-generated from your assembly name
That registers the message bus, discovers your handlers and sagas at compile time, and wires up the registration. .AddOrderService() is a source-generated extension method - it knows your handler types at compile time and produces direct registration calls with no reflection.
At build time, the Mocha.Analyzers package runs a Roslyn incremental source generator that scans your assembly for classes implementing message bus handler interfaces. For each handler it finds, it emits a registration call in a generated extension method on IMessageBusHostBuilder.
The generator discovers these types:
| Interface | Pattern |
|---|---|
IEventHandler<T> | Pub/sub events |
IEventRequestHandler<TReq, TRes> | Request/reply |
IEventRequestHandler<TReq> | Send (fire-and-forget) |
IBatchEventHandler<T> | Batch processing |
IConsumer<T> | Low-level consumer |
Saga<TState> | Saga orchestration |
If you have used the Mediator source generator, this works the same way. The mediator generates Add{ModuleName}() on IMediatorHostBuilder; the message bus generates Add{ModuleName}() on IMessageBusHostBuilder.
Recommendation: Always use the source generator for handler registration. The generated code uses optimized, reflection-free registration paths. The source-generated output is designed for long-term stability across versions. Manual registration methods are available for edge cases but their internal behavior may change between releases.
The source generator names the extension method based on your assembly:
[assembly: MessagingModule("OrderService")], the method is AddOrderService()MyCompany.OrderService.Api produces AddApi()To set an explicit module name, add the attribute to any file in your project:
using Mocha;
[assembly: MessagingModule("OrderService")]
This generates:
builder.Services
.AddMessageBus()
.AddOrderService() // from [assembly: MessagingModule("OrderService")]
.AddRabbitMQ();
Convention: Use a short, meaningful name that identifies the service or bounded context -
OrderService,Billing,Inventory. This name appears in the generated code and in yourProgram.cs, so keep it readable.
For a project named OrderService with an event handler, a request handler, and a saga, the source generator produces an extension method AddOrderService() on IMessageBusHostBuilder. This method registers all discovered handlers and sagas with optimized, reflection-free factory delegates.
Handlers are grouped by kind and ordered alphabetically within each group. The registration order is: batch handlers, consumers, request handlers, event handlers, sagas.
Note: The generated code is an implementation detail and may change between versions. Do not depend on the shape of the generated output.
When you need to register handlers outside the source generator's reach - from a plugin assembly, a dynamically loaded module, or in integration tests - use the explicit registration methods:
builder.Services
.AddMessageBus()
.AddOrderService() // source-generated handlers
.AddEventHandler<ExternalNotificationHandler>() // from another assembly
.AddRequestHandler<PluginPaymentHandler>() // from a plugin
.AddRabbitMQ();
You can mix source-generated and manual registration freely. If both the source generator and manual code register the same handler type, the configurations are composed - the source generator sets up the base registration and your manual call layers additional configuration (such as consumer middleware) on top.
Prefer the source generator. Manual registration methods use runtime reflection to create handler consumers. The source generator produces direct, reflection-free factory calls. We guarantee backwards compatibility for the source-generated registration path; the manual registration API is stable at the surface level but its internal behavior may evolve.
If IntelliSense does not show Add{ModuleName}():
Mocha.Analyzers package is referenced with OutputItemType="Analyzer" in your .csprojMOIf the source-generated method is available but a specific handler does not run:
Mocha.AnalyzersIf a class implements more than one messaging interface (e.g., both IBatchEventHandler<T> and IEventHandler<T>), the source generator registers it using the highest-priority interface only:
IBatchEventHandler > IConsumer > IEventRequestHandler<T,R> > IEventRequestHandler<T> > IEventHandler