# Membangun Workflow Engine untuk Persetujuan Multi-Langkah: NestJS, PostgreSQL, dan React

> Pembahasan mendalam tentang membangun workflow engine state-machine yang dapat digunakan kembali untuk menggerakkan permintaan cuti, voucher AP, dan invoice AR dalam ERP kustom.

URL: https://www.matthewswong.com/id/blog/workflow-engine-react-nestjs-postgresql/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2025-06-10
- Updated: 2026-10-17
- Category: ERP

Setiap proses bisnis yang melibatkan persetujuan — permintaan cuti, purchase order, voucher pembayaran — pada dasarnya adalah state machine. Dalam pekerjaan konsultasi ERP lepas saya, ketika saya membangun ERP kustom untuk klien, saya mengekstrak workflow engine generik yang menggerakkan semua alur persetujuan dalam sistem.

## Mengapa Workflow Engine Generik?

Alternatif dari engine generik adalah menulis logika persetujuan kustom untuk setiap proses bisnis. Engine generik berarti Anda memperbarui tabel transisi sekali, dan semua proses yang berbagi konfigurasi itu segera mendapat manfaat.

### Prinsip Desain State Machine

State machine untuk alur kerja bisnis membutuhkan empat hal: sekumpulan state yang terbatas, sekumpulan aksi yang terbatas, tabel yang memetakan (from_state, action, required_role) → to_state, dan cara untuk menolak transisi yang tidak valid.

### Riwayat JSONB untuk Audit Trail Lengkap

Setiap transisi state ditambahkan ke kolom JSONB bernama 'history' di tabel workflow_instances. Ini memberikan catatan lengkap dan terurut tentang siapa yang melakukan apa dan kapan — tanpa memerlukan tabel riwayat terpisah.

> Gunakan gen_random_uuid() PostgreSQL untuk ID instance workflow, dan simpan entity_type + entity_id secara terpisah. Ini membuat workflow engine benar-benar generik — WorkflowService yang sama menggerakkan permintaan cuti, voucher AP, dan proses masa depan apa pun.

## Layanan Workflow NestJS

WorkflowService memiliki satu metode publik: transition(instanceId, action, actorRole, comment). Ia memuat instance workflow, menemukan transisi yang cocok, memvalidasi peran aktor, menerapkan perubahan state, menambahkan ke riwayat JSONB, dan memancarkan event domain untuk handler downstream.

### UI React untuk Visualisasi Workflow

Komponen React untuk state workflow memiliki dua bagian: lencana status dengan kode warna, dan timeline yang dapat dilipat menampilkan riwayat transisi lengkap dengan aktor, aksi, cap waktu, dan komentar opsional.

## Menangani Kasus Tepi: Revisi dan Pembatalan

Alur persetujuan dunia nyata memiliki dua kasus tepi: permintaan revisi dan pembatalan. Keduanya membutuhkan state mereka sendiri — REVISION dan CANCELLED. State REVISION memungkinkan pengirim asli untuk memperbarui dokumen dan mengirim ulang.

> **Peringatan Kompleksitas Konfigurasi**

> Jangan mencoba membuat workflow engine berbasis konfigurasi dari UI database di versi pertama Anda. Mulai dengan tabel transisi TypeScript yang dikodekan keras, buat engine bekerja dengan andal, kemudian tambahkan fitur konfigurasi hanya ketika pengguna benar-benar memintanya.

## Pengujian Logika Workflow

Workflow engine adalah jantung ERP — bug di sini mempengaruhi setiap proses persetujuan dalam sistem. Tulis unit test yang komprehensif untuk setiap transisi yang valid dan tidak valid. Gunakan test.each Jest untuk mencakup semua transisi tanpa kode test yang berulang.

## Pelajaran Produksi: Apa yang Akan Saya Lakukan Berbeda

Setelah 12 bulan di produksi: gunakan optimistic locking pada workflow_instances untuk mencegah konflik transisi bersamaan; simpan identitas aktor lengkap dalam riwayat JSONB; dan tambahkan fitur 'delegasi' lebih awal untuk manajer yang sedang cuti.

