Final StretchOrdered learning track

Anti-Patterns and Common Pitfalls

Learn Java BPMN with Camunda BPM Platform 7 - Part 031

Camunda 7 anti-patterns and common pitfalls for production BPMN systems, including modeling, transaction, data, integration, operation, and governance failure modes.

25 min read4957 words
PrevNext
Lesson 3135 lesson track3035 Final Stretch
#java#bpmn#camunda-7#workflow+3 more

Part 031 — Camunda 7 Anti-Patterns and Common Pitfalls

Target: setelah membaca part ini, kita tidak hanya bisa mengenali desain Camunda yang buruk, tetapi juga bisa menjelaskan kenapa buruk, failure mode apa yang akan muncul, bagaimana mendeteksinya lebih awal, dan bagaimana memperbaikinya tanpa merusak running instance.

Camunda 7 adalah workflow engine yang cukup kecil untuk di-embed di aplikasi Java, tetapi cukup kuat untuk menjalankan proses bisnis long-running yang melibatkan manusia, sistem eksternal, timer, retry, audit, history, dan recovery manual.

Karena fleksibel, Camunda juga mudah disalahgunakan.

Anti-pattern di Camunda jarang terlihat sebagai compile error. Biasanya muncul sebagai:

  • proses yang tampak benar di demo tetapi macet di produksi,
  • incident yang tidak bisa dijelaskan oleh operator,
  • proses yang sulit dimigrasikan,
  • variable yang tidak bisa dibaca karena class berubah,
  • history table membengkak,
  • retry menyebabkan duplicate side effect,
  • model BPMN berubah menjadi spaghetti,
  • task user tidak punya ownership yang defensible,
  • atau engine menjadi distributed monolith yang semua tim takut menyentuhnya.

Part ini adalah katalog kegagalan desain.


1. Cara Membaca Anti-Pattern

Setiap anti-pattern perlu dibaca dengan empat lensa:

LensaPertanyaan
SymptomApa tanda awalnya di code, model, DB, Cockpit, atau support ticket?
Root causeAsumsi mental model apa yang salah?
Failure modeApa yang terjadi saat traffic, parallelism, failure, atau perubahan versi meningkat?
CorrectionBoundary, modeling, code, atau operasi apa yang perlu diubah?

Anti-pattern Camunda sering terjadi karena tim melihat BPMN sebagai diagram, bukan sebagai executable persisted state machine.


2. Mental Model Singkat: Di Mana Anti-Pattern Muncul

Camunda 7 memiliki beberapa lapisan keputusan:

Anti-pattern biasanya muncul saat boundary antar lapisan dilanggar:

  • BPMN melakukan keputusan yang seharusnya di DMN/domain service.
  • Delegate mengandung seluruh use case.
  • Variable menjadi database business entity.
  • Camunda DB dipakai sebagai read model aplikasi.
  • Job executor diperlakukan seperti message broker.
  • Cockpit dipakai sebagai primary business operations UI.
  • REST API engine diekspos langsung ke frontend.

3. Anti-Pattern #1 — God Process

Bentuk

Satu BPMN berisi semua variasi bisnis, semua exception path, semua approval, semua SLA, semua integrasi, semua recovery, semua cabang region, semua produk, semua tenant.

Symptom

  • Diagram tidak bisa dibaca tanpa zoom besar.
  • Lebih dari 40-60 activity dalam satu process definition tanpa modularisasi jelas.
  • Banyak XOR gateway berantai.
  • Naming activity generik: Validate, Check, Process, Review, Handle Error.
  • Setiap perubahan kecil memaksa regression test seluruh proses.
  • Tim takut deploy process version baru.

Root Cause

Tim menganggap BPMN adalah kanvas untuk semua logic, bukan kontrak orchestration level.

Failure Mode

  • Migration plan menjadi sangat sulit karena banyak activity id berubah.
  • Instance lama sulit diperbaiki karena state bisa berada di terlalu banyak posisi.
  • Audit trail terlalu noisy.
  • Operator tidak bisa membedakan path normal, exception, dan repair.
  • Gateway condition saling overlap.

Correction

Pecah proses berdasarkan boundary berikut:

BoundaryGunakan
Reusable business capabilityCall activity
Local grouping dengan lifecycle parent samaEmbedded subprocess
Reactive exception handling scopedEvent subprocess
Rule matrixDMN
Complex human caseworkCase shell + child processes
System integration with independent retryExternal task / async service task

Refactoring Heuristic

Jika satu BPMN perlu menjawab semua pertanyaan berikut, kemungkinan sudah menjadi God Process:

  • Siapa melakukan apa?
  • Rule bisnis apa yang berlaku?
  • Bagaimana integrasi teknis dilakukan?
  • Bagaimana error recovery dilakukan?
  • Bagaimana audit/legal evidence disusun?
  • Bagaimana variasi produk/tenant/region dipilih?

