Create your Workflow in a few minutes for Free !

1. Overview

Connecting microservices within a microservices application has always been a difficult job. Indeed, event-driven architecture tools require experienced developers. Moreover, these tools require complex code full of configuration.

1.1. What’s IOEvent ?

IOEvent is essentially a new framework for choreographing microservices. It aims to help microservices applications to link the microservices that make up the complete application with simple and easy lines of code without going further into the configuration of the "brorker" technology and without using an orchestrator like the central workflow engines.

This Framework ensures the transfer of information and communication asynchronously without waiting for a response or worrying about what happens next. Each service observes its environment. Developers will be able to connect its microservices in a short time ensuring the performance of its application thanks to the fast transfer of information between microservices, in addition allowing it to set up a monitoring of execution by process and a "dashboard" of monitoring of execution in BPM.

1.2. IOEvent Features

Before focusing on the details of what the framework provides in terms of functionality, we will list some of the goals that our framework will achieve :

1 Linking Microservices: As a developer, I need to create links between microservices using simple code that defines the source and target of each event by attaching it to any object type.
2 Simple configurations: As a framework user, I need to have a default framework configuration for the technologies used by the framework, as well as simple configuration options to customize the configuration of my application.
3 execution tracking: As a user, I need to track my process execution information (number of instances, time spent per instance in a microservice).
4 Process supervising: as a user, I need a dashboard to display the process diagram created by the microservices with the link created between the microservices and display the current instances in each microservice with all the information about them.

1.3. IOEvent Release Guide

This section summarizes IOEvent release versions, Each version of IOEvent is bound to a specific version of Java, Spring Boot and Spring Cloud version. Only these default combinations are recommended and supported by IOEvent.

Table 1. Release compatibility
IOEvent starter Version Java Version Spring Boot Version Spring Cloud Version

1.x

11

2.4.x

2020.0.3

2.5.x

2.6.x

2021.0.3

2.7.x

2.x

17

3.0.x

2.2.x

17

3.1.x

2022.0.4

2. Getting Started

If you are getting started with IOEvent, start by reading this section. It answers the basic what?, how? and why? questions.

2.1. Quick start for IOEvent

IOEvent starter dependency is available in Maven Central . You can start using IOEvent by following this steps :

2.1.1. Step 0 : System Requirements

IOEvent is developed by Spring Boot 3.1 and above, it requires Java 17 or above and Apache Kafka 2.8 or above .

2.1.2. Step 1 : Start with Kafka

To start an Apache Kafka server, you can use this docker-compose.yml file :

   version: '3.3'

volumes:
  zookeeper-data:
    driver: local
  zookeeper-log:
    driver: local
  kafka-data:
    driver: local

services:

  zookeeper:
    restart: always
    image: confluentinc/cp-zookeeper
    volumes:
      - zookeeper-data:/var/lib/zookeeper/data:Z
      - zookeeper-log:/var/lib/zookeeper/log:Z
    environment:
      ZOOKEEPER_CLIENT_PORT: '2181'
      ZOOKEEPER_ADMIN_ENABLE_SERVER: 'false'

  kafka:
    restart: always
    image: confluentinc/cp-kafka:6.2.1
    container_name: kafka
    volumes:
      - kafka-data:/var/lib/kafka:Z
    ports:
      - "9092:9092"
      - "29092:29092"
    environment:
      KAFKA_BROKER_ID: '0'
      KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
      KAFKA_NUM_PARTITIONS: '12'
      KAFKA_COMPRESSION_TYPE: 'gzip'
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: '1'
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: '1'
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: '1'
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
      KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE: 'false'
      KAFKA_JMX_PORT: '9091'
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
      KAFKA_AUTHORIZER_CLASS_NAME: 'kafka.security.auth.SimpleAclAuthorizer'
      KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: 'true'
    links:
      - zookeeper

Let’s start the Kafka server by spinning up the containers using the docker-compose command :

$ docker-compose up -d

2.1.3. Step 2 : Add IOEvent Dependency

To add IOEvent dependency, edit your pom.xml and add the ioevent-spring-boot-starter :

<dependency>
			<groupId>io.ioevent</groupId>
			<artifactId>ioevent-spring-boot-starter</artifactId>
			<version>${last-version}</version>
</dependency>

2.1.4. Step 3 : Add IOEvent Property

To import IOEvent properties, you can add the following to your application.properties or application.yaml file:

spring:
  application:
    name: MyAppName   (1)
  kafka:
    bootstrap-servers: localhost:29092  (2)
ioevent: (3)
    prefix: MyPrefix
    group_id: MyGroupID
1 Application name : Required
2 Provides the initial hosts that act as the starting point for a Kafka client to discover the full set of alive servers in the cluster : Required
3 IOEvent properties which have a default values in the project. For more details see the Developers Tools section below.

2.1.5. Step 4 : Add @EnableIOEvent

Add @EnableIOEvent annotation to the "application class", it will enable IOEventConfiguration by loading all the beans of IOEventConfiguration class.

@SpringBootApplication
@EnableIOEvent
public class MyApplication {

        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
    }

}

