Introduction
In my previous article about Brighter, I demonstrated basic usage with RabbitMQ. Today, we’ll dive into the differences between Send, Publish, and Post – three core methods for message handling in Brighter.
What is a Bus?
A bus (or data highway) transfers data between components in a system. Brighter distinguishes between two types:
Internal Bus
- Operates within your application.
- Optimized for speed (e.g., avoids serializing requests to messages).
- Automatically registers all handlers and messages by default.
External Bus
- Connects outside your application (e.g., RabbitMQ, Kafka, AWS SQS/SNS).
- Requires I/O operations (network, file access).
Example: Configuring an External Bus with RabbitMQ
var rmqConnection = new RmqMessagingGatewayConnection
{
AmpqUri = new AmqpUriSpecification(new Uri("amqp://guest:guest@localhost:5672")),
Exchange = new Exchange("paramore.brighter.exchange"),
};
services.AddServiceActivator(opt => {})
.UseExternalBus(new RmqProducerRegistryFactory(rmqConnection,
new RmqPublication[]
{
new()
{
MakeChannels = OnMissingChannel.Create,
Topic = new RoutingKey("greeting.event"),
},
}
).Create());
Command vs events
Brighter also has a cool concept called Command pattern. The Command Pattern is a behavioural design pattern that encapsulates a request as an object, so you can send a request to one or more handlers.
Command
- Represents an action to execute immediately.
- Single handler expected.
- Use imperative names (e.g., CreateUser, UpdateData).
public class CreateUser() : Command(Guid.NewGuid())
{
public string Name { get; set; }
}
Event
- Represents something that already happened.
- Multiple handlers are allowed.
- Use past-tense names (e.g., UserCreated, DataUpdated).
public class UserCreated() : Event(Guid.NewGuid())
{
public string Name { get; set; }
}
Send, Publish & Post: When to Use Each
Method | Use Case | Bus Type | Handler Behavior |
---|---|---|---|
Send | Commands | Internal | Executes one handler |
Publish | Events | Internal | Executes all handlers |
Post | Commands/Events | External | Sends to external bus |
Usage Examples:
// Send a command (single handler)
commandProcessor.Send(new CreateUser { Name = "Alice" });
// Publish an event (all handlers notified)
commandProcessor.Publish(new UserCreated { Name = "Bob" });
// Post to an external queue
commandProcessor.Post(new OrderShipped { OrderId = 123 });
Code Walkthrough: Send vs. Publish
Command Handler (Single)
public class MyCommandHandler : RequestHandler<MyCommand>
{
private readonly ILogger<MyCommandHandler> _logger;
public MyCommandHandler(ILogger<MyCommandHandler> logger) => _logger = logger;
public override MyCommand Handle(MyCommand command)
{
_logger.LogInformation(
"Command handled: ID={Id}, Text={Text}",
command.Id, command.Text
);
return base.Handle(command);
}
}
Event Handlers (Multiple)
public class MyEventHandler1 : RequestHandler<MyEvent>
{
private readonly ILogger<MyEventHandler1> _logger;
public MyEventHandler1(ILogger<MyEventHandler1> logger) => _logger = logger;
public override MyEvent Handle(MyEvent @event)
{
_logger.LogInformation(
"Handler 1: Event ID={Id}, Text={Text}",
@event.Id, @event.Text
);
return base.Handle(@event);
}
}
public class MyEventHandler2 : RequestHandler<MyEvent>
{
// Similar logging logic as MyEventHandler1
}
Execution
var processor = provider.GetRequiredService<IAmACommandProcessor>();
// Send a command (single handler)
processor.Send(new MyCommand { Text = "Hello Command" });
// Publish an event (triggers both handlers)
processor.Publish(new MyEvent { Text = "Hello Event" });
Key Notes
- Using Send with a command that has multiple handlers throws an exception.
- Post bypasses in-process handling and delegates to the external bus.
Conclusion
Brighter’s command pattern elegantly decouples actions from their execution, making it ideal for scalable systems. In future articles, we’ll dive into retries, pipelines, and monitoring.
Full Code Example:
GitHub Repository
References
Brighter Documentation
Command Pattern Guide
Command Pattern (Wikipedia)
Top comments (0)