Reactivity
stores and exchange data in the JSON
format.
One reason why JSON
is used is the flexibility it offers in the data model.
This documentation describes the mandatory fields that must be found in a JSON
document and which path can be freely structured.
The documentation also pays attention to the data model which is persisted to the database comparing to the informations dedicated to event types.
Moreover, since everything is stream in Reactivity
, different fields of the data model are flagged to understand when they are defined and where they transit inside the system.
Data are always wrapped to an event.
This allows Reactivity
to understand what to do with the data.
Therefore, any producer must use a particular data structure in their JSON
document to wrap their data:
{ "version" : "The module version", "event" : "The event type here", "data" : "The data here", "timestamp" : "The event timestamp", "id" : "The event ID", "tag" : "A random value that looks like an UID" }
The version
is a value indicating the version of the module that produces the message.
This version can be used by the consumer to know what kind of assertion can be made on the data.
By default, new version must be backward compatible which means that a consumer should be able to deal with an event flagged with an older version.
Version format follows the semver protocol.
The event
describes the nature of the message.
The possible values are an enumeration specific to the type of data contained inside the message.
No field in the message indicates the type of data because it corresponds to the topic they are published to. In the core module, messages can be published to the following topic:
-
user
: the topic where events related toUsers
are published -
organization
: the topic where events related toOrganization
are published -
artifact
: the topic where events related toArtifacts
are published
This statement is true only when the client produce and consumes data directly from/to the topic (through Kafka
).
However, there is the particular case of a WEB application subscribing to a source of events comming from a suspended SSE
connection from the server.
+--------------+-------------+ +------->| Topic | | | +--------------+ | +-----------------+ | | user | Broadcaster | SSE | | | | organization | |------>| Web Application | | | artifact | | | | | +--------------+-------------+ +-----------------+ | | +---------------------------+ | | | +--------+ KAFKA | | | +---------------------------+
In this simple diagram we can see that the Broadcaster
is consuming different topics from Kafka
.
When a message is received, it’s pushed to the web application that established a SSE
connection.
The only chance for the web application to understand the type of data represented by a received message is to find topic
attribute in the JSON
.
{ "topic" : "organization|user|artifact" "version" : "...", "event" : "...", "data" : "...", "timestamp" : "...", "id" : "...", "tag" : "..." }
The data
contains the payload of the event provided by the producer.
The structure of the data depends on the topic where is has been published, the event
and version
fields values.
The consumer will use those informations to know what structure is expected in the data
value.
timestamp
and id
are not defined by the producer but are assigned by the streaming platform.
They allow to identify the message inside Reactivity
system and to know when it has been published by the consumer.
Therefore, a producer can generate a message that simply have this structure:
{ "version" : "...", "event" : "...", "data" : "...", "tag" : "..." }
Since the id
is not generated by the producer, it’s not possible for him to recognize a produced message when it’s received through another channel.
The tag
allows him to address this issue.
The producer can associate a random value that looks like an unique UID to the tag
attribute, allowing him to implement a kind of acknowledgment for the produced message.
This is particularly handy for web applications that receive a message previously produced.
In fact, the web application acts as a producer who expects to receive its own messages sent from the broadcaster.
+----------------------------+ SSE - Consume message A +-----------------+ +-->| Topic ---> Broadcaster |------------------------->| Web Application | | +----------------------------+ +-----------------+ | | | +---------------------------+ | | | | Produce message A | +---+ KAFKA |<-----------------------------------+ | | +---------------------------+
In this figure, we see that the message A
produced by the web application is received from the broadcaster.
With the tag
attribute whose value won’t have been changed, the web application will be able to recognized it and to treat it differently comparing to the messages produced by other instances.
For example, the web application can perform an optimistic change on the UI as soon as the message is sent, giving a better feeling of performance to the user.
When the message is received, the web application can simply ignore it.
The web application can also raise an error if the message is never received (timeout), which means that the message has possibly been discarded for any reason.
Extensions are allowed to define their own type of data stored in dedicated buckets
(the document space in Couchbase
) and sent through specific topics
(in Kafka
).
It’s strongly recommended for an extension to use the same message structure described in previous sections.
To avoid clashes with build-in features of Reactivity
, any extension topic
and bucket
must follow the pattern [extension-id]/[topic-name]
.
The extension-id
is an unique identifier for the extension in Reactivity
.
This value must be used when the extension creates its own topic.
The topic-name
is a value each extension is free to define.
This allows the extensions to define as much as data type they want and store their associated documents independently to the database.
When an extension extends an existing data type (Artifacts
, Users
or Organizations
), the mechanism is different.
The extension is allowed to modify the document created by the built-in features of Reactivity
.
In that scenario, the extension is integrated to a stream processing module that intercepts messages comming from a targetted topic (user
, artifact
or organization
).
The extension can validate the message payload and discard it in case of rule violation, modify the payload or trigger any specific action before the message is persisted.
The extension is free to manage specific properties at any level of the message. For instance:
{ "version" : "The module version", "event" : "The event type here", "data" : { "my-extension-some-property" : "some value" }, "timestamp" : "The event timestamp", "id" : "The event ID", "my-extension-some-property" : "some value" }
It’s strongly recommended that an extension does not add specific properties in a different place than what is defined in the above example.
Making assertion on the data
field structure to add properties in a deeper path is possible but it would strongly couple the core with the extension, which would potentially lead to lots of regressions after a new release.
The following section describes the possible values for data
and associated event
attribute in a message provided by the core of Reactivity
.
All messages related to the User
data type are published to the user
topic.
The CREATE
, READ
and UPDATE
events for a User
has a data
attribute associated to a value that looks like this:
{ "email" : "The user email address", "firstname" : "The user first name", "lastname" : "The user last name", "picture" : "The picture of user profile" }
READ
and UPDATE
events are sent for a given User
only to the consumers connected to an Organization
that registers this User
as a member.
A string corresponding to the user email. Mandatory attribute.
firstname
A string corresponding to the user first name. Optional attribute.
lastname
A string corresponding to the user last name. Optional attribute.
picture
A base64
string corresponding to the user picture. Optional attribute.
The DELETE
event for a User
has a data
attribute associated to an empty value.
In fact, the event simply indicates that a User
identified by the event id
has been removed.
Therefore, no more information than the id
is required to identify the removed view.
This event is sent for a given User
only to the consumers connected to an Organization
that registers this User
as a member.
All messages related to the Organization
data type are published to the organization
topic.
The CREATE
event for an Organization
has a data
attribute associated to a value that looks like this:
{ "name" : "The name of the organization", "picture" : "The picture of the organization", "members" : [{ "id" : "The ID of the organization member", "role" : "The role of the member inside the organization" }] }
This event is sent through this topic by producer when a new Organization
is created.
name
-
A string corresponding to the
Organization
name. -
Mandatory attribute.
-
Must be unique.
picture
-
A
base64
string corresponding to theOrganization
picture. -
Optional attribute.
members
An array containing complex objects with two mandatory fields:
-
id
: theUser
ID corresponding to the member. -
role
: the role of the member inside theOrganization
.
The READ
and UPDATE
events for an Organization
have a data
attribute associated to a value that looks like this:
{ "name" : "The name of the organization", "picture" : "The picture of the organization" }
This events are sent when a consumer is reading the Organization
associated to the current User
.
name
A string corresponding to the Organization
name. Mandatory attribute.
picture
A base64
string corresponding to the Organization
picture. Optional attribute.
The DELETE
event for an Organization
has a data
attribute associated to an empty value.
In fact, the event simply indicates that an Organization
identified by the event id
has been deleted.
Therefore, no more information than the id
is required to identify the removed Organization
.
The ADD_CATEGORY
, READ_CATEGORY
and UPDATE_CATEGORY
events for an Organization
have a data
attribute associated to a value that looks like this:
{ "organization": "The organization ID" "name" : "The name of the category" "picture" : "The picture of item category" }
Reactivity
provides User
and Status
categories out of the box.
organization
-
The
id
of theOrganization
associated to the category. -
Mandatory attribute.
name
-
A string representing the name of the category.
-
Mandatory attribute.
picture
-
A
base64
string corresponding to the category. -
Optional attribute.
The REMOVE_CATEGORY
event for an Organization
has a data
attribute associated to an empty value.
In fact, the event simply indicates that a category identified by the event id
has been removed.
Therefore, no more information than the id
is required to identify the removed category.
The ADD_CATEGORY_ITEM
, READ_CATEGORY_CATEGORY
and UPDATE_CATEGORY_ITEM
events for an Organization
have a data
attribute associated to a value that looks like this:
{ "category": "The category ID" "name" : "The name of the item" "picture" : "The picture of the item" }
Reactivity
provides the following values for the built-in categories:
-
User
: all the currentOrganization
members with theirid
as name and the picture if any -
Status
:TODO
,WIP
andDONE
values with no picture
category
-
The
id
of the category associated to the item. -
Mandatory attribute.
name
-
A string attribute the name of the item.
-
Mandatory attribute.
picture
-
A
base64
string corresponding to the item. -
Optional attribute.
The REMOVE_CATEGORY_ITEM
event for an Organization
has a data
attribute associated to an empty value.
In fact, the event simply indicates that an item identified by the event id
has been removed.
Therefore, no more information than the id
is required to identify the removed item.
The ADD_MEMBER
, REMOVE_MEMBER
, UPDATE_MEMBER
events for an Organization
have a data
attribute associated to a value that looks like this:
{ "organization" : "The organization ID", "members" : [{ "id" : "The ID of the organization member", "role" : "The role of the member inside the organization (only when adding or updating a member)" }] }
Those events are sent when a consumer is connected to an Organization
.
Only members of that Organization
are sent.
name
A string corresponding to the Organization
name. Defined only if value has changed.
members
An array defined only if members have changed and containing complex objects with two mandatory fields:
-
id
: theUser
ID corresponding to the member. -
role
: the role of the member inside theOrganization
. Only defined when the event isADD_MEMBER
orUPDATE_MEMBER
.
The ADD_VIEW
and READ_VIEW
events for an Organization
have a data
attribute associated to a value that looks like this:
{ "organization" : "The organization identifier", "name" : "The name of the view", "period" { "from" : "From when the artifacts are displayed", "to" : "Moment until artifacts are displayed", "category" : "The category id providing the timestamp to use" }, "filters" : [{ "category" : "Filter the artifact with the specified category", "value" : "The value that must equals to the specified category in the filtered artifact" }], "type" : "Type fo the view" }
ADD_VIEW
event is sent by a producer when a view is created.
READ_VIEW
event is sent to a consumer when this consumer is connected to an Organization
.
Only views related to the Organization
are sent.
organization
The organization id
owning the view.
name
The name of the view is an unique string inside the Organization
.
The value is displayed as a summary of the view.
period
A complex object describing the period of time covering the displayed informations. This object contains the following attributes:
-
from
: The min date ofArtifact
. Must be a timestamp in milliseconds. The value is mandatory. -
to
: The max date ofArtifact
. Must be a timestamp in milliseconds if defined. The value can beundefined
,null
orfalse
. In this case no max date is applied. -
category
: The categoryid
that will be used to read the timestamp from the artifact. Must be a timestamp in milliseconds if defined. The value can beundefined
,null
orfalse
. In this case the event timestamp will be used.
filters
Optional filters in addition to the period that are applied on the Artifact
when selecting the data.
filters
value is an array of complex object with two attributes:
-
category
: the categoryid
that must be used to filter theArtifact
. The value is mandatory. -
value
: the particular category label that must be associated to theArtifact
. The value can beundefined
,null
orfalse
. In this case any value is accepted since the category exist in theArtifact
.
type
The type of view describes a way to display and store the data.
The possible values are enumerated by the system.
Reactivity
supports out of the box three types:
-
list
: display artifacts in a list with one line per item -
table
: display artifacts in a table with one or two dimensions -
timeseries
: display the artifacts distributed over a periode of time inside a chart
Additional attributes when type = 'table'
.
{ "columns" : "Category id enumerating the columns", "rows" : "Category id enumerating the rows" }
-
columns
: a categoryid
containing the values displayed in column titles.Artifact
will be organized vertically according to the value of this category. This value is mandatory. -
rows
: a categoryid
containing the values displayed in row titles.Artifact
will be organized horizontally according to the value of this category. The value can beundefined
,null
orfalse
. In this case the table has only one dimension.
Additional attributes when type = 'timeseries'
.
{ "unit" : "The time unit defining the group level of the time series", "category" : "The category id used to count artifacts" "preferences" : { "colors" : [{ "label" : "The category label that should be displayed with a particular color", "value" : "The color to apply for the specified category label" }], "chart" : "The chat that displays the time series" } }
-
unit
: the time unit that corresponds to the group level of the time series from year to minute. This field is mandatory. Possible values areyear
,year/month
,year/month/day
,year/month/day/hour
,year/month/day/hour/minute
. -
category
: the categoryid
defining howArtifact
must be grouped. This field is mandatory. -
preferences.color
: an array where each object describes the preferred color to represent a category. Each object has alabel
attribute for the category label and avalue
for the color to apply. -
prefereces.chart
: a string indicated the chart displaying the data. The possible values are managed by the user interface.
All messages related to the Artifact
data type are published to the artifact
topic.
The CREATE
, READ
and UPDATE
events for an Artifact
have a data
attribute associated to a value that looks like this:
{ "views" : "The id of the different views this artifacts belongs to", "categories" : { "A free category key" : "A free category value" } }
-
views
: an array containing theid
of the different views containing thisArtifact
. -
categories
: a free set of key/value that describes the information defined by the user interface.Reactivity
expects the following categories by default:-
A key called
user
associated to anid
allowing to associate aUser
to anArtifact
-
A key called
status
associated toTODO
,WIP
orDONE
(the default items for theStatus
category) allowing to give astatus
to anArtifact
-
READ
and UPDATE
events are sent for a given Artifact
only to the consumers connected to an Organization
that registers the views containing this Artifact
.
When the Artifact
belongs to a view of type timeseries
the categories are actually used to specify time series information:
-
A key corresponding to the grouping category
id
associated to a value indicating the count result -
A key called
timestamp
corresponding to a time in milliseconds of the newest countedArtifact