3. Developers Tools

IOEvent includes an additional set of properties that can make the application development experience a little more pleasant.

The following table lists all the properties that are applied :

Table 2. Properties
Name Type Default Description

ioevent.topic_names

List

null

A list of topic names that the user would like to create.

ioevent.prefix

String

"IOEvent-"

While topics creation the prefix will be added to all the topics declared in IOEvent annotations.

ioevent.group_id

String

"ioevent"

A unique string that identifies the consumer group where all IOEvent consumers belongs to.

ioevent.auto_create_topic

Boolean

true

Allow automatic topic creation on the broker when the topic is declared in IOEvent annotations only if the broker allows for it.

ioevent.topic_replication

String

"1"

Replication Factor used while creating topics, to specify the Kafka Replication Factor for the deciding the number of replicas you want.

ioevent.api_key

String

" "

API key is a unique identifier used to authenticate projects and allow to share them between IOEvent Cockpit users who owns the API key.

ioevent.topic_partition

int

1

Specifies the number of partitions to create the topic with.

ioevent.auto.offset.reset

String

"earliest"

Specifies the auto offset reset behavior in Kafka consumer. Possible values include

ioevent.consumers_per_topic

int

(number of partitions /2) + 1

Specifies the number of consumers per topic in the application.

4. IOEvent Annotations

As an event driven framework IOEvent has provided several annotation to manipulate event and establish communication between microservices . in this section we will list all ioevent annotations and explain each annotation .

4.1. @EnableIOEvent

Starting with @EnableIOEvent Annotation , IOEvent needs to define extra configuration to the application , a single @EnableIOEvent annotation used on the "application class" will enable IOEventConfiguration by loading all the beans of IOEventConfiguration class

@SpringBootApplication
@EnableIOEvent
public class MyApplication {

        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
    }

}

.

4.2. @IOFlow

@IOFlow annotation uses to specify the classes that contains @IOEvent methods , @IOFlow classes are processed by BeanPostProcessors to extract information from @IOEvent method , in @IOFlow we can specify the key or the name of the flow where the class methods are part of ,name of generic topic where the @IOEvent methods will send event if the topic wasn’t specified in the @IOEvent annotation and apiKey which will be attached to the events of the class methods

@Service
@IOFlow(name = "Flow_Name")
public class MyService {

	//IOEvent-Methods

}

4.3. @IOEvent

Annotation that marks a method of IOEvent, in @IOEvent you can specify the key or the name of the task, name of generic topic where the @IOEvent methods will receive and send an event if the topic wasn’t specified in the @InputEvent and @OutputEvent annotation, you also specify input as list of @InputEvent from where the annotation will receive events and output as list of @OutputEvent where the annotation will send events, you can define the method as Gateway using @GatewayOutputEvent and @GatewayInputEvent, it’s also possible to include task type (in case it’s a task) by passing the parameter EventType which takes an EventTypesEnum. The parameter textAnnotation takes a String and attaches it to a bpmn object in the diagram. finally you can declare start/end method explicitly using @StartEvent and @EndEvent or declare start/end method implicitly if you don’t mention any input/output.

@IOEvent(key = "IOEvent Task",
			input = @InputEvent(key = "inputEvent", topic = "topicName"),
			output = @OutputEvent(key = "outputEvent", topic = "topicName"),
			EventType = EventTypesEnum.USER,
			textAnnotation = "text annotation")
	public Object initMainFlow(Object o) {
		//TO-Do
		return o;
	}

4.4. @OutputEvent

@OutputEvent annotation is used to produce an event which includes a key of the output and a topic where the event will be produced ( if the topic is not mentioned the event will be sent to the generic topic specified in the @IOEvent or @IFlow annotation ).

@OutputEvent(key = "Output Key", topic = "OutputTopic")

4.5. @InputEvent

@InputEvent create a Listener which receive events from the topic ( if the topic is not mentioned it will listen to the generic topic specified in the @IOEvent or @IFlow annotation ), and while the listener consumes an event it will verify if the output key of the received event is equal to the @InputEvent key in order to invoke the specific method.

@InputEvent(key = "Input Key", topic = "InputTopic")

4.6. Gateway annotations

Gateways determine what path is taken through a process that controls the flow of both diverging and converging Sequence Flows. That is, a single Gateway could have multiple inputs and multiple output flows.

Gateways can be divided into two types, the Exclusive Gateway and the Parallel Gateway.

4.6.1. Exclusive Gateway

An exclusive gateway evaluates the state of the business process and, based on the returned IOResponse key, it breaks the flow into one of the two or more mutually exclusive paths.

It is based on the @GatewayOutputEvent annotation where we set the value of exclusive to true and define the list of @OutputEvent where the method will produce the event to the output with the same key of the IOResponse output key.

In IOResponse we specify the output key and the body to be send to the event.

@IOEvent(key = "Exclusive Gateway Task", topic = "topicName", //
		input = @InputEvent(key = "Input_Key"), //
		gatewayOutput = @GatewayOutputEvent(exclusive = true, output = { //
				@OutputEvent(key = "Output_A"), //
				@OutputEvent(key = "Output_B")//
		}))
