I have, however, spiked an approach that simply uses a Native Task Query, which would appear to me to have the benefit of introducing little extra code or configuration to our application, while largely reusing the Task interface.
The key part of my approach is to use the assignee property on Task to accommodate the candidate information - the SQL that I provide to the Native Task Query obtains the candidate information from ACT_RU_IDENTITY_LINK, uses GROUP BY to consolidate the rows in the result set by the tasks themselves, and uses SQL Server functions to concatenate each candidate and present it in the result set as though it were actually in the ASSIGNEE_ column. Task.getAssignee() would then return us a comma-separated list of candidates that the application can parse.
Are there any significant disadvantages to doing it this way? There is some potential for making comprehensibility of our application a little trickier as it could be considered an abuse of the Task.getAssignee() method contract. However, we’re thus able to reuse Task within all of our code. This has the advantage of allowing for only one code path in our application for handling tasks when they are returned by Camunda, as opposed to the application sometimes dealing with Task objects and sometimes our own DTO’s. Which would seem to be a larger compromise in terms of simplicity.
If there were any other significant bits of data that we needed to return from a task query that aren’t supported in the API, then I would definitely be considering a Custom Query, as I think there is only so much mileage we would get from this approach before we would be sacrificing comprehensibility.