Skip to content

PlatformHub Schemas

PlatformHub is an events based system, and it is designed in such a way as not to create interdependencies between plugins that communicate with each other.

As described before, this is done via messages. These messages can carry JSON payloads containing objects with primitive values. Therefore there are no traditional API contracts to make sure we're calling the right function with the right parameters.

With this in mind, how do we make sure we're sending the correct payload when we send a command or query and, how do we know what to expect when receiving a payload from an eventStream to which we're subscribed? Enter JSON schemas!

With JSON schemas each plugin can define the structure and typing of all the payloads it will receive/send according to what is declared in its Policy.

With that in mind let's look at a sample Policy file and how to construct a schema out of the messages declared in it.

Constructing Schemas

JSON
{
    "name": "AccountsPlugin",
    "publish": {
        "eventStreams": []
    },
    "subscribeOnDemand": {
        "eventStreams": [
            {
                "pluginName": "AccountsDomainModelPlugin",
                "messageName": "accountsListUpdated"
            },
        ]
    },
    "send": {
        "commands": [
            {
                "pluginName": "AccountsDomainModelPlugin",
                "messageName": "fetchAccountsList"
            }
        ]
    }
}

Parts of the Policy are hidden for clarity purposes

The Accounts plugin sends a command fetchAccountsList to the AccountsDomainModelPlugin and subscribes on demand for the accountsListUpdated eventStream returned by the AccountsDomainModelPlugin

JSON
{
    "name": "AccountsDomainModelPlugin",
    "publish": {
        "eventStreams": [
            {
                "pluginName": "AccountsDomainModelPlugin",
                "messageName": "accountsListUpdated"
            },
        ]
    },
    "receive": {
        "commands": [
            {
                "pluginName": "AccountsDomainModelPlugin",
                "messageName": "fetchAccountsList"
            },
        ],
    }
}

The AccountsDomainModelPlugin receives a fetchAccountsList command and publishes an accountsListUpdated event.

From this we can see the correlation between the two plugins and how the two policies are compatible. By adding schemas into the mix we can make sure there's also compatibility between the payloads being sent.

Schemas naming conventions

Before we delve deeper into how schemas are built, a quick word about naming conventions.