public IOResponse<Object> exclusiveGatewayMethod (Object body) {

	if (conditionMethod(body)) {
		return new IOResponse<Object>("Output_A", body);
	}
	return new IOResponse<Object>("Output_B", body);

}

4.6.2. Parallel Gateway

Parallel gateways are used to represent two tasks in a business flow. It models a fork into multiple paths of execution, or a join of multiple incoming paths of execution.

It is based on the @GatewayOutputEvent and @GatewayInputEvent annotation where we set the value of parallel to true and define the list of output branches @OutputEvent where to produce the event simultaneously.

@IOEvent(key = "Parallel-Gateway-Output-Task", topic = "topicName",
			input = @InputEvent(key = "Input_Key"),
			gatewayOutput  = @GatewayOutputEvent(parallel = true, output = {
					@OutputEvent(key = "Output_A"), //
					@OutputEvent(key = "Output_B")//
			}))
	public Object gatewayParallel(Object body) {
		//TO-Do
		return body;
	}

By converging parallel branches, they wait to receive all input branches @InputEvent to execute the parallel method and send the event to the @OutputEvent.

@IOEvent(key = "Parallel-Gateway-Input-Task", topic = "topicName",
			gatewayInput = @GatewayInputEvent(parallel = true, input = {
			@InputEvent(key = "Input_A"),
			@InputEvent(key = "Input_B") }),
		output = @OutputEvent(key = "Output_Key"))
	public Object gatewayParallel(Object body) {
		//TO-Do
		return body;
	}

4.7. @StartEvent

@StartEvent annotation define the starting point of a process which includes a key where we specify the name of the flow.

startEvent = @StartEvent(key = "Process name")

4.8. @EndEvent

@EndEvent annotation define the finishing point of a process which includes a key where we specify the name of the flow.

endEvent = @EndEvent(key = "Process name")

4.9. @IOHeaders

@IOHeaders annotation indicates that a method parameter should be bound to the headers of the event. The annotated parameter must be assignable to Map with String keys and Object values.

@IOEvent(key = "Task", topic = "topicName",
	 input = @InputEvent(key = "Input_Key"),
	 output = @OutputEvent(key = "Output_Key"))

public Object ioeventMethod (@IOPayload Object body,@IOHeaders Map<String, Object> headers) {
			//TO-Do
		return body;

	}

4.10. @IOPayload

@IOPayload annotation binds a method parameter to the payload of a the received event. It can be used also in the parallel input gateway to bind between method parameters and inputs payloads using input index.

@IOEvent(key = "JOIN TASK", //
            gatewayInput = @GatewayInputEvent(parallel = true, input = { //
                    @InputEvent(key = "Input_A", topic = "topicA"), //
                    @InputEvent(key = "Input_B", topic = "topicB") }), //
            output = @OutputEvent(key = "Output name", topic = "topicD"))
    public Object ioeventMethod(@IOPayload(index = 0) Object bodyA,
    @IOPayload(index = 1) Object bodyB) {
		//TO-Do
		}

4.11. Exception annotations

In any process, exceptions to the expected flow of execution occur, and developers need to be able to write robust application logic to recover from those exceptions. IOEvent provides the tools to define the desired reaction to those exceptions. Exception handling can be one of two ways, Handle the error or End the workflow with an error.

4.11.1. Handle Error

IOEvent offers support for handling error situations in a way that is clearly divisible from the normal flow of tasks. A typical way to take advantage of it would be to incorporate into your model a boundary event on an element in which something can go wrong. To handle an expected exception in a task we are going to update the IOEvent annotation, Firstly we are going to add the exception details with the @ExceptionEvent. The exception event have two fields the first one is the list of exceptions predicted to be occurred in the task, the second is the output which is an @OutputEvent where the payload would be sent to the handling method associated to the output.

@IOEvent(
		key = "Make Discount Event",
		topic = "discount",
		input = @InputEvent("EventCreated"),
		output = @OutputEvent("EventDiscounted"),
		exception= @ExceptionEvent(
               exception = {ArithmeticException.class},
               output=@OutputEvent(
               	key = "DiscountError",
               	topic="DLQ_exception")
           )
)
public Double makeDiscount( Double price) throws Exception {
	//Potential arithmetic exception can be caused
}

The handling method is a task method that takes the payload and perform a certain business or error handling logic to recover and continue the flow .

@IOEvent(key = "Handle Error",
			topic = "discount",
			input = @InputEvent(
				"DiscountError",
				topic="DLQ_exception"),
			output = @OutputEvent("HandledError"))
	public Double handleError(Double price) throws InterruptedException {
		//Handling the arithmetic exception
	    return price;
	}

4.11.2. End with error

We can choose to end the flow with an error in this case we use the second version of our annotation. Inside the @ExceptionEvent we specify the list of exceptions predicted to be thrown by the task and we finally we add the @EndEvent which define the end of the flow with an end error.

