githubEdit

Using an External Bus

Brighter provides support for an External Bus. Instead of handling a command or event, synchronously and in-process, (an Internal Event Bus) work can be dispatched to a distributed queue to be handled asynchronously and out-of-process. The trade-off here is between the cost of distribution (see The Fallacies of Distributed Computingarrow-up-right.

An External Bus allows you offload work to another process, to be handled asynchronously (once you push the work onto the queue, you don't wait) and in parallel (you can use other cores to process the work). It also allows you to ensure delivery of the message, eventually (the queue will hold the work until a consumer is available to read it).

As part of the Microservicesarrow-up-right architectural style an External Bus let's you implement an Event Driven Architecturearrow-up-right.

In addition use of an External Bus allows you to throttle requests - you can hand work off from the web server to a queue that only needs to consume at the rate you have resources to support. This allows you to scale to meet unexpected demand, at the price of eventual consistency.arrow-up-right. See the Task Queue Patternarrow-up-right

Brighter's External Bus Architecture

Brighter implements an External Bus using a Message Brokerarrow-up-right. The software that provides the Message Broker is referred to as Message-Oriented Middleware (MoM). Brighter calls its abstraction over MoM a Transport.

The producer sends a Command or Event to a Message Brokerarrow-up-right using CommandProcessor.Post() or CommandProcessor.DepositPost and CommandProcessor.ClearOutBox (or their *Async equivalents).

We use an IAmAMessageMapper to map the Command or Event to a Message. (Usually we just serialize the object to JSON and add to the MessageBody), but if you want to use higher performance serialization approaches, such as protobuf-netarrow-up-right, the message mapper is agnostic to the way the body is formatted.)

When we deserialize we set the MessageHeader which includes a topic (often we use a namespaced name for the Command or Event).

We store the created Message in a Outboxarrow-up-right for use by CommandProcessor.ClearOutbox() if we need to resend a failed message.

The Message Broker manages a Recipient Listarrow-up-right of subscribers to a topic. When it receives a Message the Broker looks at the topic (in Brighter terms the Routing Key) in the MessageHeader and dispatches the Message to the Recipient Channelsarrow-up-right identified by the Recipient List.

The consumer registers a Recipient Channelarrow-up-right to receive messages on a given topic. In other words when the consumer's registered topic matches the producer's topic, the broker dispatches the message to the consumer when it receives it from the producer.

A Message may be delivered to multiple Consumers, all of whom get their own copy.

in addition, we can support a Competing Consumersarrow-up-right approach by having multiple consumers read from the same Channelarrow-up-right to allow us to scale out to meet load.

TaskQueues

Sending via the External Bus

Instead of using CommandProcessor.Send() you use CommandProcessor.Post() or CommandProcessor.DepositPost and CommandProcessor.ClearOutbox to send the message

You add a message mapper to tell Brighter how to serialize the message for sending to your consumers.

Receiving via the External Bus

A consumer reads the Message using the Service Activatorarrow-up-right pattern to map between an Event Driven Consumerarrow-up-right and a Handler.

The use of the Service Activator pattern means the complexity of the distributed task queue is hidden from you. You just write a handler as normal, but call it via post and create a message mapper, the result is that your command is handled reliably, asynchronously, and in parallel with little cognitive overhead. It just works!

Last updated

Was this helpful?