Giter Club home page Giter Club logo

dativity's Introduction

dativity

Clojars Project

Dativity is a stateless, data driven process engine library for Clojure and ClojureScript.

It is inspired by the Artifact centric business process model.

Table of Contents

Motivation
Design
Features
Examples
Dependencies
License

Conventional process engines (like Activiti) are centered around activities and the sequence in which they should be performed according to design.

The key idea of Dativity is to not say in what sequence actions in a process should be performed. But rather what actions are possible to do given how actions depend on information.

For example, you cannot accept or deny an insurance claim before it has been submitted - it can be reviewed precisely after it has been submitted.

Process software should not keep its own state. The value the software is providing should be accessed through a pure function of two things: a static process-model, and a process instance consisting of data that evolves throughout the instance of the process as information is gathered.

This blog post offers a more extensive motivation.

Dativity models a process into three different entities:

  • Actions
  • Data
  • Roles

The above entites relate in the following ways:

  • Data (green) can be required by an action
  • An action (purple) produces data
  • A Role (yellow) performs an action

a simple credit application process

In the above example, the action 'create case' produces the data 'case id' and 'customer-id'. When those pieces of information have been added to the case, 'enter-loan-details' can be performed because it only depends on 'case-id' to be present.

Basic functionality

Given a process definition and a set of collected data, Dativity can answer questions like:

  • What actions can be performed next?
  • What actions can be performed by role X (user, system, officer...)
  • What actions have been performed?
  • Is action X allowed?
  • What data is required for action X?

Invalidating

Sometimes a user goes has to go back and change data. Then all subsequent (in the sense that the changed data is required by other actions) actions need to be invalidated. Dativity has support for this type of scenario, where the case is 'rewinded' to the action that was re-done. Previously entered data is kept, but 'uncommitted', and depending actions need to be performed again.

Conditional requirements

Basic functionality

The case data is just a map

(def case {})

Defining an empty case model

(def case-model (dativity.define/empty-case-model))

Add action, data and role entities to the model

(def case-model
  (-> case-model
      ; Actions
      (dativity.define/add-entity-to-model (dativity.define/action :create-case))
      (dativity.define/add-entity-to-model (dativity.define/action :enter-loan-details))
      (dativity.define/add-entity-to-model (dativity.define/action :produce-credit-application-document))
      (dativity.define/add-entity-to-model (dativity.define/action :sign-credit-application-document))
      (dativity.define/add-entity-to-model (dativity.define/action :payout-loan))
      ; Data entities
      (dativity.define/add-entity-to-model (dativity.define/data :case-id))
      (dativity.define/add-entity-to-model (dativity.define/data :customer-id))
      (dativity.define/add-entity-to-model (dativity.define/data :loan-details))
      (dativity.define/add-entity-to-model (dativity.define/data :credit-application-document))
      (dativity.define/add-entity-to-model (dativity.define/data :applicant-signature))
      (dativity.define/add-entity-to-model (dativity.define/data :officer-signature))
      (dativity.define/add-entity-to-model (dativity.define/data :loan-number))
      ; Roles
      (dativity.define/add-entity-to-model (dativity.define/role :applicant))
      (dativity.define/add-entity-to-model (dativity.define/role :system))
      (dativity.define/add-entity-to-model (dativity.define/role :officer))
      ; Production edges
      (dativity.define/add-relationship-to-model (dativity.define/action-produces :create-case :customer-id))
      (dativity.define/add-relationship-to-model (dativity.define/action-produces :create-case :case-id))
      (dativity.define/add-relationship-to-model (dativity.define/action-produces :enter-loan-details :loan-details))
      (dativity.define/add-relationship-to-model (dativity.define/action-produces :produce-credit-application-document :credit-application-document))
      (dativity.define/add-relationship-to-model (dativity.define/action-produces :sign-credit-application-document :applicant-signature))
      (dativity.define/add-relationship-to-model (dativity.define/action-produces :sign-credit-application-document :officer-signature))
      (dativity.define/add-relationship-to-model (dativity.define/action-produces :payout-loan :loan-number))
      ; Prerequisite edges
      (dativity.define/add-relationship-to-model (dativity.define/action-requires :enter-loan-details :case-id))
      (dativity.define/add-relationship-to-model (dativity.define/action-requires :produce-credit-application-document :loan-details))
      (dativity.define/add-relationship-to-model (dativity.define/action-requires :produce-credit-application-document :customer-id))
      (dativity.define/add-relationship-to-model (dativity.define/action-requires :sign-credit-application-document :credit-application-document))
      (dativity.define/add-relationship-to-model (dativity.define/action-requires :payout-loan :applicant-signature))
      (dativity.define/add-relationship-to-model (dativity.define/action-requires :payout-loan :officer-signature))
      ; Role-action edges
      (dativity.define/add-relationship-to-model (dativity.define/role-performs :applicant :create-case))
      (dativity.define/add-relationship-to-model (dativity.define/role-performs :applicant :enter-loan-details))
      (dativity.define/add-relationship-to-model (dativity.define/role-performs :applicant :sign-credit-application-document))
      (dativity.define/add-relationship-to-model (dativity.define/role-performs :officer :sign-credit-application-document))
      (dativity.define/add-relationship-to-model (dativity.define/role-performs :system :payout-loan))
      (dativity.define/add-relationship-to-model (dativity.define/role-performs :system :produce-credit-application-document))))

