# Consumer Foundation

## Goal

Build a consumer that is simple, reliable, and testable.

## Agent enforcement rules

### Do

- Prefer `record` message contracts with stable, explicit fields. Classes and interfaces are also supported when required by an existing contract or interoperability.
- Keep each consumer scoped to one business responsibility.
- Configure endpoints via registration/definitions and select retry, redelivery, and an outbox when their failure model applies.

### Do not

- Do not combine unrelated workflows in one consumer.
- Do not use sync-over-async or swallow exceptions.

## Build checklist

- Define explicit message contracts with stable names and fields.
- Keep one consumer focused on one business responsibility.
- Register with `AddMassTransit(x => x.AddConsumer<...>())`.
- Configure endpoints via `cfg.ConfigureEndpoints(context)`.
- Add retry only for short transient failures; add delayed redelivery when recovery may take longer.
- Use an outbox when outgoing messages must be coordinated with successful processing. Choose a transactional outbox when messages must survive a process failure.

## Example: consumer + registration

```csharp
public record SubmitOrder(Guid OrderId, string OrderNumber);
public record OrderSubmitted(Guid OrderId);

public class SubmitOrderConsumer : IConsumer<SubmitOrder>
{
    public async Task Consume(ConsumeContext<SubmitOrder> context)
    {
        await context.Publish(new OrderSubmitted(context.Message.OrderId));
    }
}

services.AddMassTransit(x =>
{
    x.AddConsumer<SubmitOrderConsumer>();

    x.UsingInMemory((context, cfg) =>
    {
        cfg.ConfigureEndpoints(context);
    });
});
```

## Example: consumer definition with retry + outbox

```csharp
public class SubmitOrderConsumerDefinition : ConsumerDefinition<SubmitOrderConsumer>
{
    protected override void ConfigureConsumer(
        IReceiveEndpointConfigurator endpointConfigurator,
        IConsumerConfigurator<SubmitOrderConsumer> consumerConfigurator,
        IRegistrationContext context)
    {
        endpointConfigurator.UseMessageRetry(r => r.Interval(5, 1000));
        endpointConfigurator.UseInMemoryOutbox(context);
    }
}
```

Register the definition by adding it as the second generic parameter:

```csharp
services.AddMassTransit(x =>
{
    x.AddConsumer<SubmitOrderConsumer, SubmitOrderConsumerDefinition>();

    x.UsingInMemory((context, cfg) =>
    {
        cfg.ConfigureEndpoints(context);
    });
});
```

## Guardrails

- Do not block threads with sync-over-async code.
- Do not swallow exceptions; preserve failure context.
- Do not publish side-effect events before durable state changes.
- Do not put unrelated workflows in the same consumer.

## Default implementation shape

1. Contract(s): command/event records.
2. Consumer: `IConsumer<TMessage>` with focused dependency set.
3. Registration: consumer + endpoint/middleware configuration and optional definition.
4. Tests: happy path, retry/fault path, idempotency behavior.

## Verification

- `dotnet build`
- `dotnet test --filter Consumer`
- `dotnet test --filter InMemoryTestHarness`

## References

- [Consumers (Concepts)](/concepts/consumers)
- [Consumer Configuration](/configuration/consumers)
- [Message Consumer Guide](/guides/message-consumer)
- [Retry, Redelivery, and Outbox](/concepts/outbox/#retry-redelivery-and-outbox)
