Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
codewithmukesh avatar

Messaging

  • 128 installs
  • 629 repo stars
  • Updated August 3, 2026
  • codewithmukesh/dotnet-claude-kit

Implement messaging and communication patterns in .NET applications

About

Implements message queues, pub/sub patterns, and async communication for .NET. Enables messaging-based architectures.

  • Message queue patterns
  • Async communication

Messaging by the numbers

  • 128 all-time installs (skills.sh)
  • Ranked #2,758 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/codewithmukesh/dotnet-claude-kit --skill messaging

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs128
repo stars629
Last updatedAugust 3, 2026
Repositorycodewithmukesh/dotnet-claude-kit

What it does

Implement messaging and communication patterns in .NET applications

Files

SKILL.mdMarkdownGitHub ↗

Messaging

Core Principles

1. Wolverine is the recommended default — MIT licensed, combines mediator + messaging in one library with built-in outbox, saga support, and convention-based handlers. MassTransit is an alternative but requires a commercial license from v9. 2. Outbox pattern for reliability — Always use the transactional outbox to ensure messages are published only when the database transaction succeeds. 3. Choreography for simple flows, saga for complex — If a workflow has 2-3 steps, use event choreography. If it has compensating actions or complex state, use a saga. 4. Messages are contracts — Put message types in a shared contracts project. Keep them as simple records with primitive types.

Patterns

Wolverine Setup

// Program.cs
builder.Host.UseWolverine(opts =>
{
    // Auto-discover handlers from this assembly
    opts.Discovery.IncludeAssembly(typeof(Program).Assembly);

    // RabbitMQ transport
    opts.UseRabbitMq(rabbit =>
    {
        rabbit.HostName = "localhost";
        // Or from configuration:
        // rabbit.HostName = builder.Configuration["RabbitMq:Host"]!;
    })
    .AutoProvision()   // Create queues/exchanges automatically
    .AutoPurgeOnStartup(); // Dev only — clear queues on startup

    // Enable transactional outbox with EF Core
    opts.Services.AddDbContextWithWolverineIntegration<AppDbContext>(x =>
        x.UseNpgsql(builder.Configuration.GetConnectionString("Default")));

    opts.Policies.AutoApplyTransactions(); // Wrap handlers in DB transactions
});

Why: UseWolverine() registers handler discovery, transport, and outbox in one place. AutoProvision() eliminates manual broker setup during development.

Publishing Events

Wolverine supports two publishing styles: cascading messages (return values) and explicit publishing.

// Message contract (in shared Contracts project)
public record OrderCreated(Guid OrderId, string CustomerId, decimal Total, DateTimeOffset CreatedAt);

// Style 1: Cascading messages — return the event from the handler
// Wolverine automatically publishes returned messages after the handler completes.
public static class CreateOrder
{
    public record Command(string CustomerId, List<OrderItem> Items);
    public record Response(Guid OrderId, decimal Total);

    public static async Task<(Response, OrderCreated)> HandleAsync(
        Command command, AppDbContext db, TimeProvider clock, CancellationToken ct)
    {
        var order = Order.Create(command.CustomerId, command.Items, clock.GetUtcNow());
        db.Orders.Add(order);
        await db.SaveChangesAsync(ct);

        var response = new Response(order.Id, order.Total);
        var @event = new OrderCreated(order.Id, order.CustomerId, order.Total, order.CreatedAt);

        return (response, @event); // Both are published automatically
    }
}
// Style 2: Explicit publishing via IMessageBus
public static class CreateOrder
{
    public record Command(string CustomerId, List<OrderItem> Items);
    public record Response(Guid OrderId, decimal Total);

    public static async Task<Response> HandleAsync(
        Command command, AppDbContext db, IMessageBus bus, TimeProvider clock, CancellationToken ct)
    {
        var order = Order.Create(command.CustomerId, command.Items, clock.GetUtcNow());
        db.Orders.Add(order);
        await db.SaveChangesAsync(ct);

        await bus.PublishAsync(new OrderCreated(
            order.Id, order.CustomerId, order.Total, order.CreatedAt));

        return new Response(order.Id, order.Total);
    }
}

Why: Cascading messages (tuple return) are simpler and testable — the handler is a pure function. Use explicit IMessageBus when publishing is conditional or requires multiple events.

Consuming Events

Wolverine uses convention-based handlers — no interface, no base class. Just a Handle method with the message type as the first parameter.

// Notifications module — handles OrderCreated from Orders module
public static class OrderCreatedHandler
{
    public static async Task HandleAsync(
        OrderCreated message, NotificationsDbContext db, ILogger logger, CancellationToken ct)
    {
        logger.LogInformation("Processing OrderCreated: {OrderId}", message.OrderId);

        var notification = new OrderNotification(message.OrderId, message.CustomerId);
        db.Notifications.Add(notification);
        await db.SaveChangesAsync(ct);
    }
}

Why: Convention-based handlers have zero ceremony. Wolverine discovers them by signature: any public method named Handle/HandleAsync/Consume/ConsumeAsync with the message type as the first parameter.

Transactional Outbox

Ensures messages are only published if the database transaction succeeds.

