You have most likely used libraries like MediatR, MassTransit, or CAP to handle commands and messages at some time during your.NET journey. Although each has advantages, wiring and configuration can occasionally impede the development of features alone. I recently learned about the JasperFx team's command and message bus library, Wolverine. It combines scheduled jobs, communications, CQRS, and a robust inbox/outbox into a single, streamlined solution. What's the best part? It's enjoyable to work with, quick, and clean.
This post will show you how to use Wolverine to manage commands, publish events, and schedule background messages without the need for a database or web API by guiding you through a straightforward console application.
Why Wolverine?
Wolverine aims to simplify distributed application architecture by combining.
- Command handling (like MediatR).
- Asynchronous and scheduled messaging.
- Built-in middleware and behaviors.
- Optional durable inbox/outbox with Marten (PostgreSQL).
Instead of bolting together multiple libraries, Wolverine gives you a unified model with batteries included.
The Example: Console-Based User Registration
We’ll build a console app that,
- Registers a user via a command.
- Sends a welcome email (simulated).
- Schedules a follow-up reminder message.
Everything runs in-process using Wolverine's local message bus.
Step 1. Create the Project
Project Folder Structure
WolverineConsoleDemo/
├── Program.cs
├── WolverineConsoleDemo.csproj
├── Messages/
│ ├── RegisterUser.cs
│ ├── UserRegistered.cs
│ └── RemindAdmin.cs
├── Handlers/
│ ├── RegisterUserHandler.cs
│ ├── UserRegisteredHandler.cs
│ └── RemindAdminHandler.cs
dotnet new console -n WolverineConsoleDemo
cd WolverineConsoleDemo
dotnet add package WolverineFx
Step 2. Define Messages and Handlers
RegisterUser.cs
public record RegisterUser(string Email, string FullName);
UserRegistered.cs
public record UserRegistered(string Email);
RemindAdmin.cs
public record RemindAdmin(string Email);
Step 3. Implement Handlers
RegisterUserHandler.cs
using Wolverine;
using WolverineConsoleDemo.Messages;
namespace WolverineConsoleDemo.Handlers
{
public class RegisterUserHandler
{
/// <summary>
/// Handles the specified command.
/// </summary>
/// <param name="command">The command.</param>
/// <param name="context">The context.</param>
public static async Task Handle(RegisterUser command, IMessageContext context)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"Registered: {command.FullName}");
Console.ResetColor();
await context.PublishAsync(new UserRegistered(command.Email));
await context.ScheduleAsync(new RemindAdmin(command.Email), TimeSpan.FromSeconds(10));
}
}
}
UserRegisteredHandler.cs
using WolverineConsoleDemo.Messages;
namespace WolverineConsoleDemo.Handlers
{
public class UserRegisteredHandler
{
/// <summary>
/// Handles the specified event.
/// </summary>
/// <param name="event">The event.</param>
/// <returns></returns>
public static Task Handle(UserRegistered @event)
{
Console.ForegroundColor = ConsoleColor.Blue;
Console.WriteLine($"Welcome email sent to {@event.Email}");
Console.ResetColor();
return Task.CompletedTask;
}
}
}
RemindAdminHandler.cs
using WolverineConsoleDemo.Messages;
namespace WolverineConsoleDemo.Handlers
{
public class RemindAdminHandler
{
/// <summary>
/// Handles the specified MSG.
/// </summary>
/// <param name="msg">The MSG.</param>
/// <returns></returns>
public static Task Handle(RemindAdmin msg)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine($"Admin reminded for: {msg.Email}");
Console.ResetColor();
return Task.CompletedTask;
}
}
}
Step 4. Configure Wolverine in the Program.cs
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Wolverine;
using WolverineConsoleDemo.Messages;
var builder = Host.CreateApplicationBuilder(args);
// suppress hosting lifetime logs
builder.Logging.AddFilter("Microsoft.Hosting.Lifetime", LogLevel.None);
builder.Services.AddWolverine(opts =>
{
opts.PublishMessage<RegisterUser>().ToLocalQueue("users");
opts.PublishMessage<UserRegistered>().ToLocalQueue("emails");
opts.PublishMessage<RemindAdmin>().ToLocalQueue("admin");
});
using var host = builder.Build();
// Start the host before using Wolverine
await host.StartAsync();
var bus = host.Services.GetRequiredService<IMessageBus>();
await bus.InvokeAsync(new RegisterUser("[email protected]", "Test User"));
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
// Optional: gracefully shut down
await host.StopAsync();
Sample Console Output
This output demonstrates how Wolverine utilizes color-coded messages to distinguish each part of the registration process clearly.

Conclusion
Wolverine brings a fresh, powerful approach to building message-driven systems in .NET. With minimal setup and zero boilerplate, we’ve seen how easy it is to.
- Create and handle commands.
- Publish and respond to events.
- Schedule background tasks.
- Run everything in-process without external queues or services.
This lightweight console app demonstrates that you don’t need heavy infrastructure to adopt clean, decoupled architectures. Whether you’re prototyping a feature, building core business logic, or moving toward microservices, Wolverine gives you the right tools with a developer-friendly experience. If you're tired of wiring up multiple libraries to do what Wolverine does out of the box, give it a try in your next project. You might be surprised how much simpler your code becomes.
European Best, cheap and reliable ASP.NET hosting with instant activation. HostForLIFE.eu is #1 Recommended Windows and ASP.NET hosting in European Continent. With 99.99% Uptime Guaranteed of Relibility, Stability and Performace. HostForLIFE.eu security team is constantly monitoring the entire network for unusual behaviour. We deliver hosting solution including Shared hosting, Cloud hosting, Reseller hosting, Dedicated Servers, and IT as Service for companies of all size.
