Message Routing Patterns in FabrCore

Eric Brasher February 21, 2026 at 9:55 AM 5 min read

FabrCore's AgentMessage includes metadata fields for routing: Channel, MessageType, and Kind. These are the building blocks for routing logic within your agent's OnMessage handler.

The Routing Fields

FieldTypePurpose
Channel string Logical grouping — e.g., "user", "internal", "system"
MessageType string Operation type — e.g., "chat", "command", "delegation"
Kind MessageKind Request (expects response), OneWay (fire-and-forget), Response

These fields are strings (except Kind) because FabrCore doesn't prescribe your routing taxonomy. Your application defines what channels and message types mean.

Routing in OnMessage

All routing happens inside your agent's OnMessage override. A simple switch on message metadata is usually sufficient:

RoutingAgent.cs
public override async Task<AgentMessage> OnMessage(
    AgentMessage message)
{
    return message.Channel switch
    {
        "user" => await HandleUserMessage(message),
        "internal" => await HandleInternalMessage(message),
        "system" => await HandleSystemMessage(message),
        _ => await base.OnMessage(message)
    };
}

private async Task<AgentMessage> HandleUserMessage(
    AgentMessage message)
{
    return message.MessageType switch
    {
        "chat" => await ProcessChat(message),
        "command" => await ProcessCommand(message),
        _ => await base.OnMessage(message)
    };
}

Why Not External Routing Infrastructure?

Some teams build message bus abstractions, topic subscriptions, or routing tables on top of FabrCore. This is almost always unnecessary because:

Orleans already handles message delivery. When you call SendMessage("agent-handle", message), Orleans routes that message to the correct grain instance regardless of which silo it's on. The addressing and delivery infrastructure is already there.

Routing logic is agent-specific. How an agent routes messages depends on its role. A customer support agent routes differently than a coding assistant. Putting this in the agent's OnMessage makes the routing visible and testable right where the messages arrive.

Simple code beats configuration. A 15-line switch statement is easier to understand and debug than a routing table configuration that maps channels to handlers through reflection or convention. When routing breaks, you want to set a breakpoint in OnMessage, not trace through a routing framework.

Sending with Metadata

Sending a Routable Message
await AgentHost.SendMessage(targetHandle,
    new AgentMessage
    {
        Message = "Process this order",
        Channel = "internal",
        MessageType = "command",
        Kind = MessageKind.Request
    });

The receiver's OnMessage can switch on these fields to route the message to the appropriate handler method.

Learn More

Check out the message routing documentation for the complete reference.


Eric Brasher

Builder of FabrCore and OpenCaddis.