Pisahkan pertanyaan-pertanyaan itu ke layer berbeda.


4. Anti-Pattern #2 — Spaghetti Gateway

Bentuk

Model berisi banyak gateway nested tanpa invariant eksplisit. Setiap gateway hanya punya label teknis seperti yes/no, valid/invalid, approved/rejected tanpa menyatakan invariant bisnis.

Symptom

  • XOR gateway bersarang 5-8 level.
  • Parallel join menunggu token yang tidak selalu dibuat.
  • Inclusive gateway dipakai karena “fleksibel”, bukan karena join semantics dipahami.
  • Default flow mengarah ke path sukses tanpa logging.
  • Test path hanya mencakup happy path.

Root Cause

Gateway dipakai sebagai pengganti decision model.

Failure Mode

  • Deadlock di join.
  • Path tidak deterministic.
  • Incident muncul jauh dari root decision.
  • Model sulit dibaca oleh non-engineer.
  • Rule change memicu perubahan BPMN besar.

Correction

Gunakan pola:

Gateway boleh ada, tetapi gateway sebaiknya routing berdasarkan outcome yang sudah diberi nama, bukan menghitung rule kompleks di expression.

Rule

Satu gateway sebaiknya menjawab satu pertanyaan bisnis yang jelas:

Bad: Is valid?
Good: Is applicant eligible for automated approval?

Bad: Error?
Good: Did payment provider return a recoverable business decline?

5. Anti-Pattern #3 — God Delegate

Bentuk

JavaDelegate berisi seluruh use case: validasi, rule, query DB, call HTTP, update entity, publish event, set variable, choose next state, logging, audit, retry decision.

@Component
public class ProcessApplicationDelegate implements JavaDelegate {
  @Override
  public void execute(DelegateExecution execution) {
    // parse variables
    // query database
    // call external service
    // decide business rule
    // update domain status
    // send notification
    // write audit
    // set 25 process variables
  }
}

Symptom

  • Delegate sulit ditest tanpa engine.
  • Delegate name generik: ProcessCaseDelegate, HandleApprovalDelegate.
  • BPMN hanya jadi sequence of Java methods.
  • Business analyst tidak bisa membaca behavior dari model.
  • Retry delegate menyebabkan duplicate side effects.

Root Cause

Tim memakai Camunda sebagai trigger untuk service method, bukan orchestration boundary.

Failure Mode

  • Error handling tidak terlihat di BPMN.
  • Retry tidak aman.
  • Incident tidak informatif.
  • Migrasi proses tidak menyelesaikan coupling karena logic tersembunyi di Java.
  • Versi delegate berubah saat instance lama masih berjalan.

Correction: Thin Delegate Pattern

@Component("reserveInventoryDelegate")
public class ReserveInventoryDelegate implements JavaDelegate {

  private final InventoryCommandHandler handler;

  public ReserveInventoryDelegate(InventoryCommandHandler handler) {
    this.handler = handler;
  }

  @Override
  public void execute(DelegateExecution execution) {
    var command = new ReserveInventoryCommand(
        (String) execution.getVariable("orderId"),
        (String) execution.getBusinessKey(),
        (String) execution.getVariable("reservationRequestId")
    );

    var result = handler.handle(command);

    execution.setVariable("inventoryReservationId", result.reservationId());
    execution.setVariable("inventoryReserved", result.reserved());
  }
}

Delegate hanya adapter:

  • baca input contract,
  • panggil domain/application service,
  • translate output ke process variables,
  • lempar exception yang tepat.

Business invariant tetap di domain service.


6. Anti-Pattern #4 — Variable Dumping Ground

Bentuk

Semua data dimasukkan sebagai process variable: payload request lengkap, response lengkap, entity lengkap, list besar, object Java serialized, file metadata, decision explanation, temporary values, UI state.

Symptom

  • ACT_RU_VARIABLE dan ACT_HI_VARINST membengkak.
  • Query Cockpit lambat.
  • Variable sulit dipahami karena nama tidak konsisten.
  • Serialized object gagal deserialize setelah class berubah.
  • Variable sensitive masuk history tanpa masking.
  • Gateway expression membaca nested JSON besar.

Root Cause

Tim mengira process variable adalah application database.

Failure Mode

  • DB growth cepat.
  • History cleanup berat.
  • Data retention sulit dipenuhi.
  • Security exposure.
  • Migration classpath problem.
  • Performance buruk saat variable fetch.

Correction

Pisahkan data:

DataSimpan di
Routing state kecilProcess variable
Business entity lengkapDomain database
Large document/fileObject storage / document service
Audit event bisnisAudit/event store
Temporary calculationTransient variable / local code
User form draftForm/application storage
Decision result summaryProcess variable kecil + decision history

Naming Contract

Gunakan variable yang eksplisit:

caseId
caseType
riskTier
approvalOutcome
reviewDeadline
lastExternalEventId
paymentAttemptCount

Hindari:

data
payload
request
response
object
result
flag
status

