Skip to content
This repository was archived by the owner on Aug 13, 2020. It is now read-only.

User Guide

David Edwards edited this page Dec 21, 2016 · 14 revisions

This section of the wiki contains information relevant to developers wishing to make use of the framework in their applications.

Messages

A message is the basic unit of communication through the framework. Every event, command, query and response is considered within the framework as a message.

Messages are represented as envelopes, which consist of two components:

  • Metadata - information about the message.
  • Payload - the message itself.

The payload could be anything; the fields it contains depend on the type of message. The metadata could also contain anything, but there are a set of specific fields that we use by convention, some of which are mandatory.

Metadata

Below are the metadata fields that are known by the framework:

Field Required Type Description
id Yes UUID A unique UUID that is used to track the message as it passes through the system.
name Yes String The type of message. In the case of commands and queries, this is also referred to as the action name. This is used for many purposes, including dispatching messages to handlers, and for matching messages up with JSON schemas for validation.
userId Yes UUID The id of the user that initiated message. This might be a human user or a system user. If a message is generated as a result of another message, the userId is typically propagated.
sessionId No UUID If the message originated from a user-based session, this field contains the session id
clientCorrelationId No UUID A correlation id supplied by the client if they need to track result of sending a message at a later point.
causation No Array(UUID) An list of message ids recording the cause of this message. When a new message is created as a consequence of another message, the id of the original message is appended to the causation id list and copied to the new message.

JSON Representation

When we represent a full envelope as JSON, we treat the payload as JSON object being represented, with an additional _metadata field containing the metadata. There is no specific field containing the payload. In order to extract the payload, you simply remove the _metadata field.

The metadata fields described above are represented as nested fields. Below is an example showing how they are grouped and the JSON field names used for each:

{
  "_metadata": {
    "id": "861c9430-7bc6-4bf0-b549-6534394b8d65",
    "name": "example-context.commands.example-command",
    "correlation": {
      "client": "d51597dc-2526-4c71-bd08-5031c79f11e1"
    },
    "context": {
      "session": "45b0c3fe-afe6-4652-882f-7882d79eadd9",
      "user": "72251abb-5872-46e3-9045-950ac5bae399"
    },
    "causation": ["163af847-effb-46a9-96bc-32a0f7526f99"]
  },
  "exampleId": "1c64d54d-03ce-4246-90c0-d74f5bd6df55",
  "exampleField": "Example Value"
}

Code generators

There are several code generators available to generate adapters for incoming requests and clients for outgoing requests. The generated adapters and clients will be driven from RAML definitions of your service endpoints. The generators are for us with the RAML Maven plugin.

REST adapter generator

The REST adapter generator generates REST adapters for incoming REST requests.

Action mapping

The adapter generator has a mechanism to map incoming requests to actions within the framework. A combination of the resource endpoint and a content type is used to determine the action. For POST requests, the content type of the request body can be used. For GET requests, the accepted content type of the response body can be used.

The RAML Maven plugin currently only understands RAML 0.8 which does not support RAML annotations, etc. So to allow mappings to be defined within the RAML, the generator looks for custom blocks of YAML embedded within the description field of each resource.

The generator looks for a block within the description that is delimited with ... at the start and end. Each block can contain multiple (mapping) definitions, with the following fields:

  • requestType - the content type of a POST body
  • responseType - the content type of an accepted GET response body
  • name - the name of the action that will handle the request

For example:

/users/{userId}:
  description: |
    Commands relating to updating a user
  post:
    description: |
        ...
        (mapping):
            requestType: application/vnd.user+json
            name: create-user
        (mapping):
            requestType: application/vnd.address+json
            name: update-address
        ...
    body:
        application/vnd.user+json:
            schema: !include json/schema/user.json
        application/vnd.address+json:
            schema: !include json/schema/address.json
  get:
    description: |
        ...
        (mapping):
            responseType: application/vnd.user+json
            name: get-user
        (mapping):
            responseType: application/vnd.user-summary+json
            name: get-user-summary
        ...
    responses:
        200:
            body:
                application/vnd.user+json:
                application/vnd.user-summary+json:

Messaging adapter generator

The messaging adapter generator generates messaging adapters for incoming JMS requests. Adapter generation is driven from a messaging RAML:

#%RAML 0.8
title: Messaging RAML to generate Cakeshop event listener
baseUri: message://event/listener/message/cakeshop
version: v1
/cakeshop.event:
  post:
    body:
        application/vnd.cakeshop.recipe-added+json:
            schema: !include json/schema/cakeshop.recipe-added.json
            example: !include json/add-recipe.json
  • baseUri denotes type of component (e.g. event listener, event processor) and service name.
  • resource uri contains name of the destination (topic/queue).
  • Media type(s) of the body contains name of the event/command that later need to handled in a developer written handler, e.g:
@ServiceComponent(Component.EVENT_LISTENER)
public class RecipeAddedEventListener {

    @Handles("cakeshop.recipe-added")
    public void handle(final JsonEnvelope envelope) {
        //your code to handle recipe-added events
    }
}

It's also possible generate adapter that will handle all messages by specifing general json media type: application/json

#%RAML 0.8
title: Messaging RAML to generate Cakeshop event listener
baseUri: message://event/listener/message/cakeshop
version: v1
/cakeshop.event:
  post:
    body:
        application/json:

Your handler to handle all events would then look like this:

@ServiceComponent(Component.EVENT_LISTENER)
public class AllEventListener {

    @Handles("*")
    public void handle(final JsonEnvelope envelope) {
        //your code to handle all events
    }
}