A distributed messaging framework built on Microsoft Orleans, providing a unified API for producing and consuming messages across different brokers.
| Project | Description |
|---|---|
| Orleans.Messaging | Core abstractions — IMessagingClient, Message<T>, SubscriptionBuilder, grain contracts |
| Orleans.Messaging.Kafka | Kafka broker implementation |
| Orleans.Messaging.Memory | In-memory broker — ideal for testing and local development |
| Orleans.Messaging.SerDes | Pluggable serialization framework (JSON, Avro, String) |
All broker implementations expose the same IMessagingClient interface, resolved via keyed DI using a service key (see MessageBrokerNames).
public interface IMessagingClient
{
Task<string> Subscribe<TMessage>(Action<SubscriptionBuilder<TMessage>> configure);
Task<string> Subscribe<TMessage>(MessageSubscriptionInput<TMessage> input);
Task Unsubscribe<TMessage>(string subscriptionId);
Task Unsubscribe<TMessage>(TopicSubscription subscription);
Task Unsubscribe<TMessage>(string queueName, string subscriptionPattern, string subscriptionId);
Task Produce<TMessage>(string queueName, string key, TMessage message);
Task Produce<TMessage>(string queueName, Message<TMessage> message);
}// Payload only
await client.Produce<OrderCreated>("orders", orderId, new OrderCreated { ... });
// Full message with headers
var msg = new Message<OrderCreated>
{
Key = orderId,
Payload = new OrderCreated { ... }
}.AddHeader("source", "checkout");
await client.Produce("orders", msg);
// Convenience extension
var msg = new OrderCreated { ... }.AsMessage(key: orderId);There are two registration modes depending on where your service runs:
| Mode | Extension method | Use when |
|---|---|---|
| Silo | siloBuilder.AddMessagingKafka(key, ...) / siloBuilder.AddMessagingMemory(key, ...) |
Running inside an Orleans silo — registers full grain infrastructure for both producing and consuming |
| Client | hostBuilder.AddMessagingKafkaClient(key, ...) / hostBuilder.AddMessagingMemoryClient(key, ...) |
Running outside a silo (e.g., HTTP API, standalone worker that only publishes events) — registers a lean producer-only IMessagingClient |
Both modes resolve the same IMessagingClient interface; the client mode simply omits consumer grain infrastructure that requires a silo.
Run multiple independent broker instances side by side using MessageBrokerNames:
public static class MessageBrokerNames
{
public const string DefaultBroker = "messageBroker"; // default
public const string Conduit = "conduitMessageBroker";
public const string IronwoodRelay = "ironwoodRelayMessageBroker";
// ...
}Resolve the right client via keyed DI:
var client = serviceProvider.GetRequiredKeyedService<IMessagingClient>(MessageBrokerNames.DefaultBroker);