Guidelines On Messaging With AMQP

Introduction

Before diving into the details of messaging with AMQP, we will consider some basic concepts you should consider.

Contracts

A contract defines the message payload, and is the single point of truth on what type of payload to expect in the message.

  • JSON schema is a great tool to describe contracts.
  • Quicktype is an online JSON schema renderer, targeting multiple frameworks such as Python, C#, Typescript and Java.

Queues

A queue is the temporary storage of a message. All queues should be unique to every consumer context.

Exchanges

An exchange is the message switch on the message broker. It routes incoming messages to subscribing queues based on message topics.

Direct Exchanges

A direct exchange matches the whole topic with subscribing queues. Thus it is used to send a message with the command pattern to a single receiver, as a one-to-one exchange.

The default direct exchange used by PikaBus is named:

  • PikaBusDirect

Topic Exchanges

A topic exchange matches parts or more of the topic with subscribing queues. Thus it is used to publish a message with the event pattern to potentially many receiver, as a one-to-many exchange.

The default topic exchange used by PikaBus is named:

  • PikaBusTopic

Message Headers

All messages comes with headers giving some basic information about the message. PikaBus have defined a standard set of headers to enable different service implementations to comply on a common set of headers. The default PikaBus prefix is possible to change as preferred.

Header Key Header Value Type Description
PikaBus.MessageId Guid Unique message id.
PikaBus.CorrelationId Guid Unique message correlation id.
PikaBus.ReplyToAddress String Which address/queue to send replies.
PikaBus.OriginatingAddress String Originating address/queue.
PikaBus.ContentType String Content type, commonly application/json.
PikaBus.ContentEncoding String Content encoding, commonly utf-8.
PikaBus.MessageType String Optional contract namespace of message type, commonly <CONTRACT_NAMESPACE>.<CONTRACT_TYPE>.
PikaBus.Intent String Message intent - command or event.
PikaBus.TimeSent String UTC timestamp at what time the message was sent, commonly 07/22/2019 11:40:19 - (month/day/year hour:min:sec).
PikaBus.ErrorDetails String Error details attached in case of an exception.
PikaBus.SourceQueue String Source queue of a message that has been forwarded to an error queue after it has failed.

Events & Commands

With implementing a messaging service with PikaBus you will encounter the option to Publish or Send a message. Wether to choose either one follows a few basic principles. In short, a message shall only have one logical owner and usage of the Command or Event principle follows the message ownership.

Command Event
Used to make a request to perform an action. Used to communicate that an action has been performed.
Shall be sent to the logical owner. Shall be published by the logical owner.
Cannot be published. Cannot be sent.
Cannot be subscribed to or unsubscribed from. Can be subscribed to and unsubscribed from.
  • Table reference: https://docs.particular.net/nservicebus/messaging/messages-events-commands

Events

A published message is an event notified by the owner of the message to the public to communicate that an action has been performed. Keep in mind ownership of the message contract, thus the owner will be the event notifier and will have all rights reserved for publishing that message. Any subsribers to the event will subscribe on the topic of the event.

Topics

A topic is the event routing key used to publish a message, and must be unique across all services. The topic must be explicitly defined for every contract.

Commands

A sent message is a command sent directly to a recipient of a message endpoint to request an action. In this case, the message is owned by the consumer performing the action requested. With using RabbitMq as the message broker, the recipient address will be the queue name owned by the consumer.

Message Endpoints

A message endpoint is an explicit routing of a message sent to a recipient.

Event Types

There are mainly two types of events you should consider, notification and integration messages.

Notification Events Integration Events
Contains short and concise information about the event. Contains as much information as possible about the event.
Calls must be made back to the originator of the event to obtain more information. No more calls needs to be made since all information about the event follows the message.
Should only be used in closely coupled contexts due to coupling. Should be used between decoupled contexts to keep them decoupled.

Notification Events

Notification events should be short and concise, with minimal information describing the action performed. Any subsribers will react on the event usually by performing a synchronous call back to the publisher of the event to obtain more information.

Pros Cons
Event payload are kept at a minimum, and data is solely stored at the originator. Calls back to originator creates coupling, and you may end up with chatty services.

Integration Events

Integration events contains as much information as possible about the event to easily keep subscribers eventually consistent with the originator without coupling.

Pros Cons
All information about the event is contained within the message. Data is usually copied across services, and kept consistent following the eventually consistency principle.

Error Handling

Failed messages occur when a service fails processing the message after a given number of retries.

It is adviced to be as fault-tolerant as possible, and only throw an exception to fail the message when no other option is available.

By default, PikaBus implements error handling by forwarding failed messages to a durable queue named error after 5 retry attemps with backoff policy between each attempt.