flow-parent

dataflow orchestration library

License

License

GroupId

GroupId

com.github.warmuuh
ArtifactId

ArtifactId

flow-parent
Last Version

Last Version

1.1.0
Release Date

Release Date

Type

Type

pom
Description

Description

flow-parent
dataflow orchestration library
Project URL

Project URL

https://github.com/warmuuh/flow
Source Code Management

Source Code Management

https://github.com/warmuuh/flow

Download flow-parent

Filename Size
flow-parent-1.1.0.pom 5 KB
Browse

How to add to project

<!-- https://jarcasting.com/artifacts/com.github.warmuuh/flow-parent/ -->
<dependency>
    <groupId>com.github.warmuuh</groupId>
    <artifactId>flow-parent</artifactId>
    <version>1.1.0</version>
    <type>pom</type>
</dependency>
// https://jarcasting.com/artifacts/com.github.warmuuh/flow-parent/
implementation 'com.github.warmuuh:flow-parent:1.1.0'
// https://jarcasting.com/artifacts/com.github.warmuuh/flow-parent/
implementation ("com.github.warmuuh:flow-parent:1.1.0")
'com.github.warmuuh:flow-parent:pom:1.1.0'
<dependency org="com.github.warmuuh" name="flow-parent" rev="1.1.0">
  <artifact name="flow-parent" type="pom" />
</dependency>
@Grapes(
@Grab(group='com.github.warmuuh', module='flow-parent', version='1.1.0')
)
libraryDependencies += "com.github.warmuuh" % "flow-parent" % "1.1.0"
[com.github.warmuuh/flow-parent "1.1.0"]

Dependencies

compile (1)

Group / Artifact Type Version
org.slf4j : slf4j-api jar 1.7.26

provided (1)

Group / Artifact Type Version
org.projectlombok : lombok jar 1.18.6

test (5)

Group / Artifact Type Version
org.junit.jupiter : junit-jupiter-api jar 5.4.0
org.junit.jupiter : junit-jupiter-engine jar 5.4.0
org.assertj : assertj-core jar 3.11.1
org.mockito : mockito-all jar 1.10.19
org.slf4j : slf4j-simple jar 1.7.26

Project Modules

  • flow

Flow Build Status Maven Central

Dataflow orchestration library. Stop worrying about how to execute your code or how to stitch together your futures. Just define which data-types you need and what you produce, the rest will be figured out by flow and you can concentrate fully on your business logic.

Changelog:

  • 1.0.0 - initial release
  • 1.1.0
    • async execution engine using CompletableFutures
    • supporting generic dependencies (e.g. List<String>)

Installation

<dependency>
	<groupId>com.github.warmuuh</groupId>
	<artifactId>flow</artifactId>
	<version>...</version>
</dependency>

Usage

Flow allows you to only define the input and output of your business logic. Depending on the required and provided dependencies, flow figures out what can be executed in parallel and what has to wait for other providers to finish.

To setup flow and be ready to use it, you need to initialize it with the used contract as well as the used execution engine:

Included in flow is a SequentialExecutionEngine, which executes each necessary provider in a blocking order. Another provided execution engine is the RxJavaExecutionEngine which uses RxJava to execute all steps in parallel and stitches the results together so that the execution happens as parallel as possible.

Example for Sequential Execution:

var flow = new Flow<>(new AnnotationContract(), new SequentialExecutionEngine<>());
flow.registerProviders(...);
var plan = flow.planExecution(new TypeRef(...), new TypeRef(InputObject.class));
ObjectRef result = flow.executePlan(plan, new InputObject());

Example for parallel execution using RxJava Execution-engine:

var flow = new Flow<>(new AnnotationContract(), new RxJavaExecutionEngine<>());
flow.registerProviders(...);
var plan = flow.planExecution(new TypeRef(...), new TypeRef(InputObject.class));
Single<ObjectRef> result = flow.executePlan(plan, new InputObject());

As flow is very flexible, you can easily create your own contract for defining providers. A predefined contract is the AnnotationContract.

AnnotationContract

To define your provider, you can use the @Flower annotation:

public static class ExampleClass {
		@Flower
		public ProvidedType execute(RequiredType object) {
			...
		}
	}

Generics

Parameterized types are normally removed due to type erasure. Only in certain cases are the types kept. Thats why to support generic types, a slightly different syntax is necessary:

var plan = flow.planExecution(new GenericTypeRef<List<Double>>() {}, new GenericTypeRef<List<String>>() {});
List<String> strings = asList("0", "1", "2");
ObjectRef result = flow.executePlan(plan, new GenericObjectRef<List<String>>(strings) {});

More Details

  • The execution plans are used to encode the steps that are necessary to provide the queried response. These plans are immutable and there is no need to recalculate those plans every time, they can be cached. Also this functionallity can be used in unit tests to verify that the registered providers are able to provide the required dependencies.
  • This framework can be easily integrated into e.g. Spring, just register all your necessary beans that contain providers into flow.

Note on Dataflow centric development

  • If you are used to imperative development, this will be slightly strange, as you have to encode all your logic into types. If there are several outcomes of your providers (e.g. error vs fetched result), you have to take care that the returned type is able to represent both states.
  • using dataflow centric development heavily improves your testability. (clearly defined responsibility, clear dependencies, all input/output is defined via types)
  • providers work on their own. They dont actively "fetch" data except, if this is there only task
  • providers stick to SRP (single responsibility principle), they should only do one thing. This leads to easy testability.

Versions

Version
1.1.0
1.0.0