7. Anti-Pattern #5 — Java Serialized Object Variables

Bentuk

Process variable menyimpan object Java biasa dengan Java serialization.

execution.setVariable("customer", customerEntity);

Symptom

  • Instance lama gagal saat class berubah.
  • Worker yang berbeda tidak punya class yang sama.
  • REST client tidak bisa membaca variable.
  • Migration antar service sulit.
  • Security review menolak Java serialization.

Root Cause

Object in-memory disamakan dengan long-running persisted contract.

Failure Mode

Long-running instance hidup lebih lama daripada versi class. Begitu class berubah, serialized variable menjadi debt.

Correction

Gunakan primitive atau JSON contract eksplisit.

ObjectValue customerSnapshot = Variables
    .objectValue(customerSnapshotDto)
    .serializationDataFormat(Variables.SerializationDataFormats.JSON)
    .create();

execution.setVariable("customerSnapshot", customerSnapshot);

Lebih baik lagi, simpan hanya ID dan ringkasan immutable:

{
  "customerId": "C-10091",
  "riskTier": "HIGH",
  "snapshotVersion": 3
}

8. Anti-Pattern #6 — Business Status = Process Activity Name

Bentuk

Aplikasi menampilkan status bisnis dengan membaca current BPMN activity.

Current activity: Review Application
=> Business status: UNDER_REVIEW

Symptom

  • Rename activity merusak UI/report.
  • Multiple token menyebabkan status ambigu.
  • Parallel path membingungkan dashboard.
  • Migrasi BPMN mengubah status historis.

Root Cause

Process state disamakan dengan business state.

Failure Mode

Business users melihat status berubah karena refactoring teknis, bukan karena event bisnis.

Correction

Buat explicit business status projection.

Camunda activity tetap operational state. Business status harus punya lifecycle sendiri.


9. Anti-Pattern #7 — Synchronous Remote Call Inside Same Transaction

Bentuk

Service task synchronous memanggil HTTP/gRPC eksternal dalam transaksi engine yang sama tanpa async boundary dan tanpa idempotency.

Symptom

  • API caller timeout.
  • External system berhasil, tetapi Camunda rollback.
  • Retry menciptakan duplicate request.
  • Incident message tidak cukup untuk replay aman.

Root Cause

Technical transaction boundary dianggap sama dengan business transaction boundary.

Failure Mode

External side effect tidak ikut rollback dengan DB Camunda.

Correction

Gunakan async boundary + idempotency.

Command ke external system harus punya idempotency key:

idempotencyKey = processInstanceId + ':' + activityId + ':' + businessCommandId

Untuk integrasi kritikal, gunakan outbox + message correlation atau external task.


10. Anti-Pattern #8 — Async Everywhere

Bentuk

Semua service task diberi asyncBefore="true" karena dianggap lebih aman.

Symptom

  • Banyak job kecil.
  • Job executor backlog tinggi.
  • Process latency naik.
  • Cockpit penuh incident noisy.
  • Boundary transaksi terlalu granular.
  • Debug path sulit karena setiap step dipisah job.

Root Cause

Async boundary diperlakukan sebagai best practice universal.

Failure Mode

DB dan job executor menjadi bottleneck. Setiap boundary berarti job creation, acquisition, locking, execution, retry, dan history event tambahan.

Correction

Gunakan async boundary saat ada alasan:

SituasiAsync?
Remote side effectYa, sering asyncBefore
Expensive calculationYa, jika perlu retry/decoupling
Pure in-memory mappingTidak
Gateway routing sederhanaTidak
Before parallel fan-outSering ya
Before wait state alamiBiasanya tidak perlu
User taskSudah wait state alami
Timer/message catchSudah wait state alami

11. Anti-Pattern #9 — No Async Boundary Before Risky Side Effect

Bentuk

Kebalikan dari anti-pattern sebelumnya: semua dibiarkan synchronous.

Symptom

  • Failure di step terakhir rollback semua step sebelumnya.
  • Error message muncul ke API caller, bukan ke operational incident queue.
  • Proses gagal start padahal sebagian side effect sudah terjadi.

Correction

Letakkan transaction save point sebelum side effect:

<bpmn:serviceTask id="chargePayment" camunda:delegateExpression="${chargePaymentDelegate}" camunda:asyncBefore="true">
  <bpmn:extensionElements>
    <camunda:failedJobRetryTimeCycle>R5/PT10M</camunda:failedJobRetryTimeCycle>
  </bpmn:extensionElements>
</bpmn:serviceTask>

12. Anti-Pattern #10 — Misusing BPMN Error for Technical Failure

Bentuk

Semua exception ditangkap lalu dilempar sebagai BpmnError supaya proses masuk path error.

try {
  paymentClient.charge(command);
} catch (Exception e) {
  throw new BpmnError("PAYMENT_FAILED");
}