Payloads are divided into groups based on the Policy structure:

  • Send
  • Receive
  • Publish
  • SubscribeOnDemand
  • SubscribeOnStartup (doesn't take payloads)
  • PublishToTopic
  • SubscribeToTopic

For each of them there can be inputPayload(s) and outputPayload(s).

In our example the AccountsDomainModelPlugin will declare one InputPayload for the fetchAccountsList command and one InputPaylad and one OutputPayload for the accountsListUpdated eventStream.

These will be named according to the following convention

For send and subscribe message types:

{MessageContext}.{MessageType}.{PluginName}.{MessageName}.{PayloadType}.schema.json

For receive and publish message types:

{MessageContext}.{MessageType}.{MessageName}.{PayloadType}.schema.json

For publishToTopic message type:

PublishToTopic.TopicStream.{TopicName}.{TopicMessageName}.OutputPayload.schema.json

For subscribeToTopic message type:

SubscribeToTopic.TopicStream.{TopicName}.{TopicMessageName}.InputPayload.schema.json

Now that we know this, we can name our three payloads for the AccountsDomainModelPlugin

  1. Receive.Command.FetchAccountsList.InputPayload.schema.json
  2. Publish.EventStream.AccountsListUpdated.InputPayload.schema.json
  3. Publish.EventStream.AccountsListUpdated.OutputPayload.schema.json

Let's have a look at what those above mean:

  1. The payload that AccountsDomainModelPlugin accepts when plugins are sending it the fetchAccountsList command
  2. The payload that AccountsDomainModelPlugin accepts when returning the publisher that will emit accountsListUpdated values
  3. The payload that AccountsDomainModelPlugin sends when emitting values via the publisher for the accountsListUpdated eventStream.

Now since the AccountsPlugin wants to communicate with the AccountsDomainModelPlugin it will have to declare schemas that are compatible with what's been done above.

It will declare:

  1. Send.Command.AccountsDomainModelPlugin.FetchAccountsList.InputPayload.schema.json
  2. SubscribeOnDemand.EventStream.AccountsDomainModelPlugin.AccountsListUpdated.InputPayload.schema.json
  3. SubscribeOnDemand.EventStream.AccountsDomainModelPlugin.AccountsListUpdated.Output.schema.json

What these mean:

  1. The payload the AccountsPlugin sends with the fetchAccountsList command to AccountsDomainModelPlugin
  2. The payload the AccountsPlugin sends when asking for a publisher (only for SubscribeOnDemand since SubscribeOnStartup doesn't take any input payloads) that will emit the accountsListUpdated values
  3. The payload the AccountsPlugin expects from the AccountsDomainModelPlugin publisher emitting values for accountsListUpdated

It becomes clear that in order for the two plugins to be compatible, the corresponding schemas need to have matching fields between what AccountsDomainModelPlugin sends and what AccountsPlugin expects and vice versa.

For further details on schemas naming conventions please visit our Confluence page

Constructing Schemas (continued)

Let's now look in more detail at the contents of the different schemas

  1. The AccountsDomainModelPlugin does not expect to receive any payload with the fetchAccountsList command therefore it declares Receive.Command.FetchAccountsList.InputPayload.schema.json as a "null" payload
JSON
{
    "type": "null"
}

As already observed, AccountsPlugin needs to have an identical counterpart, which is going to be Send.Command.AccountsDomainModelPlugin.FetchAccountsList.InputPayload.schema.json and it too will be a "null" payload

JSON
{
    "type": "null"
}
  1. The AccountsDomainModelPlugin does not require any payload when being asked for the publisher for accountsListUpdated on subscribeOnDemand therefore again it will declare a "null" payload for Publish.EventStream.AccountsListUpdated.InputPayload.schema.json
JSON
{
    "type": "null"
}

And the same is true for the AccountsPlugin schema SubscribeOnDemand.EventStream.AccountsDomainModelPlugin.AccountsListUpdated.InputPayload.schema.json

  1. AccountsDomainPlugin declares that it will send a list of accounts as a payload when emitting values for accountsListUpdated and it does that in the schema Publish.EventStream.AccountsListUpdated.OutputPayload.schema.json
JSON
{
    "type": "array",
    "items": {
        "type": "object",
        "properties": {
            "name": {
                "type": "string"
            },
            "accountNumber": {
                "type": "string"
            },
            "kind": {
                "enum": [
                    "current",
                    "savings",
                    "credit"
                ]
            },
            "isPrimary": {
                "type": "boolean"
            },
            "balance": {
                "type": "number",
                "minimum": 0
            },
        },
        "required": [
            "name",
            "accountNumber",
            "isPrimary",
            "kind"
        ],
        "additionalProperties": false
    }
}

This schema shows that the payload must be an array, and the array will contain objects with certain properties (these actually describe the properties of an Account), of which some are required: name, accountNumber, isPrimary and kind

We know that the AccountsPlugin must declare what it expects to find inside the payload that it receives from the accountsListUpdated eventStream. This means that AccountsPlugin expects exactly what AccountsDomainModelPlugin sends, therefore they're compatible.

For more information on how JSON schemas work please visit JSON Schemas

Further details about how to create schemas for iOS can be found here.

Validation

A common validation script needs to be added to your IDE and will run at compile time, when building your host app. If all schemas are compatible it will succeed, otherwise it will surface errors in console guiding you through what's wrong with your schemas and the fixes needed. For more information on the tool and how to use it please see plarform-hub-tools

Suffix / File Extension

Must exactly match ".schema.json" in lower case

Example List of Schema Files Used in the iOS Reactive POC Sample App

- Publish.EventStream.AccountBalanceUpdated.InputPayload.schema.json
- Publish.EventStream.AccountBalanceUpdated.OutputPayload.schema.json
- Publish.EventStream.AccountTransactionBatchUpdated.InputPayload.schema.json
- Publish.EventStream.AccountTransactionBatchUpdated.OutputPayload.schema.json
- Publish.EventStream.AccountsListUpdated.InputPayload.schema.json
- Publish.EventStream.AccountsListUpdated.OutputPayload.schema.json
- Receive.Command.FetchAccountBalance.InputPayload.schema.json
- Receive.Command.FetchAccountTransactionBatch.InputPayload.schema.json
- Receive.Command.FetchAccountsList.InputPayload.schema.json
- Receive.Command.UpdateAccountIsPrimary.InputPayload.schema.json
- Send.Command.NetworkPlugin.PerformRequest.InputPayload.schema.json