@IOEvent(
		key = "Make Discount Event",
		topic = "discount",
		input = @InputEvent("EventCreated"),
		output = @OutputEvent("EventDiscounted"),
		exception= @ExceptionEvent(
               exception = {DataFormatException.class},
			 	endEvent=@EndEvent("End with error")
			 	)
)
public Double makeDiscount( Double price) throws Exception {
	//Potential error can be caused
}

4.12. Timer Annotations

We may need to schedule, to delay or to include a defined timer that trigger a subprocess or the workflow itself .

4.12.1. Start Timer

IOEvent provides the start timer annotation which consist of triggering the start of a process according to a specific time date or a time cycle definition. Once the timer is triggered, a new process instance is created, and the corresponding timer start event is activated. To implement this using IOEvent framework we have to add the @IOTimer annotation to the @StartEvent with the specific time definition.

@IOEvent(
		key="Start cleaning every 5 min",
		startEvent = @StartEvent(
				key="Periodic cleaning",
				timer=@IOTimer(cron="* */5 * * * *")
				),
		output = @OutputEvent(key = "order to clean"))
public void scan() {
	//scan process
}

4.12.2. Intermediate Timer

An intermediate catch event requires a time duration to determine its triggering time. Upon entering an intermediate catch event, a timer is scheduled. At this point, the process instance pauses and remains in a waiting state until the timer is triggered. Once the timer is triggered, the catch event is considered completed, and the process instance resumes its execution. and to implement this feature we add the @IOTimer annotation into the _@IOEvent annotation and we set the delay time with the appropriate time unit.

@IOEvent(key="Coffe Break",
		input=@InputEvent(key="take a break"),
		output=@OutputEvent(key="back to work"),
		timer=@IOTimer(delay=5, timeUnit = TimeUnit.MINUTES))
public void takeBreak() {
	//delay
}

4.13. @IOTimer

Timer events are events triggered by a defined timer.

4.13.1. Timer start events

A process can have one or more timer start events (besides other types of start events). Each of the timer events must have either a time date or time cycle definition.
When a process is deployed, it schedules a timer for each timer start event.
When a timer is triggered, a new process instance is created.

@IOEvent(
           key="Timer start event",
           startEvent = @StartEvent(
                   key="start event",
                   timer=@IOTimer(cron="*/2 * * * * *")
                   ),
           output = @OutputEvent(key = "output"))
   public Object methodToExecute() {
       //TODo
   }

4.13.2. Intermediate timer events

An intermediate timer catch event must have a time duration definition that defines when it is triggered.
When an intermediate timer event is entered, a corresponding timer is scheduled. The process instance stops at this point and waits until the timer is triggered. When the timer is triggered, the catch event is completed and the process instance continues.

@IOEvent(key = "intermediate timer",
		input = @InputEvent("input"),
		output = @OutputEvent("output"),
           timer = @IOTimer(delay = 1,timeUnit = TimeUnit.MINUTES ))
   public object methodToExecute() {
       //toDo
   }

4.14. User Task annotations

A User Task is a typical “workflow" Task where a human performer performs the Task with the assistance of a software application and is scheduled through a task list manager of some sort.

4.14.1. User Task

In IOEvent, currently, we only support user tasks that come after a certain task (we do not support, at least for now, user tasks that come as the first step of the process). To implement a user task, we need to mark the output going to the user task in the task that precedes it with the userActionRequired parameter set to true. Then, we have to add the user task with the EventType parameter set to EventTypesEnum.USER. The method representing the user task could take as an argument the payload that comes from the response of the human performer.

After this implementation, we can either use the IOEvent Cockpit to manage the user tasks (retrieve the list of tasks and send responses) or embed the IOEvent-userTask-starter (which is a starter dedicated to handling user tasks and offering specific endpoints) in a specific application that will manage the user tasks. For more information about this case, refer to the IOEvent-userTask-starter documentation.

@IOEvent(key = "step before user task",
		input = @InputEvent("input"),
		output = @OutputEvent("output to user task",
		userActionRequired = true)
           )
   public object stepBeforeUserTask() {
       //toDo
   }

@IOEvent(key = "user task",
		input = @InputEvent("output to user task"),
		output = @OutputEvent("output"),EventType = EventTypesEnum.USER
           )
   public object userTask(Object humanResponse) {
       //toDo
   }

5. IOEvent Method

In this section we will explain the structure of IOEvent method :

@Service
@IOFlow(name = "FLow Name" , topic = "topicName") (1)
public class MyService {
	@IOEvent (key = "Task Key", topic = "topicName",
			input = @InputEvent(key = "INPUT_EVENT",topic = "topicName"),
		   output  = { @OutputEvent(key = "OUTPUT_EVENT")
			}) (2)
	public MyObject ioeventMethod(MyObject body) (3)
	{
		// To-Do
		return body;
	}
1 @IOFlow annotation is Required and to be specified on the service class that contain IOEvent methods.
2 @IOEvent annotation must be declared above IOEvent method.
3 IOEvent Method must respect 2 rules :
  • Rule 1: For the method parameter we can use any type of object that provide no argument constructer.

