How to Build Good Workflow Applications

In this short tutorial I will explain a few basic design patterns helping you to build really good Business Process Management Systems based on Imixs-Workfow. For these patterns it does not matter if you build a app just with the Imixs-Workflow core engine or if you use Imixs-Office-Workflow. All patterns explain very basic concepts which will help you a lot in understanding to build and maintain your business application. So lets start!

Pattern-1: Put your Business Logic into the Model

Imixs-Workflow is a BPMN Workflow engine. BPMN gives you the perfect place to describe your business process including the work-flow, responsibilities and business rules. Even if you are an experienced Java developer, try to describe your business process in the BPMN model first.

The advantage of putting processing logic into the model is that you can easily change this logic later without the need to code, compile and deploy your app again. This is the core idea of low-code development which helps you to speed up coding and implementing a business application.

To give you an example: maybe you think about setting the Access Level of a process instance with a piece of java code like this:

if (workitem.getItemValueDouble("budget")>10000) 
   workitem.setItemValue("$readaccess","manager-role");
else
   workitem.setItemValue("$readaccess","team-role");

Looks easy enough, but this is a hard coded role that is hard to change later. The same can be achieved with a BPMN conditional event in a much more elegant way:

The submit event is followed by a Gateway providing a decision based on the budget amount. Find out more about conditional events here.

Pattern-2: Use Plugins to Validate, Convert and Aggregate

Plugins provide a cross-model-functionality. This means, a Plugin is executed in every Event part of the processing-life-cycle. Therefore, Plugins are best suited to process recurring general checks and conversions of item values. Remember, the plugin execution is usually independent of the process instance.

Plugin code should be fast and not to complex. Complexity should be part of your model, not of your code.

public class MyPlugin extends AbstractPlugin {
  @Override
  public ItemCollection run(ItemCollection workitem, ItemCollection event)
                                          throws PluginException {
    // validate and aggregate
    double budget=workitem.getItemValueDouble("budget");
    if (budget <= 0) {
      throw new PluginException(MyPlugin.class.getName(),"INPUT ERROR",
                    "Please enter the Budget.");
    } else {
      // aggregate by rounding 
      workitem.setItemValue("budget", Math.round(budget));
    }
   } 
 }   

In this example it does not matter in which status the process instance is. We just want to ensure that a budget is always given and rounded to integer values. Of course you can do the same in a business rule directly in a BPMN Event, but in this scenario it is not efficient to put generic validations into every single event element of a model. A plugin is the right choice.

Pattern-3: Put Edge Cases Into Adapters

The Imixs-Workflow Adapter API is another powerful part of the Imixs microkernel architecture. An Adapter class can execute business logic and adapt the data of a process instance. In difference to the Plugin, an Adapter is bound directly to a BPMN Event and is only executed when the event is triggered. This gives you a better control to implement edge cases of business logic. An Adapter is the place to implement complex business logic.

public class DemoAdapter implements org.imixs.workflow.SignalAdapter {
  // inject services...
  @Inject
  ModelService modelService;
  
  @Override
  public ItemCollection execute(ItemCollection document, ItemCollection event) throws AdapterException {
    ....
  }
}

You can use CDI, EJB and all other Jakarta EE APIs. So in all cases a Plugin is to generic use SignalAdapters instead.

Pattern-4: Don’t Use Bean Validation

Yes, Bean Validation is a great thing and Imixs-Workflow is based on Jakarta EE Framework so it is possible to use beans to validate your data. But beans have one disadvantage: They are tightly coupled to a specific program situations like a form submit or a Rest API call. The BPMN business logic can be much more complex and be triggered form a web form as also via the Rest API. This can make it more complex and the final implementation is often not transparent to the processing life-cycle.

So instead make use of the Plugins to validate your business data as explained before.

Pattern-5: Don’t Use Hard Coded Model Queries

Try to avoid hard coded model query in your Java classes. This often happens in Plugin Code if you want to run your Java code only in specific situations depending on the current status of a process instance.

public class MyPlugin extends AbstractPlugin {
  @Override
  public ItemCollection run(ItemCollection workitem, ItemCollection event)
                                          throws PluginException {
    // Avoid this
    if (workitem.getTaskID() == 5000) {
      //.....
    }
   } 
 }   

This example code used a hard coded rule to test the TaskID and makes your code dependent on a concrete model. Try to use SingnalAdapters instead in situations where you want to execute your code only in specific situations.

Pattern-6: Don’t use CDI Events to Intensely

The Imixs-Workflow Engine provide a Cross-Function-API based on CDI Events. This API allows you to intercept the Processing Life-Circle directly and can be helpful in some situations. In fact, Imixs-Workflow is using CDI events for example to synchronize user data or generate event log entries. But in a complex business workflow it is not always fully transparent when a CDI Event will be process from different components and this can result in strange side effects. So it is not a good pattern to use CID events to validate or update the data of a process instance. Use instead the Plugin and Adapter APIs.

Pattern-7: Separate External APIs into Microservices

Imixs-Workflow supports a great Microservice-Architecture and a powerful Rest-API. Make use of this architecture in all cases you need to integrate external APIs.

Normally, you would first add an external API via a Maven dependency and then implement it in a plugin or adapter class. Yes, this works, but it also creates unnecessary dependencies in your core application. A better way to connect external APIs is to implement them in a separate microservice and then establish a communication via the Imixs Event-Log API.

This pattern is implemented in an asynchronous way, but of course you can also implement it in a synchronous way. Finally the pattern makes your application lean and more maintainable as you avoid a big single monolithic application artefact.

Leave a Reply

Your email address will not be published. Required fields are marked *