Migrating Camunda 7 External Tasks
Camunda 7 external tasks let workers pull work over REST: a worker calls fetchAndLock
for a topic, does the job, then calls complete, failure or bpmnError. The
fleans-camunda-external-task
plugin serves the same REST API on top of Fleans. You can move your processes to Fleans and
keep your workers. In most cases you only change their base URL.
The engine has no Camunda-specific code. The plugin is an ordinary custom-task plugin running in its own plugin host that joins the engine’s cluster. It stores waiting tasks in its own table and reports workers’ results back to the workflow.
Plugin 0.8.0 requires Fleans 0.8.0 or later.
1. Adapt the BPMN
Section titled “1. Adapt the BPMN”Replace Camunda’s camunda:type="external" camunda:topic="…" attribute pair with a Fleans
task type that starts with camunda::
<!-- Camunda 7 --><bpmn:serviceTask id="charge" camunda:type="external" camunda:topic="charge-card" />
<!-- Fleans --><bpmn:serviceTask id="charge" type="camunda:charge-card" />Workers keep subscribing to the topic (charge-card). Camunda’s <camunda:inputOutput> is not
read. Use Fleans input/output mappings instead;
the plugin README
has a conversion table.
The task’s businessKey is the process instance’s business id,
set when you start the instance ("BusinessId": "order-123" on POST /Execution/start or
POST /Execution/message). You don’t need a mapping for it.
Other Camunda task metadata comes from reserved input targets, evaluated when the task is queued (except extension properties, evaluated at fetch time) and never sent as variables:
<zeebe:ioMapping> <zeebe:input source="=order.priority" target="taskPriority" /> <!-- camunda:taskPriority --> <zeebe:input source="acme" target="tenantId" /> <zeebe:input source="eu-west" target="extensionProperty_region" /> <!-- camunda:properties --></zeebe:ioMapping>A businessKey input replaces the business id, even when it evaluates to null. Use it where
the instance has no business id, such as a call activity’s child instance:
<zeebe:input source="=order.id" target="businessKey" />.
2. Run the plugin host
Section titled “2. Run the plugin host”The plugin repository ships a ready-to-run host image,
ghcr.io/nightbaker/fleans-camunda-external-task. With the
Docker Compose bundle:
Start the engine first. The bundle creates the fleans_default network that the plugin host
joins, and the network only gets that name when the compose project is called fleans:
cd <unzipped-engine-compose-bundle>echo "COMPOSE_PROJECT_NAME=fleans" >> .env # names the network fleans_defaultdocker compose up -d # engine first: creates the networkThen add a docker-compose.override.yaml next to the bundle’s docker-compose.yaml:
services: camunda-external-task: image: ghcr.io/nightbaker/fleans-camunda-external-task:<version> networks: [fleans_default] ports: ["8090:8080"] environment: ConnectionStrings__orleans-redis: "redis:6379,password=${ORLEANS_REDIS_PASSWORD}" Orleans__ClusterId: "fleans" Orleans__ServiceId: "fleans" ConnectionStrings__camunda-external-tasks: "Host=postgres;Database=camunda_external_tasks;Username=postgres;Password=${POSTGRES_PASSWORD}" depends_on: [redis, postgres]networks: fleans_default: external: true name: fleans_defaultThen run docker compose up -d again from the bundle directory. Compose merges the override and
starts the plugin host on the existing network. If you skip the engine-first step or the project
name, it fails with network fleans_default declared as external, but could not be found.
Orleans__ClusterId and Orleans__ServiceId must match the bundle’s CLUSTER_CLUSTER_ID and
CLUSTER_SERVICE_ID. The database and the camunda_external_tasks table are created at startup.
To run the plugin inside a plugin host you already have, add the
Fleans.Plugins.CamundaExternalTask NuGet package and register it with
AddCamundaExternalTasks(...) and MapCamundaExternalTaskApi(). See the
repository README for
details and every configuration setting.
3. Point the workers at the host
Section titled “3. Point the workers at the host”ExternalTaskClient.create().baseUrl("http://camunda-external-task:8080/engine-rest").build();new Client({ baseUrl: "http://camunda-external-task:8080/engine-rest" });Every endpoint the official Java and JS clients call is supported: fetchAndLock (including
long polling, usePriority, sorting, and the businessKey / tenant filters), complete,
failure, bpmnError, lock, extendLock, unlock, and the file-variable download. The
operator endpoints are there too: task queries and counts, topic-names, PUT /retries and
PUT /priority. Errors use Camunda’s body and status codes. Set
CamundaExternalTasks__BasicAuth__Username and __Password on the host to require HTTP basic
authentication (the clients’ BasicAuthProvider / BasicAuthInterceptor).
Differences from Camunda 7
Section titled “Differences from Camunda 7”- Incidents are opt-in. By default,
failurewithretries = 0fails the activity with error code500, and an error boundary event can catch it. WithCamundaExternalTasks__Incidents=truethe task stays as an incident, as in Camunda, untilPUT /external-task/{id}/retriesresolves it. Incidents are not shown in the Fleans UI. bpmnErrorfails the activity with the worker’serrorCode, so an error boundary event with that code catches it. Its variables are visible to the boundary or error event sub-process that catches it, but not to an error boundary on a parent workflow’s call activity.- Variables: primitives,
Date(kept as a string),Json,Bytes,File, andObjectwithapplication/jsonare supported. Binary values live in the workflow state, so keep them small. Java-serialized objects are rejected. - Missing fields:
processDefinitionVersionTagis alwaysnull, because Fleans has no equivalent. - Scope: the
/external-taskresource plus the file-variable download. Start processes and correlate messages through the Fleans REST API.
The repository README lists every difference.