SlimMessageBus Documentation

repository·master·Indexed 20 days ago

https://github.com/zarusz/slimmessagebus

A lightweight, flexible, and extensible messaging framework for .NET supporting Pub/Sub and Request-Response patterns. It provides a facade for multiple transport providers including Kafka, RabbitMQ, Azure Service Bus, Azure EventHubs, Amazon SQS/SNS, NATS, Redis, MQTT, PostgreSQL, SQL, and in-memory messaging. Features include serialization plugins (System.Text.Json, Newtonsoft.Json, Avro, Protobuf), transactional outbox patterns, FluentValidation integration, and AsyncAPI specification generation.

Tokens
66K
Snippets
180
Records
233
Agent score
71%

What's inside SlimMessageBus

  1. Overview of SlimMessageBus documentation and features

    master

    SlimMessageBus is a lightweight message bus for .NET. The documentation provides comprehensive guides for setting up various transport providers, plugins, and specific use cases.

    Available Transports

    You can use SlimMessageBus with a wide variety of messaging backends:

    • Cloud Services: Amazon SQS/SNS, Azure EventHubs, Azure ServiceBus
    • Distributed Systems: Apache Kafka, NATS, RabbitMQ, Redis, MQTT
    • Databases: PostgreSQL, SQL
    • In-Memory: Memory (for local testing or simple scenarios)
    • Hybrid: Support for combining multiple transports

    Available Plugins

    Extend the bus functionality with these plugins:

    • Serialization: Configure how messages are encoded/decoded.
    • Transactional Outbox: Ensure reliable message delivery using the outbox pattern.
    • Validation: Use FluentValidation to validate messages before processing.
    • AsyncAPI: Automatically generate AsyncAPI specifications.
    • Resilience: Implement a Consumer Circuit Breaker for improved system stability.
  2. What is the Hybrid provider?

    master

    The Hybrid provider allows you to compose multiple transport providers into a single IMessageBus instance. This enables different layers of an application to use the same interface while the hybrid bus automatically routes messages to the correct transport (e.g., Memory, Kafka, Azure Service Bus) based on the message type and configuration.

    Note: Since version 2.0.0, the hybrid bus is the default bus implementation. The SlimMessageBus.Host.Hybrid package is deprecated.

  3. Understand deserialization error behavior

    master

    Errors during message deserialization occur at two main stages:

    1. MessageType Resolution

    SlimMessageBus uses a MessageType header to identify the payload type.

    • Missing Header: SMB attempts to infer the type from the consumer path (topic/queue), but only if exactly one message type is associated with that path. If multiple types are associated, deserialization fails.
    • Malformed/Unknown Type: If the header is malformed or refers to an unknown CLR type, the message fails in the underlying transport.
    • No Match: If no matching message type is declared for the path, the message fails in the transport.

    2. Actual Deserialization

    If the configured serialization plugin cannot parse the payload:

    • Transports with DLQ support (e.g., Azure Service Bus, SQS, RabbitMQ): The message is retried and eventually routed to the Dead Letter Queue (DLQ).
    • Transports without DLQ support (e.g., Kafka): The message is acknowledged as processed.
  4. Implement Request-Response communication

    master

    SlimMessageBus (SMB) supports asynchronous request-response patterns over topics or queues. This allows a service to await a response for a sent message.

    Key Requirements

    • Dedicated Reply Queue/Topic: Every micro-service instance that sends requests must have its own dedicated queue or topic for receiving replies. This ensures responses are routed back to the correct instance.
    • Reliability Warning: If a service instance crashes while awaiting a response, the context and TPL task are lost. For mission-critical workflows that must survive restarts, use the Saga pattern instead of request-response.

    Delivery Guarantees and Timeouts

    To prevent infinite waiting, you can configure timeouts globally or per request type. Timeouts are configured on the requesting (sender) side using .ExpectRequestResponses().

    // Configure the sender side to receive replies
    .ExpectRequestResponses(x =>
    {
      x.ReplyToTopic("servicename-instance1"); // Dedicated topic for this instance
      x.DefaultTimeout(TimeSpan.FromSeconds(20));
    })
  5. How AsyncAPI documentation is extracted from code

    master

    The plugin uses standard C# XML documentation comments to build the AsyncAPI document. It specifically looks at:

    • Message Types: The <summary> of the record or class.
    • Consumer Methods: The <summary> and <remarks> of the OnHandle method.

    Example of documented code that will appear in the AsyncAPI spec:

    /// <summary>
    /// Event when a customer is created within the domain.
    /// </summary>
    /// <param name="Id"></param>
    public record CustomerCreatedEvent(Guid Id, string Firstname, string Lastname);
    
    public class CustomerCreatedEventConsumer : IConsumer<CustomerCreatedEvent>
    {
        /// <summary>
        /// Upon the <see cref="CustomerCreatedEvent"/> will store it with the database.
        /// </summary>
        public Task OnHandle(CustomerCreatedEvent message, CancellationToken cancellationToken) { }
    }
    /// <summary>
    /// Event when a customer is created within the domain.
    /// </summary>
    public record CustomerCreatedEvent(Guid Id, string Firstname, string Lastname);
    
    public class CustomerCreatedEventConsumer : IConsumer<CustomerCreatedEvent>
    {
        /// <summary>
        /// Upon the <see cref="CustomerCreatedEvent"/> will store it with the database.
        /// </summary>
        public Task OnHandle(CustomerCreatedEvent message, CancellationToken cancellationToken) { }
    }
  6. How Message Type Resolution works

    master

    SlimMessageBus uses a MessageType header (a string) to communicate type information. This allows consumers to identify the correct .NET Type for deserialization.

    • Producer: Converts the .NET Type to a string and adds it to the header.
    • Consumer: Reads the header, resolves the string back to a .NET Type, and passes it to the serializer.

    Default Behavior: The AssemblyQualifiedNameMessageTypeResolver is used by default, formatting names as assembly-qualified names without version information (e.g., MyNamespace.MyMessage, MyAssembly).

    Missing Headers: If the MessageType header is missing (e.g., from a non-SMB producer), SMB attempts to infer the type based on the consumer's Topic or Queue definition. If a consumer handles multiple message types, resolution will fail.

  7. Migrate from MassTransit to SlimMessageBus

    master

    When migrating from MassTransit to SlimMessageBus, note the following differences:

    Consumer Interface

    • MassTransit: Uses IConsumer<T> with a Consume(ConsumeContext<T> context) method.
    • SlimMessageBus: Uses IConsumer<T> with an OnHandle(T message, CancellationToken cancellationToken) method.

    Configuration Style

    • MassTransit: Typically uses a nested configuration pattern within AddMassTransit.
    • SlimMessageBus: Uses a fluent builder API via AddSlimMessageBus for more concise setup.

    Advantages of SlimMessageBus

    • Interceptors: Native support for cross-cutting concerns like logging, tracing, and validation without modifying consumer logic.
    • Hybrid Messaging: Ability to combine in-memory and external providers (like Azure Service Bus) seamlessly.
    • Extensibility: Easy integration with plugins like FluentValidation or Outbox patterns.
  8. Key differences between MediatR and SlimMessageBus

    master

    When migrating from MediatR to SlimMessageBus, observe the following technical mapping:

    FeatureMediatRSlimMessageBus
    Service InterfaceIMediatorIMessageBus
    Request InterfaceIRequest<TResponse>IRequestMessage<TResponse>
    Handler MethodHandle(...)OnHandle(...)
    RegistrationAddMediatR(...)AddSlimMessageBus(mbb => mbb.WithProviderMemory()...)
    CapabilitiesPrimarily in-memoryIn-memory, Hybrid (In-memory + Out-of-process), and Interceptor pipelines