## Sumber & Bacaan Lebih Lanjut

- NestJS Docs — CQRS and Event-Driven Architecture — https://docs.nestjs.com/recipes/cqrs
- PostgreSQL Docs — JSON Types and JSONB Operations — https://www.postgresql.org/docs/current/datatype-json.html
- XState — State Machine and Statechart Library Documentation — https://xstate.js.org/docs/

## Source links

- https://docs.nestjs.com/recipes/cqrs
- https://www.postgresql.org/docs/current/datatype-json.html
- https://xstate.js.org/docs/

## FAQ

### Mengapa membangun workflow engine generik daripada menulis logika persetujuan kustom per fitur?

Menulis logika persetujuan kustom per proses bisnis memang praktis di awal, tetapi menjadi masalah pemeliharaan ketika kebutuhan berubah — misalnya, menambahkan level persetujuan kedua untuk nominal tertentu berarti harus menyentuh setiap alur persetujuan secara terpisah. Engine generik berarti Anda memperbarui tabel transisi sekali, dan semua proses yang berbagi konfigurasi itu langsung mendapat manfaat. Dalam sebuah ERP, misalnya, WorkflowService yang sama dapat menggerakkan permintaan cuti, voucher AP, maupun invoice AR.

### Bagaimana audit trail lengkap disimpan tanpa tabel riwayat terpisah?

Setiap transisi state ditambahkan ke kolom JSONB bernama 'history' di tabel workflow_instances, sehingga tersedia catatan lengkap dan terurut tentang siapa melakukan apa dan kapan. Operator JSONB PostgreSQL memudahkan kueri terhadap riwayat ini, dan pendekatan ini cukup cepat untuk rantai persetujuan tipikal sebanyak 5–15 langkah. Instance workflow juga menyimpan entity_type dan entity_id secara terpisah agar engine tetap benar-benar generik lintas jenis dokumen.

### Bagaimana WorkflowService NestJS tetap terpisah dari logika bisnis?

WorkflowService hanya memiliki satu metode publik — transition(instanceId, action, actorRole, comment) — yang memuat instance, menemukan transisi yang cocok, memvalidasi peran aktor, menerapkan perubahan state, dan menambahkan ke riwayat JSONB. Alih-alih memanggil logika bisnis secara langsung, ia memancarkan domain event, dan handler downstream mendengarkan event state workflow tertentu menggunakan EventEmitter2 dari NestJS. Pemisahan berbasis event ini membuat engine tidak perlu mengetahui apa pun tentang kebijakan cuti, aturan invoice, atau domain bisnis lainnya.

### Bagaimana pendekatan yang direkomendasikan untuk menguji workflow engine?

Karena workflow engine memengaruhi setiap proses persetujuan dalam sistem, artikel ini merekomendasikan penulisan unit test yang komprehensif untuk setiap transisi yang valid maupun tidak valid. Untuk setiap transisi, Anda perlu memverifikasi bahwa peran yang benar dapat melakukannya, bahwa peran yang salah ditolak dengan ForbiddenException, dan bahwa riwayat tercatat dengan benar. Gunakan test.each dari Jest untuk mencakup semua transisi tanpa kode test yang berulang.

### Apa pelajaran produksi terpenting setelah 12 bulan menjalankan engine ini?

Tiga peningkatan yang paling menonjol: pertama, tambahkan optimistic locking melalui kolom versi pada workflow_instances untuk mencegah konflik transisi bersamaan; kedua, simpan identitas aktor secara lengkap di riwayat JSONB daripada hanya string peran; ketiga, tambahkan fitur delegasi lebih awal agar manajer yang sedang cuti dapat melimpahkan wewenang persetujuan kepada wakilnya. Artikel ini juga memperingatkan agar tidak langsung membuat engine berbasis konfigurasi dari UI database di versi pertama — mulailah dengan tabel transisi TypeScript yang dikodekan keras hingga pengguna benar-benar memintanya.

---

- HTML version: https://www.matthewswong.com/id/blog/workflow-engine-react-nestjs-postgresql/
- English: https://www.matthewswong.com/en/blog/workflow-engine-react-nestjs-postgresql/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