Symptom

  • Network timeout diperlakukan seperti business decline.
  • Tidak ada retry teknis.
  • Process lanjut ke rejection padahal provider sedang down.
  • Audit menyatakan keputusan bisnis, bukan technical outage.

Root Cause

Tim tidak membedakan business error dan technical exception.

Failure Mode

Business outcome salah.

Correction

FailureRepresentasi
Card declinedBPMN error / business result
Customer ineligibleDMN outcome / BPMN path
HTTP 503 provider downJava exception -> retry/incident
Timeout unknown outcomeretry with idempotency / reconciliation
Validation bugexception -> incident

13. Anti-Pattern #11 — Swallowing Exceptions

Bentuk

Delegate menangkap exception, log error, lalu process lanjut.

try {
  provider.call();
} catch (Exception ex) {
  log.error("failed", ex);
}

Symptom

  • Process marked completed padahal business action gagal.
  • Tidak ada incident.
  • Operator tahu gagal dari sistem eksternal, bukan Camunda.
  • Audit trail menyesatkan.

Root Cause

Tim ingin menghindari incident, bukan memperbaiki recovery model.

Correction

  • Lempar exception untuk technical failure.
  • Gunakan BPMN error untuk business failure yang valid.
  • Gunakan escalation/user task untuk manual decision.
  • Tambahkan runbook untuk incident umum.

14. Anti-Pattern #12 — Signal for Targeted Message

Bentuk

Signal event dipakai untuk melanjutkan satu process instance tertentu.

Symptom

  • Banyak instance bergerak karena satu event.
  • Race antar tenant atau case.
  • Sulit menjamin recipient tunggal.

Root Cause

Message dan signal semantics tertukar.

Correction

Gunakan message event dengan correlation key/business key untuk targeted recipient.

Gunakan signal hanya untuk broadcast semantics yang memang disengaja, misalnya “global shutdown notice” untuk semua active processes yang listen ke signal tersebut.


15. Anti-Pattern #13 — Missing Business Key

Bentuk

Process instance dimulai tanpa business key.

Symptom

  • Operator mencari instance berdasarkan variable query lambat.
  • Message correlation rapuh.
  • Duplicate process instance sulit dideteksi.
  • External system tidak punya stable reference.

Correction

Business key harus menjadi stable business identity.

runtimeService.startProcessInstanceByKey(
    "enforcementCaseProcess",
    caseId,
    variables
);

Jika satu business entity bisa punya beberapa process, buat convention:

case:{caseId}:main
case:{caseId}:appeal:{appealId}
order:{orderId}:fulfillment
customer:{customerId}:kyc-review:{reviewId}

16. Anti-Pattern #14 — Direct Camunda DB Mutation

Bentuk

Operasi support memperbaiki job, variable, incident, atau task dengan UPDATE ACT_RU_* langsung.

Symptom

  • Incident tidak resolved walau retries diubah.
  • History/user operation log tidak lengkap.
  • Cache engine inconsistent.
  • Upgrade schema risk meningkat.

Root Cause

Camunda DB dianggap public API.

Correction

Gunakan API:

managementService.setJobRetries(jobId, 3);
runtimeService.setVariable(executionId, "manualCorrectionReason", reason);
runtimeService.createProcessInstanceModification(processInstanceId)
    .cancelAllForActivity("wrongActivity")
    .startBeforeActivity("correctActivity")
    .execute();

Untuk operasi massal, gunakan batch API atau Cockpit Enterprise feature jika tersedia.


17. Anti-Pattern #15 — Cockpit as Business Backoffice

Bentuk

Business user melakukan operasi reguler lewat Cockpit: assign case, approve, reject, correct data, trigger next action.

Symptom

  • Semua user diberi access luas ke Cockpit.
  • Operasi bisnis tidak punya validation domain.
  • Perubahan variable dilakukan manual tanpa business guardrail.
  • Audit berbunyi “operator modified variable”, bukan “supervisor approved override”.

Root Cause

Cockpit adalah operational tool, bukan business UI.

Correction

Buat business backoffice sendiri yang memanggil application facade.

Cockpit untuk operator teknis: inspect, retry, suspend, migration, incident resolution.

Business operation harus punya domain authorization, validation, audit, dan reason code.


18. Anti-Pattern #16 — Direct Engine REST from Frontend

Bentuk

SPA/frontend langsung memanggil Camunda REST API untuk start process, complete task, set variables, atau correlate message.

Symptom

  • UI tahu process definition key, task id, variable names.
  • Security model sulit.
  • Variable contract bocor ke frontend.
  • Perubahan BPMN merusak UI.

Root Cause

Engine API dipakai sebagai product API.

Correction

Tambahkan workflow facade:

Facade menyembunyikan process internals dan menjaga contract stabil.


19. Anti-Pattern #17 — Process Model Coupled to UI Steps

Bentuk

Setiap screen menjadi user task atau setiap button menjadi BPMN activity.

