Handling asynchronously transfered data

This section describes the handling of the asynchronous data transfer within the DCIS. It may look a bit overcomplicated at first, but there is reason behind this madness.

As example we show the domain of dcis-users but the same pattern is applied to every other domain with data lead, too. So a single client may have multiple of the dcis-*-client and dcis-*-store libraries in use.

1. Defintion of library types

There are three different types of libraries provided for usage.

Generic Libraries

These libraries provide generic services used for certain architectural elements. Currently, there are three of them:

  • messaging

  • REST handling

  • UI handling

Client Libraries

Libraries for receiving asynchronous data. Every SCS sends out events to the messaging broker for data changing actions. These client libraries offer an easy way to connect to these events. They provide an SPI where the implementation of the local SCS can plug in the handling of these events.

Store Libraries

Libraries for storing the data locally. Normally we store via JPA. These libraries implement the SPI for the client library and store the "canonical data format" (I don) t have a better name for it). But these libraries are optional and can be ignored by the SCS if they prefer other data format or organization.

2. Using the canonical store library

In this example, the client SCS just uses the library dcis-store-users and let the magic happen.

  1. The java library de.paladins-inn.torganized-play:dcis-users-store needs to be added to the dependency list of the client. The de.paladins-inn.torganized-play:dcis-users-client will be automatically included.

  2. The liquibase master changelog needs to include db/users/master.yml as changelog to get the updates to the SQL tables.

  3. The package de.paladinsinn.tp.dcis.users.store needs to be added to

    • @EntityScan

    • @JpaRepositoryScan

A small diagram showing the orchestration for client libraries and store libraries, e.g. users domain
@startuml
left to right direction
skinparam actorStyle awesome
skinparam nodesep 50
skinparam ranksep 200

component scsClient as Client {
  component "client-code" as scsClientCode

  rectangle << generic >> {
    package lib.messaging << generic >> {
      component EventBus as bus
    }

    package lib.rest << generic >> {
    }

    package lib.ui << generic >> {
    }
  }

  component users.client << client >> {
    component messaging as amqp

    package de.paladinsinn.tp.dcis.users.model {
    }
  }

  component users.store << store >> {
    package de.paladinsinn.tp.dcis.users.store {
      component JpaUserEventsHandler
      component UserJPA
      component UserRepository
      component UserService
    }
  }
}

together {
  component "Messaging Broker" as broker << External >>
  component "dcis-users" as scsUser << External >>
}

amqp --> bus : data update
bus --> JpaUserEventsHandler : data update

scsClientCode --> lib.rest : use
scsClientCode --> lib.ui : use
scsClientCode --> UserService : read data
scsClientCode --> UserService : write data

UserService --> UserRepository : read
UserService --> bus : write data
JpaUserEventsHandler --> UserRepository : write
UserRepository --> UserJPA

bus --> amqp : write data

broker <.up. scsUser : data update
broker .up.> scsUser : write data
amqp <.. broker : data update
amqp ..> broker : write data

@enduml

The UserJPA is only used within the users.store component. It will provide a User as specified in the users.client component.

3. Use a locally defined data store

  1. The java library de.paladins-inn.torganized-play:dcis-users-client needs to be added to the dependency list of the client.

  2. The user has to implement the special handler for the data that needs to be stored. In this example it’s the UserEventsHandler interface. The class has to be annotated with @Service and the library dcis-store-users must not be included.

Alternative with a non-standard data store on the client side.
@startuml
left to right direction
skinparam actorStyle awesome
skinparam nodesep 50
skinparam ranksep 200

component scsClient as Client {
  component "client-code" as scsClientCode {
    component code

    package local.store << store >> {
      component storeWriter as data.writer
      component storeReader as data.reader
    }
  }

  rectangle << generic >> {
    package lib.messaging << generic >> {
      component EventBus as bus
    }

    package lib.rest << generic >> {
    }

    package lib.ui << generic >> {
    }
  }

  package users.client << client >> {
    component messaging as amqp
  }

}

together {
  component "Messaging Broker" as broker << External >>
  component "dcis-users" as scsUser << External >>
}

amqp --> bus : data update
data.writer <-- bus : date update

code --> lib.rest : use
code --> lib.ui : use
code --> data.reader : reading data
data.writer --> bus : writing data

bus --> amqp : writing data

broker <.up. scsUser : data update
broker .up.> scsUser : writing data
amqp <.. broker : data update
amqp ..> broker : writing data

@enduml