Seeding businessevents

Basics

The Business Events configurations database is not multi-tenant, meaning it can contain contain configuration for just one tenant.

Clients

Start by creating the clients:

DECLARE @clientCrm UNIQUEIDENTIFIER
DECLARE @clientDocuments UNIQUEIDENTIFIER
EXEC CreateOrGetClient 'crm-lime', @clientCrm OUTPUT
EXEC CreateOrGetClient 'documents-sharepoint', @clientDocuments OUTPUT

Possible naming conventions

  • [capability]-[system], e.g. "crm-lime"
  • [capability], e.g. "crm"
  • [system], e.g. "lime"

Note!

To be able to translate values int Nexus Value Translator, use the same client names as in the valuetranslations database.

Events, Publications and Subscriptions

The database structure has a few tables for representing an event.
Business Events database schema

Static configuration

Use stored procedures to handle the configuration for you:

DECLARE @minorId UNIQUEIDENTIFIER
DECLARE @majorId UNIQUEIDENTIFIER
DECLARE @publicationId UNIQUEIDENTIFIER

EXEC CreateOrGetEventWithVersion '<Entity name>', '<Event name>', <Major version>, <Minor version>, '{
	<contract>
}', @majorId OUTPUT, @minorId OUTPUT

EXEC CreateOrGetPublication @clientCrm, @minorId, @publicationId OUTPUT
EXEC CreateOrUpdateSubscription @clientDocuments, @publicationId, '$(ClientDocumentsProductUpdatedUrl)'

DECLARE @priority INT = 2
EXEC CreateOrUpdateSubscription @clientX, @publicationId, '$(ClientXProductUpdatedUrl)', @priority
-- Legacy
DECLARE @publicationId UNIQUEIDENTIFIER = '<guid>'
IF NOT EXISTS (SELECT 1 FROM Publication WHERE Id = @publicationId)
    INSERT INTO Publication (Id, ClientId, EventMinorVersionId) VALUES (@publicationId, @clientCrm, @minorId)
EXEC CreateOrUpdateSubscription @clientDocuments, @publicationId, '$(ClientDocumentsProductUpdatedUrl)'
DECLARE @priority INT = 2
EXEC CreateOrUpdateSubscription @clientX, @publicationId, '$(ClientXProductUpdatedUrl)', @priority
Attribute Description Example
CreateOrGetEventWithVersion Creates an event that some client can publish and other client subscibe to
<Entity name> The entity that the event is happening for Product, Person, Document
<Event name> The name of the event Created, LoggedIn, Deleted
<Major version> Use same major as long as no breaking changes are made (such as renamed or deleted attributes in the contract) 1, 2
<Minor version> Change the minor version when adding an attribute 0, 1, 2
<contract> A json object describing the fields of the event See more below
@majorId The id of the created MajorVersion
@minorId The id of the created MinorVersion, containing the Json contract
@publicationId A unique identifier (GUID) for a client's publication
CreateOrGetPublication Creates a publication for a client, or returns existing
CreateOrUpdateSubscription Creates a subscription for a client on the specified url
$(Client<Client name><Entity><Event>Url) A variable name representing the client's webhook url for this event. Could also be the url instead of variable. $(ClientDocumentsProductUpdatedUrl)
@priority A subscription can have a priority level so that the webhook call ends up on a prioritized queue. The default value is NULL.
Note! you need to configure priority queues in Async Caller
NULL (default), 1, 2, 3, ...

Dynamic subscription registration (available in BE version 1.13.0)

You can have a client register it's own subscriptions.

  1. Register the client with DynamicalRegistration
  2. Expose the subscription registration endpoint from Nexus Business Event throgh the Business API
  3. Let the client register it's subscriptions at runtime
DECLARE @clientX UNIQUEIDENTIFIER
EXEC CreateOrGetClient2 'dyna-x', 1, @clientX OUTPUT

where 1 means that the client has DynamicalRegistration.

Note: When calling the subscription registration endpoint, all the subscriptions for the client will be replaced.

Event contract

Each event has a JSON contract with attributes of certain types.

Type Example Comment
int 12, -31 An integer number
double 9.00982, -1.0 A double precision number
bool true, false A boolean value
string "Hello world" A UTF-8 string value
iso8601date "2019-05-20" An ISO date, without time
iso8601datetime "2019-05-20T13:29:00+02:00" An ISO date with time
email "name@example.com" An email address
Value translator concept "product.id" Used for value translations between clients
Array "OrderLines": [ { "Id": "string", ... } ] Define the object inside array brackets

Example

EXEC CreateOrGetEventWithVersion 'Product', 'Purchased', 1, 0, '{
    "ProductId": "product.id",
    "PurchasedBy": "person.id",
    "PurchasedAt": "iso8601datetime"
    "ItemCount": "int",
    "OrderLines": [
        {
            "Id": "string",
            "ArticleNumber": "string",
            "Count": "int"
        }
    ]
}', @majorId OUTPUT, @minorId OUTPUT

Optional attributes

Sometimes optional attributes are needed, e.g. if only one of two attributes have a value. To configure an attribute as optional, use the syntax

"<attribute name>": "<type> | null"

Example

{
    "WorkingPlaceId": "string | null",
    "UnitId": "string | null"
}

Versioning

All events have a Major and Minor version. They start out at 1.0 and backward compatible changes bumps the Minor version, whereas non-compatible changes bumps the major version.

Meta data on each event

When an event is sent, a MetaData attribute is automatically added:

{
    ProductId: ...,
    ...
    MetaData: {
        MessageId: "string",
        PublisherName: "string; (client name, see above)",
        EntityName: "string",
        EventName: "string",
        MajorVersion: "integer",
        MinorVersion: "integer",
        PublishedAt: "iso8601datetime"
    }
}

Test bench

Event publishers will want to verify their publications, either manually or as part of an integration test suite.

For this purpose, expose an endpoint in the platform api (calling Nexus Business Events test bench) which will respond with a PublicationTestResult object.

POST https://api.acme.com/BusinessEvents/TestBench/{Entity}/{Event}/{MajorVersion}/{MinorVersion}?client={client}

{
   "Attribute1": "value1",
   "Attribute2": "value2",
   ...
}
200 OK
{
    Verified: "bool",
    Errors: [ "...", "...", ... ]
    Contract: { ... }
    Payload: { ... }
}

(Note that the response code is always 200, even if Verified == false.

Variables

Configuration is very similiar across environments, so working with variables in the sql script is a way to reduce the number of files needed in the repository. In these examples we use the Azure Devops syntax for variables, $(VariableName).

Automation

One way to use continous integration for the seeding is like in this guide: Continuous integration for database seeding