Symptom

  • BPMN berubah tiap UI redesign.
  • Process instance menunggu “Fill Tab 2” atau “Click Submit”.
  • Human work tidak sesuai responsibility, hanya layout.

Root Cause

User journey UI disamakan dengan workflow responsibility.

Correction

User task mewakili unit of accountability, bukan screen.

Bad user task: Fill Address Page
Good user task: Provide Applicant Information

Bad user task: Click Approve Button
Good user task: Risk Officer Review

Form boleh punya banyak step. Workflow task tetap satu responsibility.


20. Anti-Pattern #18 — Timer Explosion

Bentuk

Setiap instance membuat banyak timer untuk reminder kecil, polling, repeated checks, SLA, escalation, cleanup, dan internal scheduling.

Symptom

  • ACT_RU_JOB penuh timer.
  • Job executor sibuk mengambil timer kecil.
  • Timer due date cluster menyebabkan spike.
  • SLA timer membuat incident saat delegate lambat.

Root Cause

Timer BPMN dipakai sebagai general scheduler.

Correction

Gunakan timer BPMN hanya untuk business-relevant wait:

KebutuhanSolusi
SLA deadline per caseBPMN timer boundary/event subprocess
Reminder email periodikScheduler eksternal / notification service
Poll external statusExternal worker/backoff scheduler
Cleanup technical dataBatch job platform
Business timeoutBPMN timer

21. Anti-Pattern #19 — Unbounded History

Bentuk

History level tinggi, TTL tidak jelas, variable besar disimpan, cleanup tidak diatur.

Symptom

  • ACT_HI_* tumbuh cepat.
  • Backup/restore lambat.
  • Cockpit lambat.
  • GDPR/retention risk.
  • Disk growth menjadi incident platform.

Root Cause

Audit dianggap sama dengan menyimpan semua hal selamanya.

Correction

Definisikan retention per process definition:

Process TypeHistory NeedTTL Example
Payment authorizationLow-medium90-365 days
Regulatory enforcementHigh7-10 years
Onboarding draftLow30-90 days
Internal approvalMedium1-3 years

Jangan jadikan Camunda history sebagai satu-satunya regulatory audit. Buat audit projection/domain event log untuk evidence yang harus stabil lintas engine migration.


22. Anti-Pattern #20 — No Incident Runbook

Bentuk

Tim punya monitoring incident, tetapi tidak punya prosedur pemulihan.

Symptom

  • Operator bertanya ke developer setiap failed job.
  • Retry dilakukan tanpa tahu apakah aman.
  • Incident di-resolve dengan variable edit random.
  • Tidak ada reason code.

Correction

Setiap service task eksternal/async harus punya runbook:

Activity: chargePayment
Failure types:
- PROVIDER_TIMEOUT
- PROVIDER_5XX
- DUPLICATE_COMMAND
- INVALID_MERCHANT_CONFIGURATION

Safe retry:
- PROVIDER_TIMEOUT: yes if idempotencyKey present
- PROVIDER_5XX: yes after provider status page green
- DUPLICATE_COMMAND: no, reconcile first
- INVALID_MERCHANT_CONFIGURATION: no, fix config first

Operator action:
1. Check idempotency key in variable paymentCommandId.
2. Query payment provider by command id.
3. If no charge found, set retries=1.
4. If charge found, correlate PaymentCharged message.
5. Record reason code and ticket id.

23. Anti-Pattern #21 — Retry Without Idempotency

Bentuk

Failed job retry memanggil operasi eksternal yang tidak idempotent.

Symptom

  • Duplicate charge.
  • Duplicate email.
  • Duplicate shipment.
  • Duplicate ticket.
  • Operator takut retry.

Correction

Buat semua side effect yang bisa diretry menjadi idempotent.

Side EffectIdempotency Strategy
Payment chargeProvider idempotency key / command id
Email notificationnotification event id
Shipment bookingbooking request id
Case creationunique business key
Document generationdeterministic document request id

Jika external system tidak mendukung idempotency, gunakan reconciliation step sebelum retry.


24. Anti-Pattern #22 — Overusing Execution and Task Listeners

Bentuk

Listeners dipakai untuk business logic utama.

Symptom

  • Behavior tersembunyi dari BPMN shape.
  • Listener order sulit dipahami.
  • Test path tidak jelas.
  • Task complete memiliki side effect tak terlihat.

Root Cause

Listener terasa convenient karena bisa disisipkan di lifecycle event.

Correction

Gunakan listener untuk cross-cutting atau lifecycle adapter kecil:

  • set assignment metadata,
  • emit audit technical event,
  • normalize form field,
  • record task opened/closed,
  • enforce simple invariant.

Jangan gunakan listener untuk:

  • payment charge,
  • approval decision,
  • case transition utama,
  • external API orchestration,
  • complex validation.

25. Anti-Pattern #23 — No Versioning Strategy

Bentuk

Process definition baru dideploy tanpa strategy untuk instance lama.

