During the implementation of tasklists we should always think about runtime behaviour in a production scenario.
But lets be honest - first of all backend devs are more interested in data quality than response time (<2 secs). So in every developers life there comes the day he gets a speeding ticket.

The main problem during the retrieval of tasks and coresponding data often is, that user tasks are created and stored in the camunda schema (ACT_RU_TASK table) while business data (that has to be presented to the user) is stored in another business-related schema. So you may first

  1. retrieve a camunda task list
  2. followed by iterating over this list to get some IDs (customer, account) from the coresponding process variables to
  3. collect data from your business-related tables (using those IDs) and
  4. create a business task list using these entities.

Might take a while right...?

wait

To solve this dilemma and make things performant camunda documentation suggests custom queries which is a fine approach if you can manage to have all your data in one place:

"Precondition: In order to use your own MyBatis (or all types of SQL) queries, your domain data and the process engine data must be stored in the same database. Otherwise you cannot technically construct a single SQL query, which is our goal in terms of performance optimization", [camunda documentation].

Unfortunately this was just not possible in a recent customer scenario so we went with a kind of unusual way:

Redundant user task management

The basic idea is to have a representation of each and every user task of your engine stored somewhere else in a spezialized table. And this representation contains exactly the information needed by the user.

Since we could not change the way on how to retrieve data while creating a task we changed the when! Imagine a task list containing 10 tasks and each task needs 1 second to get fully loaded with additional data. It takes 10 secs every time the task list is requested. Instead we now run this "1-second-running code" only once at task creation (before the user even takes notice of its existence) and store a co-responding record to our specialized table.

do

Requesting a task list now just means to query that specialized table. This sped up things a lot in our application.

Further thoughts:

  1. Yes yes yes I know. Holding "same" data in different places and having to keep it in sync is nothing you want to do on a frequent base. There are lots of developers who do not really like this idea. You should maybe just use it for important tasklists that cannot be sped up in other ways.
  2. The above shown example uses two service tasks for crating and removing a user task. This is just for the purpose of clarification. In our scenario, we worked with listeners at start and end of task creation.  This also has the important effect that you can use the actual taskId when storing and deleting it!

Let me know what you think and if I can help you to understand this better. This was just a rough nutshell.