К основному содержанию
скилл CX Codex

messaging — скилл для Claude Code: асинхронные сообщения в .NET

Скилл для .NET-проектов: подсказывает, как настроить Wolverine или MassTransit, outbox, саги и брокеры RabbitMQ и Azure Service Bus.

Установить в Claude Code · Через Skills CLI (Vercel) Все способы и платформы ↓
npx skills add codewithmukesh/dotnet-claude-kit --skill messaging -a claude-code
Открыть на GitHub

Что делает messaging

Скилл описывает асинхронный обмен сообщениями в .NET. Внутри — настройка Wolverine, публикация событий двумя способами (возврат значения из обработчика или явный вызов IMessageBus), обработчики по соглашению об именах и транзакционный outbox с таблицами Wolverine в DbContext.

Wolverine здесь идёт как рекомендуемый вариант: лицензия MIT, медиатор и обмен сообщениями в одной библиотеке, outbox и саги из коробки. MassTransit — альтернатива, но с версии 9 требует коммерческой лицензии. Это повод заранее решить, готовы ли вы платить за брокерную обвязку.

Для простых сценариев на 2-3 шага советует хореографию событий, для компенсирующих действий и сложного состояния — сагу. Типы сообщений держите в отдельном проекте контрактов, простыми record с примитивными типами. В примерах настройки RabbitMQ хост задаётся как localhost, а AutoPurgeOnStartup() помечен как режим только для разработки.

Какие задачи решает

  • настроить Wolverine с RabbitMQ и автосозданием очередей
  • связать публикацию событий с транзакцией базы через outbox
  • описать сагу для заказа с оплатой и компенсацией
  • вынести контракты сообщений в общий проект
  • выбрать между Wolverine и MassTransit по лицензии

Примеры запросов агенту

  • Настрой Wolverine с RabbitMQ и транзакционным outbox для AppDbContext
  • Добавь обработчик OrderCreated в модуль уведомлений
  • Опиши сагу OrderSaga, которая стартует по OrderCreated и запускает ProcessPayment

Как установить messaging

1Через Skills CLI (Vercel)

npx skills add codewithmukesh/dotnet-claude-kit --skill messaging -a claude-code

2Вручную: положить файлы

~/.claude/skills/messaging/SKILL.md

Папка скилла целиком; для одного проекта — .claude/skills/messaging/

По шагам, с командами для каждой платформы: Как установить скилл в Claude Code: пять способов

Вопросы о messaging

Как установить messaging в Claude?

Выполните npx skills add codewithmukesh/dotnet-claude-kit --skill messaging -a claude-code. Скилл появится в ~/.claude/skills/messaging/SKILL.md.

Что делает скилл messaging?

Описывает паттерны асинхронного обмена сообщениями в .NET: Wolverine и MassTransit, outbox, саги и хореографию, настройку RabbitMQ и Azure Service Bus.

Чем Wolverine отличается от MassTransit?

Wolverine под лицензией MIT и объединяет медиатор с обменом сообщениями, outbox и сагами. MassTransit с версии 9 требует коммерческой лицензии.

Исходный текст

SKILL.md · MIT
---
name: messaging
description: >
  Asynchronous messaging patterns for .NET applications. Covers Wolverine and
  MassTransit, outbox pattern, saga and choreography, and broker configuration
  for RabbitMQ and Azure Service Bus.
  Load this skill when implementing event-driven communication, background
  processing, module-to-module messaging, or when the user mentions "Wolverine",
  "MassTransit", "message bus", "RabbitMQ", "Azure Service Bus", "event",
  "publish", "consumer", "outbox", "saga", "integration event", "queue",
  or "pub/sub".
---

# 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

```csharp
// 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.

```csharp
// 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
    }
}
```

```csharp
// 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.

```csharp
// 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.

```csharp
// 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.

```csharp
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:

```csharp
// 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

```csharp
// 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

```csharp
// 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

```csharp
// 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

| Scenario | Recommendation |
|----------|---------------|
| 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 publishing | Transactional outbox (both Wolverine and MassTransit support this) |
| Simple 2-3 step workflow | Event choreography |
| Complex workflow with compensation | Wolverine saga or MassTransit saga |
| Local development broker | RabbitMQ (via Docker or Aspire) |
| Production cloud broker | Azure Service Bus or RabbitMQ |
| Want single lib for mediator + messaging | Wolverine (replaces both Mediator and MassTransit) |

© codewithmukesh · MIT · codewithmukesh/dotnet-claude-kit

Похожие скиллы

Всё по теме «Бэкенд и API» →
azure-messaging-webpubsubservice-py Microsoft Скилл по Python SDK для Azure Web PubSub Service: рассылка сообщений по WebSocket, группы, токены доступа и управление подключениями. 1установок akka-net-management Aaronontheweb Скилл подсказывает агенту, как настроить Akka.Management и Cluster Bootstrap: динамическое обнаружение узлов, health-эндпоинты, работа в Kubernetes и Azure. CX Codex +1 409установок microsoft-azure-webjobs-extensions-authentication-events-dotnet Microsoft Скилл для работы с кастомными событиями аутентификации Microsoft Entra ID в .NET: добавление claims в токены, сбор атрибутов, OTP. 1установок azure-functions-csharp официальный github Правило для GitHub Copilot, которое задаёт стандарты разработки Azure Functions на C# в изолированной модели worker. Пригодится, если пишете функции и хотите единый стиль в проекте. ★ 40 418звёзд репо microsoft-code-reference официальный github Проверяет Microsoft API и SDK по официальной документации, ищет рабочие примеры и помогает разбирать ошибки в Azure и .NET-коде. 14 251установок appinsights-instrumentation davila7 Подключает веб-приложение к Azure App Insights: команда сама разберётся в стеке проекта и добавит отправку телеметрии. 91установок

Из того же набора46

openapi Настраивает встроенную генерацию спецификаций OpenAPI в .NET 10 без Swashbuckle. Помогает агенту писать трансформеры, метаданные и схемы безопасности. CX Codex +1 2 800установок configuration Настраивает конфигурацию .NET 10 через типизированные options, проверку и разделение секретов. Пригодится при работе с appsettings и переменными окружения. CX Codex +1 2 800установок scalar Скилл подсказывает агенту, как подключить Scalar вместо Swagger UI в приложении на .NET 10 и настроить темы, авторизацию и несколько документов. CX Codex +1 2 800установок opentelemetry Скилл настраивает трассировку, метрики и логи OpenTelemetry в .NET 10 через единый вызов с OTLP-экспортом. CX Codex +1 2 800установок project-structure Скилл задаёт агенту правила раскладки .NET-решения: центральные версии пакетов, общие свойства сборки, формат .slnx и именование проектов. CX Codex +1 2 800установок httpclient-factory Скилл обучает ассистента конфигурировать IHttpClientFactory и типовые HTTP-клиенты для .NET 10. Помогает настроить политики устойчивости и избежать исчерпания сокетов. CX Codex +1 2 800установок de-sloppify Наводит порядок в .NET-коде по семи шагам: от форматирования до передачи CancellationToken. Нужен перед релизом, после крупных изменений или обновления зависимостей. CX Codex +1 2 800установок logging Скилл про наблюдаемость в .NET 10: разводит liveness и readiness, прокидывает correlation ID и задаёт уровни логов. CX Codex +1 2 800установок

Спонсоры проекта

Компании, которые помогают держать аналитику открытой