Symptom

  • Instance lama tertahan di activity yang tidak ada di versi baru.
  • Delegate berubah incompatible.
  • Form key berubah.
  • Message name/correlation key berubah.
  • Operator tidak tahu versi mana yang sedang berjalan.

Correction

Buat version policy:

PerubahanMigration Needed?
Tambah label activityTidak, jika id tetap
Rename activity idYa/berisiko
Tambah service task before wait stateMungkin
Ubah variable contractYa, jika instance lama masih aktif
Ubah call activity bindingSangat mungkin
Ubah message nameYa, untuk waiting subscriptions
Ubah form keyMungkin, tergantung active task

Activity id adalah compatibility contract. Jangan rename sembarangan.


26. Anti-Pattern #24 — Activity ID as Auto-Generated Garbage

Bentuk

Activity id seperti Activity_0x72as9, Gateway_1lq83mm, Event_0abc dibiarkan masuk production.

Symptom

  • Incident sulit dibaca.
  • Migration mapping sulit.
  • Metrics tidak bermakna.
  • Runbook tidak bisa menunjuk activity jelas.

Correction

Gunakan stable id:

StartEvent_CaseSubmitted
Task_CollectEvidence
Gateway_RiskTierRouting
ServiceTask_CheckSanctions
UserTask_SupervisorReview
BoundaryTimer_ReviewSlaExceeded
EndEvent_CaseClosed

Label boleh human-friendly. ID harus stable dan operation-friendly.


27. Anti-Pattern #25 — Message Correlation by Variable Query Only

Bentuk

Event eksternal mencari process instance berdasarkan variable longgar.

runtimeService.createMessageCorrelation("PaymentReceived")
    .processInstanceVariableEquals("orderId", orderId)
    .correlate();

Symptom

  • Multiple match exception.
  • No match karena variable belum committed.
  • Correlation lambat.
  • Duplicate instance menerima event tidak jelas.

Correction

Gunakan business key dan correlation keys yang dirancang.

runtimeService.createMessageCorrelation("PaymentReceived")
    .processInstanceBusinessKey(orderId)
    .setVariable("paymentEventId", eventId)
    .correlateWithResult();

Untuk event ingestion yang rawan duplicate, masukkan inbox table sebelum correlate.


28. Anti-Pattern #26 — Treating Job Executor as Queue

Bentuk

Job executor dipakai untuk high-throughput work queue umum.

Symptom

  • Banyak service task async untuk setiap item batch.
  • Job backlog besar.
  • Engine DB menjadi bottleneck queue.
  • Prioritas bisnis sulit.
  • Noisy incident.

Root Cause

Job executor memang mengambil pekerjaan, tetapi ia dirancang untuk melanjutkan process execution, bukan menggantikan Kafka/RabbitMQ/SQS/worker queue.

Correction

Gunakan job executor untuk workflow continuations. Untuk high-volume data processing, gunakan queue platform dan correlate result ke Camunda jika business process perlu menunggu.


29. Anti-Pattern #27 — Multi-Instance Without Backpressure

Bentuk

Multi-instance parallel memanggil external system untuk ratusan/ribuan item.

Symptom

  • External API rate limit.
  • DB spike.
  • Optimistic locking.
  • Job executor saturation.
  • Incident storm.

Correction

Untuk fan-out besar, gunakan batch process atau queue.

Jika tetap memakai BPMN multi-instance:

  • batasi collection size,
  • gunakan async boundary dengan retry cycle,
  • gunakan idempotency per item,
  • set job priority,
  • monitor backlog,
  • pertimbangkan sequential multi-instance.

30. Anti-Pattern #28 — Tenant Isolation by Convention Only

Bentuk

Tenant id disimpan sebagai variable tetapi tidak dipakai di authorization, query boundary, deployment, atau API.

Symptom

  • User bisa melihat task tenant lain melalui salah konfigurasi filter.
  • Message event salah correlate.
  • Report bercampur.
  • Admin operasi tidak tenant-scoped.

Correction

Tenant isolation harus konsisten:

LayerTenant Control
APItenant context required
Process definitiontenant-aware deployment jika diperlukan
Task querytenant filter
Message correlationtenant id + business key
Authorizationtenant-specific group/permission
Reportingtenant partition/filter
Audittenant id explicit

31. Anti-Pattern #29 — Process Instance Modification as Normal Business Flow

Bentuk

Operator rutin memindahkan token karena BPMN tidak memodelkan path bisnis.

Symptom

  • Modification terjadi setiap hari.
  • Banyak instance punya path yang tidak sesuai model.
  • Audit butuh penjelasan manual.
  • Developers menolak memperbaiki BPMN karena “bisa dimodify di Cockpit”.

Root Cause

Repair tool dipakai sebagai business workflow.

Correction

Jika modification sering, modelnya kurang lengkap. Tambahkan explicit path:

  • manual correction task,
  • reopen event,
  • supervisor override,
  • escalation subprocess,
  • cancellation/retry path.