  • Rule 2: For the return object type, if it’s a void the method will send the received parameter into the output event as a payload, else it will send the return object specified inside the method as an output event payload.

6. IOEvent Cockpit

In this section we will present the interfaces of IOEvent-Cockpit and explain the features offered by each view.

6.1. Getting started

cockpit-api and cockpit-ui are mandatory images to use IOEvent Cockpit, so in order to run them you should :

6.1.1. System requirement

IOEvent Cockpit api is supporting Elasticsearch version 7, we are currently working on the latest versions .

6.1.2. IOEvent cockpit-api

  • Pull cockpit-api image using a specific tag :

$ docker pull ioevent/cockpit-api:tag
  • Run IOEvent Cockpit-api image :

Replace the variable between {} with your personal value and run this command :

$ docker run -e ES_URL=https://{ElasticsearchHost:Port} -e KAFKA_BOOTSTRAP_SERVER={KafkaHost:Port} -p 8761:8761 ioevent/cockpit-api
  • Run IOEvent Cockpit-api with docker compose :

version: '3.3'

services:
  ioeventapi:
        image: ioevent/cockpit-api:tag
        ports:
           - 8761:8761
        environment:
          ES_URL: https://ElasticsearchHost:Port
          KAFKA_BOOTSTRAP_SERVER: KafkaHost:Port
Table 3. Environment variables
Available variables Default value Description

KAFKA_BOOTSTRAP_SERVER

no default

host/port pairs to use for establishing the initial connection to the Kafka cluster.

KAFKA_GROUP_ID

"ioevent-cockpit-api"

kafka Group id for the cockpit-api.

KAFKA_SASL_JAAS_USERNAME

""

sasl jaas username to connect to kafka cluster with authentication.

KAFKA_SASL_JAAS_PASSWORD

""

sasl jaas username to connect to kafka cluster with authentication.

ES_URL

no default

url to establish connection to Elasticsearch.

ES_USERNAME

""

username of Elasticsearch if exist.

ES_PASS

""

password of Elasticsearch if exist.

ENABLE_MAIL_SERVICE

false

enable the mail service on the cockpit-api.

DEFAULT_ENABLE_USERS

false

default enable users created by the cockpit-api.

IOEVENT_INSTANCE_DOWN_TIMEOUT

7000

time before the cockpit-api set the state of ioevent instance to DOWN.

IOEVENT_APP_DOWN_TIMEOUT

15000

time before the cockpit-api set the state of ioevent app to DOWN.

IOEVENT_APP_DELETE_TIMEOUT

20000

time before the cockpit-api delete the ioevent app and untrack it.

6.1.3. IOEvent cockpit-ui

  • Pull cockpit-ui image using a specific tag :

$ docker pull ioevent/cockpit-ui:tag
  • Run IOEvent Cockpit-UI image :

$ docker run -p 3000:3000 ioevent/cockpit-ui:tag

with the previous command the cockpit-ui will run and connect to the default cockpit-api host:port (localhost:8761) if you want to change it you can specify it in the environment variable as below :

docker run -e COCKPit_API_URL="SPECIFY YOU COCKPIT API URL" -p 3000:3000 ioevent/cockpit-ui:tag
  • Run IOEvent Cockpit-UI with docker compose :

version: '3.3'

services:
  ioeventui:
   	image: ioevent/cockpit-ui:tag
   	ports:
    	 - '3000:3000'
Table 4. Environment variables
Available variables Default value Description

COCKPit_API_URL

localhost:8761

URL of cockpit-api application to establish connection and from where the cockpit-ui will consume Api(s).

COCKPit_UI_REGISTRATION

false

variable to enable or disable cockpit-ui registration feature.

6.2. Sign-In

IOEvent provide a login page where the user must enter his email and password to access the application.

auth

6.3. Default User Credentials

After starting the IOEvent cockpit-api and cockpit-UI, you can log in to IOEvent Cockpit using the credentials of the user created by default during the startup. You can use the following credentials:

Username: io-admin

Password: admin

6.4. General interface

Once the user has successfully logged in, the General interface is displayed, he will find a list of his applications connected to Cockpit, and a graph that displays the number of active and completed instances as shown in figure.

img\general

6.5. IOService interface

when the user Click on IOServices on the left navigation bar it displays a list of the user services connected to Cockpit that contains the name of service , number of active instances and the status of the service , by selecting one of the services it display the informations of its instances (instance ID , Host and Port) .

img\ioservice

6.6. IOFlows interface

By choosing the IOFlows section It shows all the IOFlow (processes) that the user has access to, it represents :

  • Name : name of the IOflow.

  • Active instances : number of active instances inside the process.

  • Ended instances : number of instances that finished the process.

  • Total instances : number of total instances.

  • Action : allows to share the ioflow with other users.

img\ioflows

6.6.1. IOFlow Details interface

when choosing one of the processes it will display more details about the process.

Diagram Tab

Displays the diagram generated by Cockpit after collecting the process steps from the applications. It also gives you a real-time view of processes and instances as they run, so you can monitor their status and quickly identify technical incidents that slow down or stop the process.

img\diagram

By clicking on one of the diagram tasks it opens the property view that contains :

  • Task Info section :

    • Task Name : the name of the selected task.

    • IO Event Type : the type of the task.

