DEV Community

Cover image for Demystifying Brighter: Commands, Events, and the Send-Publish-Post Trifecta
Rafael Andrade
Rafael Andrade

Posted on

Demystifying Brighter: Commands, Events, and the Send-Publish-Post Trifecta

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());
Enter fullscreen mode Exit fullscreen mode

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; }
}
Enter fullscreen mode Exit fullscreen mode

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; }
}
Enter fullscreen mode Exit fullscreen mode

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 });
Enter fullscreen mode Exit fullscreen mode

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);  
    }  
}  
Enter fullscreen mode Exit fullscreen mode

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  
}  
Enter fullscreen mode Exit fullscreen mode

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" });  
Enter fullscreen mode Exit fullscreen mode

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)

AWS Q Developer image

Your AI Code Assistant

Implement features, document your code, or refactor your projects.
Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please consider leaving a ❤️ or a kind comment on this post if it was useful to you!

Thanks!