Skip to content

Usage Scenarios

Gerfey edited this page Jul 24, 2025 · 3 revisions

Configuration | Custom Transports


Messenger is versatile and can be used in different architectural patterns.

The following are typical scenarios:

  • Microservice Communication: Messenger is well suited for messaging between microservices. One service can send a command or event through a broker (RabbitMQ), and other services can receive and process these messages. For example, service A sends an OrderCreatedMessage message via the AMQP transport, and service B and C listen to this queue and each has its own handler for this message (you can register multiple handlers for one message in different services). This ensures asynchronous, loosely coupled communication between system components. To implement such a scenario, make sure that all services use consistent transport settings (for example, one exchange/queue). On the sender's side, you don't need to register a local handler for the message, but only call Dispatch – the message will go to the broker. On the consumer side, on the contrary, handlers are registered, and Run is started to constantly listen to the broker.

  • Event-oriented architecture: In monolithic or distributed systems, Messenger allows you to publish and subscribe to domain events. You can define multiple handlers for the same event (message). Messenger will deliver a copy of the message to each handler (within the same process, sequentially, calling each handler). For example, the UserSignedUp event can have two handlers: one for sending a welcome email, the other for analytics. Both handlers are registered for the UserSignedUpMessage message and will be called in order. If one of the handlers returns an error, by default Messenger will stop further processing of this message and return an error for the entire Dispatch (thus failures can be tracked). In the distributed version, each service can listen to a certain type of events through its queue.

  • Job Queues: Messenger can be used for asynchronous task execution (background jobs). For example, you have a web application that needs to perform lengthy operations (report generation, image processing). Instead of executing them directly in the request, you can format the task as a message (for example, GenerateReportMessage) and send it via Messenger. If you don't need an external broker, you can start with an in-memory transport – then tasks will be executed asynchronously, but inside the same process. For a more reliable system or multiple workers, switch to RabbitMQ by specifying the AMQP transport. Messenger will take care of crash delays (if configured) and ensure that task handlers are executed. You can run multiple instances of the worker application, and RabbitMQ will distribute messages between them (assuming consumer_pool_size and multiple connections).

  • CQRS: Thanks to the support of multiple buses and transports, Messenger allows you to implement the CQRS (Command Query Responsibility Segregation) pattern. You can define a separate command bus (for commands that are processed synchronously within the same service) and a query bus (for requests that allow you to receive certain data). For example:

default_bus: commands
buses:
  amqp:
    dsn: 'amqp://...'
    ...
  commands: ~
  query: ~
routing:
  SomeEventMessage: amqp    # events go through the broker

To bind a handler to a specific bus, implement the api.MessageHandlerType interface in the handler. For example:

type QueryHandler struct { /* ... */ }
func (h *QueryHandler) GetBusName() string {
    return "query"
}

func (h *QueryHandler) Handle(ctx context.Context, msg *SomeQuery) (*dto.UserDTO, error) {
    // ... handle ...
    return &dto.UserDTO{}, nil
}

By registering such a handler, Messenger will send messages like `SomeQuery' to the 'query' bus (instead of the default 'commands'). The "query" bus in the config is linked to the "sync" transport, so the "SomeQuery" messages will be processed locally and instantly.


Configuration | Custom Transports

Clone this wiki locally