    • Input : list of inputs.

    • Output : list of outputs.

    • Method Qualified Name : the full method qualified name of the task in java application.

img\taskinfo
  • Task Service section : Displays the name of service with the current service state , also it lists informations (instance ID , Host and Port) about active instances.

img\taskservice
  • Current instances section : Displays the current instances that are running on a specific task, it represents :

    • Instance Id : ID of the instance.

    • Start Instance Date : the time when the instance has started.

    • Arrived Date : the time when the instance has arrived at the current task.

    • Duration (ms) : the duration that takes the instance to arrive at the specific task.

img\currentinstance
  • Advanced Instance Info section : When choosing one of the current tasks it display the Advanced Instance Info.

img\advinstanceinfo
Tasks Tab

Displays all the steps or tasks that compose the process together. which include the service name responsible for executing the tasks also you can find the actual state of the service , by selecting one of the services it display the informations of its instances (instance ID , Host and Port) .

img\tasktab
Instances Tab

Lists the instances executed on this process, it represents :

  • Instance Id : the instance Id.

  • Process Name : name of the process.

  • Start Time : time when the instance has started.

  • End Time : time when the instance has finished (void if the instance still running).

  • Instance Duration (ms) : duration of the instance in milliseconds.

  • Status : state of instance (Active/Finished).

  • Payload : current payload of the instance.

img\intanceslist

Instance Details : for each instance, we have the instance details to present the list of step history, through which the instance went, it represents :

  • Event Type : to specify the Task type (Start - End - Task - Parallel Gateway -Exclusive Gateway ).

  • Task Name : the name of the task .

  • Input Event(s) : list of input events.

  • Output Event(s) : list of output events .

  • Start Time : the time when the Task has started .

  • End Time : the time when the Task has finished .

  • Duration (ms) : the duration of the task in milliseconds.

img\instanceinfo

7. IOEvent-userTask-starter

This section provides an overview of the ioevent-userTask-starter, and explains what is the purpose of it and how to use it.

7.1. The purpose of ioevent-userTask-starter

At working with IOEvent, you may need to manage user tasks, which are tasks that require human intervention, but in some cases you may need to manage them in a separate application and dont use the IOEvent Cockpit and this is where the ioevent-userTask-starter comes in.

This component can be added as a dependency to an application and given the appropriate configuration which is really simple, it will provide a set of endpoints that can be used to manage user tasks.

7.2. Getting Started

IOEvent-userTask-starter dependency is available in Maven Central . You can start using IOEvent by following this steps :

7.2.1. Step 0: System requirement

IOEvent-userTask-starter is developed by Spring Boot 3.1 and above, it requires Java 17 or above and Apache Kafka 2.8 or above and elasticsearch 8.x and above.

7.2.2. Step 1: Setup environment

When using ioevent-userTask-starter, you can add Elasticsearch to the same docker-compose used with IOEvent. Here is how it will look:

   version: '3.3'

volumes:
  zookeeper-data:
    driver: local
  zookeeper-log:
    driver: local
  kafka-data:
    driver: local

services:

  zookeeper:
    restart: always
    image: confluentinc/cp-zookeeper
    volumes:
      - zookeeper-data:/var/lib/zookeeper/data:Z
      - zookeeper-log:/var/lib/zookeeper/log:Z
    environment:
      ZOOKEEPER_CLIENT_PORT: '2181'
      ZOOKEEPER_ADMIN_ENABLE_SERVER: 'false'

  kafka:
    restart: always
    image: confluentinc/cp-kafka
    container_name: kafka
    volumes:
      - kafka-data:/var/lib/kafka:Z
    ports:
      - "9092:9092"
      - "29092:29092"
    environment:
      KAFKA_BROKER_ID: '0'
      KAFKA_ZOOKEEPER_CONNECT: 'zookeeper:2181'
      KAFKA_NUM_PARTITIONS: '12'
      KAFKA_COMPRESSION_TYPE: 'gzip'
      KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: '1'
      KAFKA_TRANSACTION_STATE_LOG_REPLICATION_FACTOR: '1'
      KAFKA_TRANSACTION_STATE_LOG_MIN_ISR: '1'
      KAFKA_LISTENER_SECURITY_PROTOCOL_MAP: PLAINTEXT:PLAINTEXT,PLAINTEXT_HOST:PLAINTEXT
      KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092,PLAINTEXT_HOST://localhost:29092
      KAFKA_CONFLUENT_SUPPORT_METRICS_ENABLE: 'false'
      KAFKA_JMX_PORT: '9091'
      KAFKA_AUTO_CREATE_TOPICS_ENABLE: 'true'
      KAFKA_AUTHORIZER_CLASS_NAME: 'kafka.security.auth.SimpleAclAuthorizer'
      KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND: 'true'
    links:
      - zookeeper

  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:8.7.1
    container_name: elasticsearch
    restart: unless-stopped
    ports:
      - "9200:9200"
      - "9300:9300"
    environment:
      cluster.name: docker-es-cluster
      discovery.type: single-node
      bootstrap.memory_lock: "true"
      xpack.security.enabled: "false"
      xpack.security.enrollment.enabled: "false"

7.2.3. Step 2: Add dependency

To add ioevent-userTask-starter dependency, edit your pom.xml and add :

<dependency>
    <groupId>io.ioevent</groupId>
    <artifactId>ioevent-userTask-starter</artifactId>
    <version>${last-version}</version>
</dependency>

7.2.4. Step 3: Add necessary properties

To use the ioevent-userTask-starter you need to add the adequat properties, you can add the following to your application.properties or application.yaml file:

spring:
  application:
    name: MyAppName   (1)
  kafka:
    bootstrap-servers: localhost:29092  (2)
ioevent: (3)
    application_name: Main_Application_Name (4)
	prefix: MyPrefix
1 Application name : Required
2 Provides the initial hosts that act as the starting point for a Kafka client to discover the full set of alive servers in the cluster : Required
3 IOEvent properties which have a default values in the project except the application_name which is Required. For more details see the Developers Tools section below.

7.2.5. Step 4 : Add @EnableIOEventUserTask

Add @EnableIOEventUserTask annotation to the "application class", it will enable IOEventUserTaskConfiguration by loading all the beans of IOEventUserTaskConfig class.

@SpringBootApplication
@EnableIOEventUserTask
public class MyApplication {