Generate a picture of the process definition (requires graphviz, clj only).

(dativity.visualize/generate-png case-model)

What actions are possible?

(dativity.core/next-actions case-model case)
=> #{:create-case}

What can the roles do?

(dativity.core/next-actions case-model case :applicant)
=> #{:create-case}
(dativity.core/next-actions case-model case :officer)
=> #{}

What data is produced by ':create-case'?

(dativity.core/data-produced-by-action case-model :create-case)
=> #{:customer-id :case-id}

Add some data to the case to simulate a few actions

(def case
  (-> case
    (dativity.core/add-data :case-id "542967")
    (dativity.core/add-data :customer-id "199209049345")
    (dativity.core/add-data :loan-details {:amount 100000 :purpose "home"})))

What actions were performed (simulated) so far?

(dativity.core/actions-performed case-definition case)
=> #{:enter-loan-details :create-case}

Who can do what?

(dativity.core/next-actions case-model case :applicant)
=> #{}
(dativity.core/next-actions case-model case :system)
=> #{:produce-credit-application-document}
(dativity.core/next-actions case-model case :officer)
=> #{}

The document is produced and it is signed by the officer

(def case 
  (-> case
    (dativity.core/add-data :credit-application-document {:document-id "abc-123"})))

Who can do what?

(dativity.core/next-actions case-model case :applicant)
=> #{}
(dativity.core/next-actions case-model case :system)
=> #{}
(dativity.core/next-actions case-model case :officer)
=> #{:sign-credit-application-document}

Invalidation

When a user goes 'back' and updates data, it is likely that the 'subsequent' data is no longer valid. For example, if the loan amount is changed, the produced application document is not valid anymore. In general, when an action is invalidated, all the data that is produced by that action is invalid, and data that is produced by actions that required the invalid data is invalidated recursively.

(def case
    (dativity.core/invalidate-action case-model case :enter-loan-details))

Now the only available action is to enter loan details again.

(dativity.core/next-actions case-model case :applicant)
=> #{:enter-loan-details}
(dativity.core/next-actions case-model case :system)
=> #{}
(dativity.core/next-actions case-model case :officer)
=> #{}

Now it's not possible to sign the application.

(dativity.core/action-allowed? case-model case :sign-credit-application-document)
=> false

Conditionally required data

An action-requires-data edge (arrow in the diagram) can be conditional. The requirement is enforced if and only if a given predicate is true. The predicate is a function of one data node.

To say that applications for loans of more than 300 000 require signatures from two officers we can write

(def case-model 
     (dativity.define/add-relationship-to-model case-model
                                                (dativity.define/action-requires-conditional
                                                  :payout-loan
                                                  :counter-signature
                                                  (fn [loan-details]
                                                      (> (:amount loan-details) 300000))
                                                  :loan-details)))

To generate graph pictures, install graphviz:

brew install graphviz

The core functionality of Dativity only depends on Clojure.

Ubergraph is used as an adapter to graphviz for vizualisation and is only used by a utility namespace.

MIT License

Copyright (c) 2019 Morgan Bentell

dativity's People

Contributors

agentbellnorm avatar

Watchers

James Cloos avatar

Forkers

pink-junkjard

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.