Skip to main content

IntegrationEvent Base Class

All integration events inherit from the IntegrationEvent base record, which provides common properties for event tracking and identification.

Definition

namespace BuildingBlocks.Messaging.Events;

public record IntegrationEvent
{
    public Guid Id => Guid.NewGuid();
    public DateTime OccurredOn => DateTime.Now;
    public string EventType => GetType().AssemblyQualifiedName;
}

Properties

Id
Guid
Unique identifier for each event instance. Generated automatically when the event is created.
OccurredOn
DateTime
Timestamp when the event was created. Captured at the moment of instantiation.
EventType
string
Fully qualified type name including assembly information. Used for event routing and deserialization.

Design Patterns

Record Type

Integration events use C# records for several benefits:
  • Immutability: Events shouldn’t change after creation
  • Value equality: Compare events by their content, not reference
  • Concise syntax: Automatic property generation
  • With expressions: Easy event copying with modifications
// Records provide value equality
var event1 = new BasketCheckoutEvent { UserName = "john" };
var event2 = new BasketCheckoutEvent { UserName = "john" };
// event1 == event2 would be true if all properties match

// With expressions for copying
var modifiedEvent = event1 with { TotalPrice = 100.00m };

Auto-Generated Properties

The base class uses expression-bodied members for automatic property generation:
public Guid Id => Guid.NewGuid();
The Id property generates a new GUID on each access. If you need a stable ID, capture it when the event is created:
var basketEvent = new BasketCheckoutEvent { ... };
var eventId = basketEvent.Id; // Capture once

Creating Custom Events

To create a new integration event:

1. Define the Event

Create a new record inheriting from IntegrationEvent:
using BuildingBlocks.Messaging.Events;

namespace YourService.Events;

public record OrderCreatedEvent : IntegrationEvent
{
    public Guid OrderId { get; set; }
    public Guid CustomerId { get; set; }
    public decimal TotalAmount { get; set; }
    public List<OrderItem> Items { get; set; } = new();
}

public record OrderItem
{
    public Guid ProductId { get; set; }
    public int Quantity { get; set; }
    public decimal Price { get; set; }
}

2. Publishing Events

Use MassTransit’s IPublishEndpoint to publish events:
public class OrderService
{
    private readonly IPublishEndpoint _publishEndpoint;
    
    public OrderService(IPublishEndpoint publishEndpoint)
    {
        _publishEndpoint = publishEndpoint;
    }
    
    public async Task CreateOrderAsync(CreateOrderRequest request)
    {
        // Create order logic...
        
        var orderEvent = new OrderCreatedEvent
        {
            OrderId = order.Id,
            CustomerId = order.CustomerId,
            TotalAmount = order.TotalAmount,
            Items = order.Items
        };
        
        await _publishEndpoint.Publish(orderEvent);
    }
}

3. Consuming Events

Implement IConsumer<TEvent> to handle events:
using MassTransit;
using BuildingBlocks.Messaging.Events;

public class OrderCreatedEventHandler : IConsumer<OrderCreatedEvent>
{
    private readonly ILogger<OrderCreatedEventHandler> _logger;
    
    public OrderCreatedEventHandler(ILogger<OrderCreatedEventHandler> logger)
    {
        _logger = logger;
    }
    
    public async Task Consume(ConsumeContext<OrderCreatedEvent> context)
    {
        var message = context.Message;
        
        _logger.LogInformation(
            "Order created: {OrderId} for customer {CustomerId}",
            message.OrderId,
            message.CustomerId
        );
        
        // Handle the event...
    }
}

Best Practices

Use past tense for event names since they represent something that already happened:
  • OrderCreatedEvent
  • PaymentProcessedEvent
  • CreateOrderEvent
  • ProcessPaymentEvent
Keep events focused and specific:
  • Do: Create separate events for different business actions
  • Don’t: Create generic events that handle multiple scenarios
  • Events should represent a single business fact
Plan for event evolution:
public record OrderCreatedEventV2 : IntegrationEvent
{
    public Guid OrderId { get; set; }
    // New properties should have defaults
    public string? PaymentProvider { get; set; }
}
Include all necessary data in the event:
  • Consumers should not need to call back to the publisher
  • Include IDs for related entities
  • Avoid large payloads (use references instead)

Context Information

MassTransit provides rich context information through ConsumeContext:
public async Task Consume(ConsumeContext<OrderCreatedEvent> context)
{
    // Message content
    var message = context.Message;
    
    // Message metadata
    var messageId = context.MessageId;
    var conversationId = context.ConversationId;
    var sentTime = context.SentTime;
    
    // Response handling
    await context.RespondAsync(new OrderAcknowledgement
    {
        OrderId = message.OrderId,
        Acknowledged = true
    });
}

Next Steps

BasketCheckoutEvent Example

See a complete integration event implementation with publishing and consuming

Build docs developers (and LLMs) love