        public static void main(String[] args) {
            SpringApplication.run(MyApplication.class, args);
    }

}

7.3. Developers Tools

To configure the ioevent-userTask-starter, you can use the following properties :

The following table lists all the properties that are applied :

Table 5. Properties
Name Type Default Description

ioevent.application_name

List

null

Name of the main application that contains the workflow.

ioevent.prefix

String

"IOEvent-"

While topics creation the prefix will be added to all the topics declared in IOEvent annotations.

7.4. General Overview

ioevent-userTask-starter provides a set of endpoints that can be used to manage user tasks, we already have swagger documentation for the endpoints, so you can access it by adding /swagger-ui.html to the application URL.

7.4.1. Usage Example

To better understand how will the ioevent-userTask-starter look like in a real application that uses IOEvent we will see the ioevent userTask sample that we have among with other examples in our samples repository .

img\generalOverview

As we can see the application named ioevent-userTask-client will be in charge of managing the interaction with the user and it will expose a set of endpoints that we can see in the following section.

7.4.2. Endpoints

img\swaggerUIUserTask

In the previous image we can see the list of endpoints that are exposed by the ioevent-userTask-starter, in example we got an endpoint that lists all the user tasks and another one that allows the user to send a response and complete the user task.

8. BPMN 2.0

This section provides an overview of the current BPMN 2.0 elements.

