-
Notifications
You must be signed in to change notification settings - Fork 33
User Guide
This section of the wiki contains information relevant to developers wishing to make use of the framework in their applications.
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.
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. |
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"
}
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.
The REST adapter generator generates REST adapters for incoming REST requests.
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:
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
}
}