Wolverine
Overview
RCommon.Wolverine integrates WolverineFx as a distributed event handler for the RCommon event handling pipeline. It is focused on event production and subscription rather than the request-response mediator pattern.
The package provides:
WolverineEventHandlingBuilder/IWolverineEventHandlingBuilder— registers Wolverine-based subscribers through the RCommon fluent builder.PublishWithWolverineEventProducer— publishes events to all Wolverine handlers usingIMessageBus.PublishAsync(fan-out).SendWithWolverineEventProducer— sends events to a single Wolverine handler usingIMessageBus.SendAsync(point-to-point).WolverineEventHandler<TEvent>— bridges Wolverine's message dispatch to RCommon'sISubscriber<TEvent>abstraction.
Events must implement ISerializableEvent (from RCommon.Models) to flow through the Wolverine producers.
Installation
dotnet add package RCommon.WolverineThis package depends on RCommon.Core and WolverineFx. Wolverine itself requires host-level setup via UseWolverine() on the application host builder — see the Wolverine documentation for transport and endpoint configuration.
Configuration
Wolverine is configured at the host level and within the RCommon builder. Register the Wolverine host extension on the application host builder, then wire up RCommon subscribers.
using RCommon;
using RCommon.Wolverine;
using Wolverine;
var builder = WebApplication.CreateBuilder(args);
// Configure Wolverine at the host level
builder.Host.UseWolverine(opts =>
{
// Configure transports, endpoints, and discovery here
opts.Discovery.IncludeAssembly(typeof(Program).Assembly);
});
// Configure RCommon with Wolverine event handling
builder.Services.AddRCommon()
.WithEventHandling<WolverineEventHandlingBuilder>(wolverine =>
{
wolverine.Consume<OrderPlacedEvent, OrderPlacedEventHandler>();
wolverine.Consume<OrderShippedEvent, OrderShippedEventHandler>();
});
In RCommon 3.2.0, Consume<TEvent, TEventHandler>() is the idiomatic verb for an inbound Wolverine
consumer: it registers the ISubscriber<TEvent> handler and records the event subscription so the
router delivers the message. There is no separate AddProducer<T>() step to pair it with — that is
only needed on the outbound side when this service emits an event, via Publish<TEvent>() (fan-out)
or Send<TEvent>() (point-to-point). AddSubscriber<TEvent, TEventHandler>() remains as an
[Obsolete] alias forwarding to Consume for pre-3.2.0 source compatibility.
WithEventHandling<WolverineEventHandlingBuilder> is a cache-aware sub-builder verb. When multiple modules call it, the cached WolverineEventHandlingBuilder is reused and each module's AddSubscriber<TEvent, THandler> and AddProducer<T> registrations accumulate on the same instance. AddProducer<T> deduplicates by concrete producer type, so the same producer registered from two modules yields exactly one descriptor. Note that the host-level builder.Host.UseWolverine(...) call must happen exactly once in the application host; only the RCommon sub-builder participates in module-level composition. See Modular Composition for the full conflict matrix.
Usage
Defining an event
Events dispatched through Wolverine must implement ISerializableEvent:
using RCommon.Models.Events;
public class OrderPlacedEvent : ISerializableEvent
{
public OrderPlacedEvent(Guid orderId, Guid customerId, DateTime placedAt)
{
OrderId = orderId;
CustomerId = customerId;
PlacedAt = placedAt;
}
public Guid OrderId { get; }
public Guid CustomerId { get; }
public DateTime PlacedAt { get; }
}
Creating a subscriber
Implement ISubscriber<TEvent> from RCommon.EventHandling.Subscribers. This is the same interface used by MediatR subscribers — the Wolverine adapter bridges message dispatch to it automatically through WolverineEventHandler<TEvent>.
using RCommon.EventHandling.Subscribers;
public class OrderPlacedEventHandler : ISubscriber<OrderPlacedEvent>
{
private readonly IEmailService _emailService;
public OrderPlacedEventHandler(IEmailService emailService)
{
_emailService = emailService;
}
public async Task HandleAsync(OrderPlacedEvent @event, CancellationToken cancellationToken = default)
{
await _emailService.SendOrderConfirmationAsync(@event.CustomerId, @event.OrderId, cancellationToken);
}
}
Registration
Register subscribers using the IWolverineEventHandlingBuilder extension method. Each call records the event-to-producer subscription in the EventSubscriptionManager so the router only delivers the event to the producers registered for it.
builder.Services.AddRCommon()
.WithEventHandling<WolverineEventHandlingBuilder>(wolverine =>
{
wolverine.Consume<OrderPlacedEvent, OrderPlacedEventHandler>();
});
If you need to construct the subscriber with a factory delegate:
wolverine.Consume<OrderPlacedEvent, OrderPlacedEventHandler>(
sp => new OrderPlacedEventHandler(sp.GetRequiredService<IEmailService>()));
Producing events
Declare each event this service emits with Publish<TEvent>() for fan-out delivery (all subscribers receive the event) or Send<TEvent>() for point-to-point delivery (one handler). These verbs auto-register the matching producer (PublishWithWolverineEventProducer / SendWithWolverineEventProducer) and record the route with EventSubscriptionManager, so there is no need for a raw services.AddScoped<IEventProducer, T>() call:
builder.Services.AddRCommon()
.WithEventHandling<WolverineEventHandlingBuilder>(wolverine =>
{
// Fan-out: all subscribed handlers receive the event
wolverine.Publish<OrderPlacedEvent>();
// Point-to-point: only one handler receives the event
// wolverine.Send<OrderPlacedEvent>();
});
Produce events through RCommon's IEventBus or by raising domain events that flow through the event router — the producers are invoked by the routing infrastructure automatically.
API Summary
IWolverineEventHandlingBuilder extension methods
| Method | Description |
|---|---|
AddSubscriber<TEvent, TEventHandler>() | Registers TEventHandler as a scoped ISubscriber<TEvent> and records the subscription in the EventSubscriptionManager. |
AddSubscriber<TEvent, TEventHandler>(Func<IServiceProvider, TEventHandler>) | Same as above, but uses a factory delegate to construct the subscriber. |
PublishWithWolverineEventProducer
| Member | Description |
|---|---|
ProduceEventAsync<T>(T, CancellationToken) | Calls IMessageBus.PublishAsync<T> to deliver the event to all Wolverine handlers subscribed to T. Skips delivery if the event type is not subscribed to this producer. |
SendWithWolverineEventProducer
| Member | Description |
|---|---|
ProduceEventAsync<T>(T, CancellationToken) | Calls IMessageBus.SendAsync<T> to deliver the event to a single Wolverine handler. Skips delivery if the event type is not subscribed to this producer. |
WolverineEventHandler<TEvent>
| Member | Description |
|---|---|
HandleAsync(TEvent, CancellationToken) | Called by Wolverine's message dispatch infrastructure. Delegates to the injected ISubscriber<TEvent>. |
Key interfaces and base types
| Type | Package | Description |
|---|---|---|
IWolverineEventHandlingBuilder | RCommon.Wolverine | Builder interface for configuring Wolverine event handling. Extends IEventHandlingBuilder. |
WolverineEventHandlingBuilder | RCommon.Wolverine | Default implementation. Constructed by WithEventHandling<WolverineEventHandlingBuilder>. |
ISerializableEvent | RCommon.Models | Marker interface required on all events dispatched through Wolverine producers. |
ISubscriber<TEvent> | RCommon.EventHandling | Application-level handler interface. Implement this for your event handling logic. |
IWolverineEventHandler<TEvent> | RCommon.Wolverine | Wolverine-facing handler interface implemented by WolverineEventHandler<TEvent>. |