Skip to main content

message-patterns-dotnet

Apply pub/sub, request/reply, idempotency, outbox, and saga orchestration patterns in .NET without MediatR.

Source facts

Repository
visionarycoder/vc.Agentic
Last source activity
November 9, 2026 at 06:22
Detected SKILL.md language
English
Stars
0
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
message-patterns-dotnet
description
Apply pub/sub, request/reply, idempotency, outbox, and saga orchestration patterns in .NET without MediatR.
metadata
{"metadata-version":"1.0.0","title":"Message Patterns for .NET","document-type":"skill","repository":"vc.Agentic","language":"Standard Technical English","updated":"2026-11-08","collection":"optional"}
# Message Patterns for .NET Implement transport-agnostic messaging patterns with explicit contracts, durable dispatch, and replay-safe business handling. ## When to Use | Condition | Use | |---|---| | Task needs pub/sub, request/reply, or event-driven boundaries | Use this skill | | Duplicate delivery or replay safety matters | Use this skill | | Persistence and message dispatch need one transaction boundary | Use this skill | | Long-running business workflow spans multiple steps or services | Use this skill | ## When Not to Use | Condition | Route | |---|---| | Need is broker-specific topology or settlement | Use `azure-service-bus-patterns` or `rabbitmq-patterns` | | Work is in-process request orchestration only | Use direct method calls or channels | | Task asks for MediatR mediator pipeline | Use explicit application services instead | | Requirement is distributed cache or Redis pub/sub details | Use `redis-patterns-dotnet` | ## Required Inputs | Input | Required | Notes | |---|---|---| | Message contract set | Yes | Commands, events, replies, and ids | | Consistency boundary | Yes | Database transaction or equivalent | | Idempotency key source | Yes | Message id, business key, or request token | | Workflow state | Yes | Saga state fields and terminal conditions | | Timeout and retry path | Yes | Expiry, retry budget, compensation path | ## Workflow | Step | Agent Action | Test | Pass | |---|---|---|---| | 1 | Classify message flow as pub/sub, request/reply, outbox, or saga. | Review chosen pattern. | Pattern matches business boundary. | | 2 | Define stable message contracts with ids and correlation fields. | Inspect DTOs. | Contracts expose message id and correlation data. | | 3 | Implement idempotent handler boundary. | Review persistence path. | Duplicate delivery does not repeat side effects. | | 4 | Add outbox persistence where state change and publish share one unit of work. | Inspect transaction and outbox write. | State change and outbox record commit together. | | 5 | Add saga state and transition rules for long-running flow. | Review saga state transitions. | Every step has next, timeout, and terminal path. | | 6 | Verify duplicate, timeout, and compensation behavior. | Run targeted tests. | Success and failure flows pass. | ## Implementation Patterns | Pattern | Use | Avoid | |---|---|---| | Pub/sub event | One producer notifies many subscribers | Synchronous fan-out in the request thread | | Request/reply | Caller needs one correlated response | Event flow for strict single reply | | Idempotent handler | At-least-once delivery exists | Side effects keyed by transient transport state | | Outbox | Database write and publish share one boundary | Publish-after-commit with no durable dispatch record | | Saga orchestration | Multi-step workflow needs state and compensation | Distributed transaction across services | ```csharp public interface IMessage { string MessageId { get; } string CorrelationId { get; } } public sealed record OrderSubmitted(string MessageId, string CorrelationId, string OrderId, decimal Total) : IMessage; public sealed class OrderService(AppDbContext dbContext) { public async Task SubmitAsync(Order order, CancellationToken cancellationToken) { dbContext.Orders.Add(order); dbContext.OutboxMessages.Add(OutboxMessage.From("orders.submitted", JsonSerializer.Serialize(new OrderSubmitted(order.Id, order.Id, order.Id, order.Total)))); await dbContext.SaveChangesAsync(cancellationToken); } } public sealed class OrderSaga { public string OrderId { get; init; } = string.Empty; public string State { get; private set; } = "Submitted"; public void Apply(CreditReserved _) => State = "CreditReserved"; public void Apply(InventoryRejected _) => State = "Compensating"; } ``` ## Rules | Rule | Agent Verifies | Fix | |---|---|---| | MSG-001 | Contracts expose message id and correlation id | Add stable identifiers | | MSG-002 | Duplicate delivery does not repeat external side effects | Add idempotency store keyed by message id | | MSG-003 | Publish-after-write uses outbox inside same persistence boundary | Add outbox table and dispatcher | | MSG-004 | Saga state stays explicit and persistent | Add state record with timestamps and terminal state | | MSG-005 | Request/reply uses reply contract and timeout path | Add reply correlation and timeout handling | | MSG-006 | Event handlers stay transport-agnostic | Move broker logic behind publisher or consumer adapter | | MSG-007 | MediatR stays out of new design | Use explicit interfaces and handlers | ## Verification Checklist | Check | Test | Pass | |---|---|---| | Contract and correlation shape | Review DTOs and publisher or consumer boundaries. | Contracts expose message and correlation ids. | | Idempotency and outbox | Process the same message twice and simulate publish-after-write outage. | Side effects occur once and outbox rows persist for later dispatch. | | Request, event, and saga behavior | Exercise pub/sub, request-reply, and compensation paths that changed. | Replies correlate, subscribers react once, and saga state reaches the expected terminal value. | | Build and tests | Run targeted build and tests. | Build succeeds and changed tests pass. | ## Common Pitfalls | Pitfall | Agent Fix | |---|---| | Transport adapter contains business orchestration | Agent moves orchestration into application service or saga | | Publish occurs after database commit with no outbox record | Add outbox persistence | | Idempotency key uses random per-attempt value | Agent keys off stable message or business id | | Saga state lives in memory only | Agent persists saga state | | MediatR enters transport boundary design | Use explicit contracts and handlers | ## MCP Hooks | Need | GitHub MCP hook | Agent action | |---|---|---| | Find existing contract and outbox patterns | `search_code` | Search for message ids, correlation ids, outbox tables, and saga state before authoring new flows. | | Review messaging PR changes | `pull_request_read` | Read changed publishers, handlers, and persistence boundaries before extending message workflows. | | Verify idempotency coverage | `search_code` | Search for duplicate guards, retry logic, and compensation tests so replay safety stays consistent. | ## Outputs - Stable command, event, and reply contracts - Idempotent handler boundary - Outbox persistence and dispatcher flow - Saga state and transition model - Targeted verification steps for duplicate, timeout, and compensation paths
View on GitHub