Search code examples
resttransactionsmicroservicescamunda

Data Coherence for completing camunda user task and updating external DB


We use camunda as REST service in our app. For completing user task in our app we have to make 2 actions:
1. Write data in local data base
2. Complete task by camunda rest API

But this operation is not transactional. It is a problem for us.

This code code is not transactional:

completeUserTask(String taskId, SpecificTaskData userData) {
     dao.update(userData)
     camundaRestApi.completeUserTask(taskId)
}

I see two available solution. Both split camunda user task to 2 nodes: enter image description here

But i don’t like the idea creating service task or message receiver for each user tasks.
I think it makes a diagram unclear (more irrational).
What do you think about this problem? Thank you.


Solution

  • My advice is probably too late :-)

    We also faced the problem about how to place both operation (writing business data and completing the task/sending message) in the same transaction.

    Our solution is organize methods like this:

    @Transactional
    void correlate(String message, String businessKey, DataObject dataObject) {
            dataRepository.update(dataObject);
            runtimeService.correlateMessage(messageCode, businessKey);        
    }
    

    Of course you can use any other triggers to continue the process flow (complete/submit user task).

    Another approach is create execution listener which can get data from process context and put it into wherever you need (DAO, service, event bus, etc). Also by Java Config you can add such listener for any kind of activity by default (without specifying it in Modeler):

        public class AddSendEventListenerToBpmnParseListener implements BpmnParseListener {
        
          @Override   public void parseProcess(Element processElement, ProcessDefinitionEntity processDefinition) {
            processDefinition.addBuiltInListener(PvmEvent.EVENTNAME_START, new SendEventListener());
            processDefinition.addBuiltInListener(PvmEvent.EVENTNAME_END, new SendEventListener());   }
        
          @Override   public void parseIntermediateThrowEvent(Element intermediateEventElement, ScopeImpl scope, ActivityImpl activity) {
            activity.addBuiltInListener(PvmEvent.EVENTNAME_START, new SendEventListener());   }
        
          @Override   public void parseStartEvent(Element startEventElement, ScopeImpl scope, ActivityImpl startEventActivity) {   }
        
        // other methods omitted 
    
    }