Process instance modification tetap dipakai untuk exceptional repair, bukan routine operation.


32. Anti-Pattern #30 — No Domain-Level State Machine

Bentuk

Camunda menjadi satu-satunya state machine untuk semua entity bisnis.

Symptom

  • Domain service tidak bisa menjawab status tanpa query Camunda.
  • Jika process corrupt, business entity tidak punya invariant sendiri.
  • Reprocessing event sulit.
  • Migration engine berarti migration business state.

Correction

Gunakan dua state:

Domain state menjawab “apa status bisnis entity?”

Process state menjawab “workflow sedang menunggu apa?”


33. Anti-Pattern #31 — “Happy Path Only” Process Tests

Bentuk

Test hanya memastikan process instance selesai untuk satu input.

Symptom

  • Gateway edge tidak dites.
  • Timer tidak dites.
  • Failed job tidak dites.
  • Message duplicate tidak dites.
  • User task authorization tidak dites.

Correction

Testing matrix minimum:

AreaTest
Gatewaysetiap condition + default path
Timerdue, not due, escalation, cancel
Messagematch, no match, duplicate, late
Async jobsuccess, exception, retry exhaustion
External taskcomplete, failure, BPMN error
User taskclaim, complete, invalid actor
Variableserialization, missing variable, type mismatch
Migrationactive wait states mapping

34. Anti-Pattern #32 — Process Documentation Separate from BPMN

Bentuk

Runbook, business meaning, SLA, variable contract, and ownership hanya ada di Confluence dan tidak sinkron dengan BPMN.

Symptom

  • Model tidak punya annotations/extension docs.
  • Activity id tidak match runbook.
  • Operator tidak tahu owner activity.
  • Variable contract berubah tanpa update docs.

Correction

Setiap process definition perlu executable + operational metadata:

Process: enforcement-case-main
Owner: Enforcement Platform Team
Business owner: Case Operations
Critical variables: caseId, riskTier, reviewDeadline, regulatoryClockId
Incidents owner: Platform L2 first, Case Ops second
SLA: initial review within 5 business days
Migration policy: wait-state only migration, no active service task migration

Gunakan BPMN documentation element untuk context kecil, dan repo docs untuk detail.


35. Anti-Pattern #33 — One Environment, One Reality

Bentuk

Proses hanya diuji di local H2 atau hanya di dev environment tanpa volume, concurrency, dan real DB behavior.

Symptom

  • Optimistic locking baru muncul di production.
  • Query performance beda drastis.
  • Serialization classpath beda.
  • Timer behavior beda karena timezone/clock.
  • Job executor config tidak sama.

Correction

Pipeline environment minimal:

H2 bagus untuk cepat, bukan bukti production readiness.


36. Anti-Pattern Severity Matrix

Anti-PatternSeverityUsually Found ByFix Cost If Late
God ProcessHighChange request/migrationVery High
God DelegateHighTesting/retry failureHigh
Variable Dumping GroundHighDB growth/security reviewHigh
Sync Remote Call Same TxCriticalIncident/duplicate side effectVery High
BPMN Error for Technical FailureCriticalBusiness outcome auditHigh
Direct DB MutationCriticalInconsistent incident/historyVery High
No Business KeyHighOperations/correlationMedium-High
Signal for Targeted MessageCriticalProduction event bugHigh
Cockpit as BackofficeHighSecurity/audit reviewHigh
Unbounded HistoryHighDB/storage incidentHigh
No RunbookMedium-HighL2 supportMedium
Activity ID GarbageMediumIncident/migrationMedium
Async EverywhereMediumPerformance/backlogMedium
No Versioning StrategyCriticalRelease/migrationVery High

37. Anti-Pattern Detection Checklist

Gunakan checklist ini saat review BPMN/process application.

Modeling Review

  • Apakah setiap gateway punya pertanyaan bisnis eksplisit?
  • Apakah rule kompleks dipindahkan ke DMN/domain service?
  • Apakah activity id stable dan readable?
  • Apakah boundary subprocess/call activity masuk akal?
  • Apakah semua event semantics benar: message vs signal vs error vs escalation?
  • Apakah timer merepresentasikan business wait, bukan technical scheduler?

Data Review

  • Apakah process variable kecil, named, dan version-aware?
  • Apakah tidak ada Java serialized entity long-running?
  • Apakah sensitive data tidak masuk history tanpa alasan?
  • Apakah business status tidak bergantung pada current activity?

Integration Review

  • Apakah remote side effect punya async boundary?
  • Apakah retry idempotent?
  • Apakah duplicate/late/out-of-order event ditangani?
  • Apakah correlation memakai business key/correlation key stabil?

Operation Review

  • Apakah failed job punya runbook?
  • Apakah Cockpit hanya operational tool?
  • Apakah manual modification exceptional, bukan routine?
  • Apakah history TTL dan cleanup jelas?
  • Apakah incident owner jelas?