  • Tasks

Tasks can model the actual work performed in the process. Various types of tasks are supported, such as :

Service Task : Service tasks are used to invoke services.

img\servicetask

Receive Task : Receive tasks are triggered when certain message is received.

img\receinve

Send Task : Send tasks are triggers for the next workflow object. Its important to mention that send and receive tasks are nothing but symbols that indicate the semantic behind the workflow task. In other word, technically there’s no difference between service, send and receive tasks, its all up to the microservice designer to determine the semantic of the task.

img\SendMessagTask

User Task : User task is triggered when a user, manually indicates the end of the real task (delivery, preparing food, doing paper work..). Cockpit offers a user interface for marking finished user tasks, or u can customise your own interface using our exposed api’s.

img\User

Manual Task : Manual task works exactly like user task, but they are not assigned to a user. IOEvent doesn’t cover the concept of user yet in its core, but one way to do it is to use our exposed api’s (in user task defintion), design your own application with user definition and grant permission for each user to manage his tasks. User task with role management is coming soon in our next release

img\manualTask

Script Task : In BPMN standard a Script Task is executed by a business process engine. The task defines a script that the engine can interpret. When it comes to IOEvent, since our framework is based on event driven design, script task runs on the microservice that the framework user implements. This will avoid bottlenecks and enhance customization options for the user.

img\ScriptTask

Business Rule Task : Business Rule Task is newly added in BPMN 2.0. It provides a mechanism for a process to provide input to a Business Rules Engine and then obtain the output provided by the Business Rules Engine.In IOEvent since it doesn’t have business rules engine in it’s core, it’s the responsability of the framework user to assume if the task’s logic meets this bpmn object semantic.

img\businessRule
  • Gateways

Gateways determine what path is taken through a process that controls the flow of both diverging and converging Sequence Flows it allows modeling decisions based on data and events.

Exclusive Gateway

A diverging Exclusive Gateway (or XOR Gateway) is used to create alternative paths within a Process flow. For a given instance of the Process, only one of the paths can be taken.

img\gatEX

Parallel Gateway

Parallel gateways are used to represent two tasks in a business flow. It models a fork into multiple paths of execution, or a join of multiple incoming paths of execution.

img\gatPA
  • Events

BPMN defines different Event types. The following are supported by IOEvent :

Start Event: defines the starting point of a process.

img\startEv

Conditional Start Event: Defines the starting point of a process if a predefined condition is true.

img\startConditional

End Event: define the finishing point of a process.

img\endEv

Timer Event: Timer event creates an event after a certain configurable period of time passed.

img\timer
img\startTimer

Intermediate Message Throw Event: Throws a message to be intercepted by a catch event, and continues the process.

img\messageThrow

Intermediate Message Catch Event: When the workflow reaches this object, it’s blocked until a predefined message is arrived (thrown by a throw message event). Message events are identified by a message key.

img\messageCatch

Error End Event: used to indicate that a certain process path ends with an error.

img\errend

Error Boundary Event: used to emulate errors thrown by certain tasks.

img\errbound

9. Request a demo environment

You can request a demo environment by sending an email to our support team email : support@ioevent.io and we will provide you with a demo environment to test the IOEvent framework.

The IOEvent demo environment is constructed within our EKS Fargate cluster and includes all the essential components to seamlessly commence using IOEvent without the need for any installations.

We furnish you with the IOEvent Cockpit UI URL and the Kafka bootstrap servers URL, requiring you only to build your IOEvent application by providing the appropriate configuration.

Here is a brief overview of our environment along with the installed dependencies:

img\demoenv

We can provide the user with the IOEvent UI URL and the Kafka bootstrap servers URL. We assume the following:

Kafka Bootstrap Servers URL: k8s-default-987897384793847.elb.eu-central-1.amazonaws.com:9094

So, this is how your application.properties file will look:

spring:
  application:
    name: appName
  kafka:
    bootstrap-servers: k8s-default-987897384793847.elb.eu-central-1.amazonaws.com:9094

10. Install IOEvent environment on your own infrastructure

You have the option to install the necessary dependencies for IOEvent and set up the environment in your own Kubernetes cluster. Alternatively, you can also install it in Minikube. We have the required Helm charts available in our Github repository.

img\githubcontent

10.1. Setup environment on Minikube

Let’s start with ioevent-minikube,

img\ioeventminikube

Within the 'templates', we have the Elasticsearch template where we define the necessary deployment and service and once installed, it will provide an Elasticsearch server that will be utilized by our IOEvent Cockpit.

Next, we have the 'kafka-cluster' template, we opted to employ Strimzi as a Kubernetes operator to manage our Kafka cluster, with plans to consistently upgrade versions in the future. For storage, particularly in a testing and demo environment, we chose 'ephemeral', this means that the data will be stored directly in the containers and will not persist after restarts.

Following that, we have the 'ioeventcockpitapi' template, responsible for initiating the API of our IOEvent Cockpit application. For this service, we have included an ingress to simplify URL management, eliminating the need for any URL changes. The last template is 'ioeventcockpitui,' designed for installing our IOEvent Cockpit UI.

To install this Helm chart, a functional Minikube cluster, Helm, and Kubectl are required. First, you need to install the Strimzi operator tgz using the following command directly under the Minikube directory:

helm dependency update

The subsequent step involves installing our Helm chart:

helm install releasename . -n namespace

You have the flexibility to customize the release name and the namespace according to your preferences.

10.2. Setup environment on Kubernetes cluster

Now, let’s move on to installing the environment on a Kubernetes cluster.

img\githubcontent

elasticsearch, ioeventCockpit-API, ioeventCockpit-UI, and kafka are four seperate Helm charts that are available in our Github repository. Each chart is responsible for installing a specific component of the IOEvent environment.

Each template in this charts is parameterized, allowing you to customize the installation according to your requirements, but we have also included default values for each parameter in the values.yaml file.

For kafka, we have included the Strimzi operator to manage the Kafka cluster, and for storage, we have chosen 'ephemeral' to store the data directly in the containers, which will not persist after restarts, it’s particularly useful in a testing and demo environment but you can change it based on your needs.

To install this Helm chart, a functional Kubernetes cluster, Helm, and Kubectl are required.

Let’s start by the kafka chart, you need to install the Strimzi operator tgz using the following command directly under the kafka chart directory:

helm dependency update

and then you can install the chart using the following command:

helm install releasename ./kafka -n namespace

You have the flexibility to customize the release name and the namespace according to your preferences.

For kafka as you can see in the template, we added an external listener to allow the communication between the Kafka cluster and the outside world. We used LoadBalancer type along with aws annotations you can change it based on your infrastructure.

Our second chart is the elasticsearch chart, it’s responsible for installing the Elasticsearch server that will be utilized by our IOEvent Cockpit, pay attention to the compatibility between the Elasticsearch version and the version of the IOEvent Cockpit, the default values that we have in the values.yaml file are compatible with the latest version of the IOEvent Cockpit.

It can be installed using the following command:

helm install releasename ./elasticsearch -n namespace

Next, we have the ioeventCockpitAPI chart, it’s responsible for initiating the API of our IOEvent Cockpit application, you can install it using the following command:

helm install releasename ./ioeventCockpit-API -n namespace

The last chart is ioeventCockpitUI, it’s designed for installing our IOEvent Cockpit UI, you can install it using the following command:

helm install releasename ./ioeventCockpit-UI -n namespace

Before installing this chart you need to update the ioeventui.apiUrl value in the values.yaml file with the url of the ioeventCockpitAPI service.