// 1. Register DbContext with Wolverine integration
builder.Host.UseWolverine(opts =>
{
    opts.Services.AddDbContextWithWolverineIntegration<AppDbContext>(x =>
        x.UseNpgsql(builder.Configuration.GetConnectionString("Default")));

    opts.Policies.AutoApplyTransactions();
});

// 2. DbContext — add Wolverine outbox tables
public class AppDbContext(DbContextOptions<AppDbContext> options) : DbContext(options)
{
    public DbSet<Order> Orders => Set<Order>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Wolverine inbox/outbox tables — required for transactional messaging
        modelBuilder.AddIncomingWolverineMessageTable();
        modelBuilder.AddOutgoingWolverineMessageTable();
    }
}

Why: AddDbContextWithWolverineIntegration + AutoApplyTransactions wraps every handler in a transaction that includes outbox writes. Messages are only sent after the transaction commits — no dual-write problem.

Saga (Stateful Orchestration)

Wolverine sagas use a Saga<T> base class with Start and Handle methods. Cascading messages drive the saga forward.

public record OrderSagaState(Guid Id)
{
    public string? CustomerId { get; set; }
    public bool PaymentReceived { get; set; }
}

public class OrderSaga : Saga<OrderSagaState>
{
    public Guid Id { get; set; }

    // Start the saga when an OrderCreated event arrives
    public static (OrderSagaState, ProcessPayment) Start(OrderCreated message)
    {
        var state = new OrderSagaState(message.OrderId)
        {
            CustomerId = message.CustomerId
        };

        var command = new ProcessPayment(message.OrderId, message.Total);
        return (state, command); // State is persisted, command is sent
    }

    // Handle payment result
    public CompleteOrder Handle(PaymentCompleted message)
    {
        PaymentReceived = true;
        MarkCompleted(); // Ends the saga
        return new CompleteOrder(Id);
    }

    // Compensating action on failure
    public CancelOrder Handle(PaymentFailed message)
    {
        MarkCompleted();
        return new CancelOrder(Id);
    }
}

Why: Wolverine sagas use simple C# methods instead of a state machine DSL. Each handler returns cascading messages to drive the workflow. MarkCompleted() cleans up the saga state.

Alternative: MassTransit

MassTransit is a mature alternative with a commercial license requirement from v9+. Key API surface:

// Setup
builder.Services.AddMassTransit(x =>
{
    x.SetKebabCaseEndpointNameFormatter();
    x.AddConsumers(typeof(Program).Assembly);
    x.UsingRabbitMq((context, cfg) =>
    {
        cfg.Host(builder.Configuration.GetConnectionString("RabbitMq"));
        cfg.ConfigureEndpoints(context);
    });
});

// Publishing
await publishEndpoint.Publish(new OrderCreated(...), ct);

// Consuming — requires IConsumer<T> interface
public class OrderCreatedConsumer(AppDbContext db) : IConsumer<OrderCreated>
{
    public async Task Consume(ConsumeContext<OrderCreated> context)
    {
        var message = context.Message;
        // Handle event...
    }
}

// Outbox
x.AddEntityFrameworkOutbox<AppDbContext>(o =>
{
    o.UsePostgres();
    o.UseBusOutbox();
});

// Saga — uses MassTransitStateMachine<TState>
public class OrderSaga : MassTransitStateMachine<OrderSagaState> { /* ... */ }
License note: MassTransit v9+ requires a commercial license for production use. Wolverine (MIT) is the recommended default for new projects.

Anti-patterns

Don't Publish Events Without Outbox

// BAD — if SaveChanges succeeds but Publish fails, data is inconsistent
await db.SaveChangesAsync(ct);
await bus.PublishAsync(new OrderCreated(...));

// GOOD — use transactional outbox (messages are in the same transaction)
// Configure AddDbContextWithWolverineIntegration() + AutoApplyTransactions()
// Wolverine handles this automatically

Don't Put Complex Logic in Message Contracts

// BAD — behavior in a message
public record OrderCreated(Guid OrderId)
{
    public decimal CalculateShipping() => /* logic */; // DON'T
}

// GOOD — messages are pure data
public record OrderCreated(Guid OrderId, string CustomerId, decimal Total, DateTimeOffset CreatedAt);

Don't Use Fire-and-Forget for Important Events

// BAD — no guarantee of delivery
_ = Task.Run(() => bus.PublishAsync(new OrderCreated(...)));

// GOOD — await the publish (with outbox, this is transactional)
await bus.PublishAsync(new OrderCreated(...));

Decision Guide

ScenarioRecommendation
Module-to-module communication (new project)Wolverine with events (MIT, free)
Module-to-module communication (existing MassTransit)MassTransit (commercial license required from v9)
Reliable event publishingTransactional outbox (both Wolverine and MassTransit support this)
Simple 2-3 step workflowEvent choreography
Complex workflow with compensationWolverine saga or MassTransit saga
Local development brokerRabbitMQ (via Docker or Aspire)
Production cloud brokerAzure Service Bus or RabbitMQ
Want single lib for mediator + messagingWolverine (replaces both Mediator and MassTransit)

Related skills

Backend & APIsbackendintegrations

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.