Versioning Review

  • Apakah change compatible dengan active instances?
  • Apakah migration plan tersedia untuk wait state aktif?
  • Apakah form/DMN/delegate contract backward compatible?
  • Apakah deployment bisa rollback secara operasional?

38. Review Kata-Kunci yang Harus Dihindari

Dalam BPMN/Java/process docs, kata-kata ini sering menandakan smell:

KataKemungkinan Smell
datavariable tidak spesifik
payloaddumping ground
process everythinggod delegate/process
handle errorerror taxonomy tidak jelas
retry manuallyrunbook tidak matang
temporaryakan menjadi permanent debt
just modify in cockpitmissing business flow
call service directlytransaction boundary risk
signal this casemessage/signal confusion
status from activitybusiness state coupling

39. A Practical Refactoring Sequence

Jika sistem Camunda sudah terlanjur memiliki banyak anti-pattern, jangan refactor semua sekaligus. Urutkan berdasarkan blast radius.

Step 1 — Stabilize Incident Handling

Buat runbook untuk top failed jobs. Jangan mulai dengan redesign besar kalau operator masih tidak bisa recover.

Step 2 — Add Business Identity

Pastikan process instance bisa dicari, dikorelasikan, dan diaudit dengan business key dan IDs domain.

Step 3 — Make Retries Safe

Side effect tanpa idempotency adalah bom waktu. Perbaiki sebelum menaikkan retry atau async volume.

Step 4 — Shrink Variable Contract

Kurangi payload besar dan Java object serialization. Ini mengurangi DB pressure dan migration risk.

Step 5 — Extract Logic

Refactor God Delegate menjadi thin delegate + application service.

Step 6 — Modularize Process

Baru setelah logic dan data contract jelas, pecah BPMN menjadi subprocess/call activity/DMN.


40. Mini Case Study: Payment Workflow yang Buruk

Model Buruk

Masalah:

  • Process Payment Delegate melakukan validasi, charge, DB update, email, dan decision.
  • No async boundary.
  • No idempotency.
  • Technical timeout masuk reject.
  • No runbook.
  • Business status dari activity.

Model Lebih Baik

Perbaikan:

  • Validation jelas.
  • Charge punya async boundary.
  • Business decline berbeda dari technical exception.
  • Result event dikorelasikan.
  • Payment command id idempotent.
  • Fulfillment hanya lanjut setelah confirmation.

41. Kaufman Deliberate Practice

Latihan efektif untuk part ini bukan menghafal katalog. Latih kemampuan diagnosis.

Drill 1 — Smell Review

Ambil satu BPMN production/non-production. Tandai:

  • gateway tanpa invariant,
  • activity id buruk,
  • service task tanpa async boundary sebelum remote call,
  • variable besar,
  • missing business key,
  • listener yang menyimpan business logic.

Drill 2 — Failure Story

Untuk setiap service task, tulis:

Jika step ini gagal setelah side effect eksternal berhasil tetapi sebelum Camunda commit, apa yang terjadi?

Jika jawaban tidak jelas, desain belum production-grade.

Drill 3 — Recovery Rehearsal

Simulasikan incident:

  • failed job retry exhausted,
  • external task retries zero,
  • message event late,
  • duplicate event,
  • user completed task dengan data salah,
  • timer escalation salah trigger.

Buat operator action yang aman.


42. Summary

Anti-pattern Camunda 7 hampir selalu berasal dari satu kesalahan mental model: menganggap process engine sebagai framework aplikasi biasa.

Process engine adalah runtime untuk state machine bisnis yang long-running, durable, recoverable, dan operationally visible. Karena itu desain yang baik harus memperlakukan BPMN, variables, delegates, messages, jobs, incidents, history, dan operations sebagai satu sistem.

Prinsip praktis:

  • BPMN untuk orchestration, bukan semua logic.
  • DMN/domain service untuk keputusan dan invariant kompleks.
  • Delegate tipis sebagai adapter.
  • Variable kecil, eksplisit, version-aware.
  • Remote side effect harus idempotent dan transaction-aware.
  • Business status jangan bergantung pada current BPMN activity.
  • Cockpit untuk operasi teknis, bukan business backoffice.
  • Migration/versioning harus dipikirkan sebelum deploy.
  • Incident harus punya runbook, bukan sekadar retry button.

Jika Anda bisa mengenali anti-pattern ini saat design review, Anda sudah berada jauh di atas level “bisa pakai Camunda”. Anda mulai berpikir sebagai engineer yang menjaga workflow platform tetap aman, bisa diaudit, bisa dipulihkan, dan bisa berevolusi.


References

Lesson Recap

You just completed lesson 31 in final stretch. Use the series map if you want to review the broader track, or continue directly into the next lesson while the context is still warm.

Continue The Track

Keep the momentum while the lesson is still fresh. Move backward for review or continue forward into the next concept.