Workflow theo sự kiện vs API request-response cho tác vụ chạy dài
So sánh workflow theo sự kiện và API request-response cho quy trình chạy dài, tập trung vào phê duyệt, timer, thử lại và audit trail trong ứng dụng doanh nghiệp.

Tại sao các quy trình chạy dài lại phức tạp trong ứng dụng kinh doanh
Một quy trình được gọi là “chạy dài” khi nó không thể hoàn thành trong một bước nhanh. Nó có thể kéo dài vài phút, vài giờ hoặc vài ngày vì phụ thuộc vào con người, thời gian, hoặc hệ thống bên ngoài. Bất cứ thứ gì có phê duyệt, chuyển giao, và chờ đợi đều thuộc nhóm này.
Đó là lúc tư duy API request-response đơn giản bắt đầu vỡ. Một cuộc gọi API được thiết kế cho một trao đổi ngắn: gửi yêu cầu, nhận trả lời, rồi tiếp tục. Công việc dài giống như một câu chuyện có nhiều chương. Bạn cần tạm dừng, nhớ chính xác mình đang ở đâu và tiếp tục sau mà không đoán mò.
Bạn gặp tình huống này trong các ứng dụng kinh doanh hàng ngày: phê duyệt mua sắm cần quản lý và tài chính, onboarding nhân viên chờ check giấy tờ, hoàn tiền phụ thuộc nhà cung cấp thanh toán, hoặc yêu cầu truy cập cần được xem xét rồi áp dụng.
Khi đội ngũ đối xử một quy trình dài như một cuộc gọi API đơn, vài vấn đề lặp lại xuất hiện:
- Ứng dụng mất trạng thái sau khi khởi động lại hoặc deploy và không thể tiếp tục đáng tin cậy.
- Thử lại tạo trùng lặp: thanh toán thứ hai, email thứ hai, phê duyệt đôi.
- Quyền sở hữu trở nên mơ hồ: không ai biết người yêu cầu, quản lý hay job hệ thống phải hành động tiếp.
- Hỗ trợ không có tầm nhìn và không thể trả lời “nó đang kẹt ở đâu?” mà không đào log.
- Logic chờ (timer, nhắc nhở, deadline) biến thành script nền mong manh.
Một kịch bản cụ thể: nhân viên xin quyền phần mềm. Quản lý duyệt nhanh, nhưng IT cần hai ngày để cấu hình. Nếu app không giữ trạng thái quy trình, gửi nhắc và resume an toàn, bạn sẽ có follow-up thủ công, người dùng bối rối và công việc phát sinh.
Đó là lý do lựa chọn giữa workflow theo sự kiện và API request-response quan trọng đối với các quy trình kinh doanh chạy dài.
Hai mô hình tư duy: gọi đồng bộ vs sự kiện theo thời gian
So sánh đơn giản nhất là hỏi: công việc có hoàn thành trong lúc người dùng chờ hay tiếp tục sau khi họ rời đi?
API request-response là một trao đổi đơn: một cuộc gọi vào, một phản hồi ra. Nó phù hợp cho công việc hoàn thành nhanh và có thể dự đoán, như tạo bản ghi, tính báo giá, hoặc kiểm kho. Server làm việc, trả về thành công hoặc lỗi, và tương tác kết thúc.
Workflow theo sự kiện là một chuỗi phản ứng theo thời gian. Có một sự kiện xảy ra (đơn hàng được tạo, quản lý phê duyệt, timer hết hạn), và workflow tiến tới bước tiếp theo. Mô hình này phù hợp với công việc có chuyển giao, chờ đợi, thử lại và nhắc nhở.
Khác biệt thực tế là trạng thái.
Với request-response, trạng thái thường sống trong request hiện tại cộng với bộ nhớ server cho tới khi trả lời. Với workflow theo sự kiện, trạng thái phải được lưu (ví dụ trong PostgreSQL) để quy trình có thể resume sau.
Cách xử lý lỗi cũng thay đổi. Request-response thường trả về lỗi và yêu cầu client thử lại. Workflow ghi nhận lỗi và có thể thử lại an toàn khi điều kiện tốt hơn. Chúng cũng có thể ghi lại mỗi bước như một event, giúp lịch sử dễ tái dựng.
Một ví dụ đơn giản: “Gửi báo cáo chi phí” có thể đồng bộ. “Lấy phê duyệt, chờ 3 ngày, nhắc quản lý, rồi thanh toán” thì không thể.
Phê duyệt: mỗi cách xử lý quyết định con người thế nào
Phê duyệt là nơi mà công việc dài trở nên thực tế. Một bước hệ thống hoàn thành trong mili giây, nhưng con người có thể phản hồi trong hai phút hoặc hai ngày. Lựa chọn thiết kế chính là bạn mô hình hoá việc chờ đó như một quy trình tạm dừng, hay như một tin nhắn mới đến sau này.
Với API request-response, phê duyệt thường trở thành một hình thức khó xử:
- Blocking (không thực tế)
- Polling (client hỏi “duyệt chưa?” liên tục)
- Callback/webhook (server gọi lại sau)
Tất cả đều có thể hoạt động, nhưng cần thêm nhiều kết nối chỉ để nối “thời gian con người” với “thời gian API”.
Với sự kiện, phê duyệt đọc như một câu chuyện. Ứng dụng ghi lại một event như “ExpenseSubmitted”, rồi sau đó nhận “ExpenseApproved” hoặc “ExpenseRejected”. Engine workflow (hoặc state machine của bạn) chỉ chuyển bản ghi khi event tiếp theo tới. Điều này khớp với suy nghĩ của đa số: gửi, xem xét, quyết định.
Độ phức tạp hiện ngay khi có nhiều người phê duyệt và quy tắc nâng cấp. Bạn có thể yêu cầu cả quản lý và tài chính phê duyệt, nhưng cho phép quản lý cấp cao ghi đè. Nếu không mô hình hoá rõ, quy trình khó suy luận và khó audit.
Một mô hình phê duyệt đơn giản có thể mở rộng
Một pattern thực tế là giữ một bản ghi “yêu cầu” duy nhất, rồi lưu các quyết định riêng. Cách này cho phép nhiều approver mà không cần viết lại logic lõi.
Ghi vài mẩu dữ liệu làm thực thể hạng nhất:
- Yêu cầu phê duyệt: nội dung cần phê duyệt và trạng thái hiện tại
- Các quyết định cá nhân: ai quyết định, duyệt/từ chối, timestamp, lý do
- Danh sách approver cần thiết: role hoặc người, và thứ tự nếu có
- Quy tắc kết quả: “bất kỳ một”, “đa số”, “tất cả cần thiết”, “cho phép ghi đè”
Bất cứ triển khai nào, luôn lưu ai đã phê duyệt gì, khi nào và vì sao như dữ liệu, không chỉ một dòng log.
Timer và chờ: nhắc nhở, deadline và nâng cấp
Chờ đợi là nơi các tác vụ dài trở nên lộn xộn. Người ta đi ăn trưa, lịch đầy, và “chúng tôi sẽ phản hồi” thành “ai đang sở hữu việc này bây giờ?”. Đây là một trong những khác biệt rõ ràng giữa workflow theo sự kiện và API request-response.
Với request-response, thời gian khó xử. HTTP có timeout, nên bạn không thể giữ request mở vài ngày. Các đội thường dùng polling, job theo lịch quét database, hoặc script thủ công khi chậm. Những cách này hoạt động, nhưng logic chờ nằm ngoài quy trình. Các corner case dễ bị bỏ sót, như job chạy hai lần, hoặc bản ghi thay đổi ngay trước khi nhắc.
Workflow coi thời gian như một bước bình thường. Bạn có thể ghi: chờ 24 giờ, gửi nhắc, rồi chờ đến 48 giờ tổng và nâng cấp cho người quản lý khác. Hệ thống giữ trạng thái, nên deadline không bị ẩn trong một dự án “cron + truy vấn” khác.
Một quy tắc phê duyệt đơn giản có thể đọc như sau:
Sau khi báo cáo chi phí được gửi, chờ 1 ngày. Nếu trạng thái vẫn “Pending”, gửi tin nhắn cho quản lý. Sau 2 ngày, nếu vẫn pending, chuyển cho lead của quản lý và ghi nhận việc nâng cấp.
Chi tiết quan trọng là phải làm gì khi timer chạy nhưng tình huống đã thay đổi. Một workflow tốt luôn kiểm tra lại trạng thái trước khi hành động:
- Tải trạng thái mới nhất
- Xác nhận vẫn đang pending
- Xác nhận người được giao vẫn hợp lệ (đội thay đổi là chuyện thường)
- Ghi lại quyết định và lý do
Thử lại và phục hồi lỗi mà không gây tác động trùng lặp
Retry là khi bạn làm lại vì lỗi ngoài tầm kiểm soát: cổng thanh toán timeout, nhà cung cấp email trả lỗi tạm thời, hoặc app lưu bước A nhưng crash trước bước B. Nguy cơ rất đơn giản: bạn thử lại và vô tình thực hiện hành động hai lần.
Với request-response, pattern thường là client gọi endpoint, chờ, và nếu không nhận thành công rõ ràng thì thử lại. Để an toàn, server cần xử lý các cuộc gọi lặp như cùng một intent.
Một giải pháp thực tế là idempotency key: client gửi token duy nhất như pay:invoice-583:attempt-1. Server lưu kết quả cho key đó và trả về cùng kết quả cho các lần lặp. Điều này ngăn chặn phí dụng kép, ticket trùng, hoặc phê duyệt lặp.
Workflow theo sự kiện có rủi ro trùng lặp khác. Events thường được giao ít nhất một lần (at-least-once), nghĩa là bản sao có thể xuất hiện dù mọi thứ hoạt động. Consumer cần deduplication: lưu event ID (hoặc business key như invoice_id + step) và bỏ qua bản sao. Đây là khác biệt cốt lõi: request-response tập trung vào replay cuộc gọi an toàn, events tập trung vào replay message an toàn.
Một vài quy tắc retry hữu dụng cho cả hai mô hình:
- Dùng backoff (ví dụ 10s, 30s, 2m).
- Đặt giới hạn số lần thử.
- Phân biệt lỗi tạm thời (thử lại) và lỗi vĩnh viễn (fail fast).
- Đưa lỗi lặp vào trạng thái “cần chú ý”.
- Ghi log mọi lần thử để sau này giải thích được chuyện gì xảy ra.
Retry nên được biểu thị rõ trong quy trình, không phải hành vi ẩn. Đó là cách để lỗi trở nên có thể quan sát và sửa chữa.
Audit trail: làm cho quy trình có thể giải thích được
Audit trail là file "tại sao" của bạn. Khi ai đó hỏi “Tại sao báo cáo này bị từ chối?”, bạn nên trả lời mà không đoán mò, thậm chí sau vài tháng. Điều này quan trọng với cả hai mô hình, nhưng cách làm khác nhau.
Với mọi quy trình chạy dài, ghi lại các dữ kiện cho phép bạn phát lại câu chuyện:
- Actor: ai đã thực hiện (user, service, hoặc timer hệ thống)
- Thời gian: khi nào xảy ra (có timezone)
- Input: những gì biết lúc đó (số tiền, nhà cung cấp, ngưỡng chính sách, phê duyệt)
- Output: quyết định hoặc hành động (duyệt, từ chối, trả tiền, thử lại)
- Phiên bản quy tắc: phiên bản chính sách/logic đã dùng
Workflow theo sự kiện có thể làm audit dễ hơn vì mỗi bước tự nhiên tạo event như “ManagerApproved” hoặc “PaymentFailed.” Nếu bạn lưu event cùng payload và actor, bạn có timeline sạch. Chìa khoá là giữ events mô tả đủ rõ và lưu ở nơi có thể truy vấn theo case.
API request-response vẫn có thể audit được, nhưng câu chuyện thường rải rác qua nhiều service. Một endpoint log “approved”, endpoint khác log “payment requested”, endpoint khác log “retry succeeded.” Nếu mỗi cái dùng định dạng khác nhau, audit biến thành việc điều tra tỉ mỉ.
Một sửa đơn giản là dùng “case ID” chung (correlation ID). Gắn identifier này cho mọi request, event và bản ghi DB của instance quy trình, ví dụ EXP-2026-00173. Khi đó bạn có thể truy vết toàn bộ hành trình.
Chọn cách tiếp cận phù hợp: điểm mạnh và đánh đổi
Lựa chọn tốt nhất phụ thuộc vào bạn cần trả lời ngay bây giờ hay cần quy trình tiếp tục hoạt động qua giờ hoặc ngày.
Request-response hoạt động tốt khi công việc ngắn và quy tắc đơn giản. Người dùng gửi form, server validate, lưu dữ liệu và trả thành công hoặc lỗi. Nó cũng phù hợp cho hành động đơn như create, update, hoặc check permission.
Nó bắt đầu trở nên khó chịu khi một “yêu cầu đơn” bí mật thành nhiều bước: chờ phê duyệt, gọi nhiều hệ thống ngoài, xử lý timeout, hoặc nhánh dựa trên kết quả. Bạn hoặc giữ kết nối mở (mong manh), hoặc đẩy chờ và retry vào background job khó suy nghĩ.
Workflow theo sự kiện tỏa sáng khi quy trình là một câu chuyện qua thời gian. Mỗi bước phản ứng với một event mới (duyệt, từ chối, timer fired, thanh toán lỗi) và quyết định bước tiếp theo. Điều này giúp tạm dừng, resume, retry dễ hơn và giữ dấu vết rõ lý do vì sao hệ thống làm vậy.
Có những đánh đổi:
- Đơn giản vs bền vững: request-response bắt đầu đơn giản, event-driven an toàn hơn cho chờ dài.
- Kiểu debug: request-response theo một đường thẳng, workflow cần trace qua nhiều bước.
- Tooling và thói quen: events cần logging tốt, correlation ID và mô hình trạng thái rõ ràng.
- Quản lý thay đổi: workflow thường dễ mở rộng đường đi mới nếu được mô hình tốt.
Ví dụ thực tế: báo cáo chi phí cần quản lý duyệt, rồi tài chính review, rồi thanh toán. Nếu thanh toán lỗi, bạn muốn thử lại mà không trả gấp đôi. Đó là tình huống tự nhiên cho event-driven. Nếu chỉ “gửi báo cáo” với vài kiểm tra nhanh, request-response đủ.
Từng bước: thiết kế quy trình chạy dài sống sót qua chậm trễ
Quy trình dài thường hỏng vì những lý do tẻ nhạt: tab trình duyệt đóng, server khởi động lại, phê duyệt chờ ba ngày, hoặc nhà cung cấp thanh toán timeout. Thiết kế cho những chậm trễ đó ngay từ đầu, bất kể bạn chọn mô hình nào.
Bắt đầu bằng việc định nghĩa một tập trạng thái nhỏ bạn có thể lưu và resume. Nếu bạn không thể chỉ ra trạng thái hiện tại trong DB, bạn chưa thật sự có workflow có thể resume.
Một chuỗi thiết kế đơn giản
- Đặt ranh giới: định nghĩa trigger bắt đầu, điều kiện kết thúc, và vài trạng thái chính (Pending approval, Approved, Rejected, Expired, Completed).
- Đặt tên event và quyết định: viết ra những gì có thể xảy ra theo thời gian (Submitted, Approved, Rejected, TimerFired, RetryScheduled). Dùng tên event ở thì quá khứ.
- Chọn điểm chờ: xác định nơi quy trình tạm dừng chờ con người, hệ thống ngoài, hoặc deadline.
- Thêm luật timer và retry cho từng bước: quyết định gì xảy ra khi thời gian trôi hoặc cuộc gọi lỗi (backoff, max attempts, escalate, give up).
- Định nghĩa cách resume: với mỗi event hoặc callback, tải trạng thái đã lưu, xác nhận còn hợp lệ, rồi chuyển sang trạng thái tiếp theo.
Để sống sót qua restart, persist tối thiểu dữ liệu cần để tiếp tục an toàn. Lưu đủ để chạy lại mà không phải đoán:
- Process instance ID và trạng thái hiện tại
- Ai có thể hành động tiếp (assignee/role) và quyết định của họ
- Deadlines (
due_at,remind_at) và mức nâng cấp - Metadata retry (số lần, lỗi cuối,
next_retry_at) - Idempotency key hoặc flag “đã thực hiện” cho side effect (gửi tin, charge thẻ)
Nếu bạn có thể tái dựng “chúng ta đang ở đâu” và “được phép làm gì tiếp” từ dữ liệu lưu, những chậm trễ sẽ không còn đáng sợ.
Sai lầm phổ biến và cách tránh
Quy trình dài thường vỡ khi có người dùng thực. Một phê duyệt mất hai ngày, retry chạy sai lúc, và bạn có thể gặp thanh toán kép hoặc thiếu audit trail.
Sai lầm phổ biến:
- Giữ một HTTP request mở trong lúc chờ phê duyệt con người. Nó timeout, chiếm tài nguyên server và tạo cảm giác giả rằng “có gì đó đang xảy ra”.
- Thử lại cuộc gọi mà không có idempotency. Lỗi mạng biến thành hoá đơn trùng, email trùng, hoặc chuyển trạng thái “Approved” lặp.
- Không lưu trạng thái quy trình. Nếu trạng thái nằm trong bộ nhớ, restart sẽ xóa nó. Nếu chỉ có trong log, bạn không thể tiếp tục đáng tin cậy.
- Audit trail lộn xộn. Events có đồng hồ và định dạng khác nhau, khiến timeline không đáng tin cậy khi sự cố hoặc review compliance.
- Trộn async và sync mà không có nguồn sự thật duy nhất. Hệ thống này nói "Paid", hệ thống kia nói "Pending" và không ai biết đâu là đúng.
Một ví dụ: báo cáo chi phí được duyệt trong chat, webhook tới trễ, và API thanh toán bị retry. Nếu không có trạng thái lưu và idempotency, retry có thể gửi thanh toán lần hai và hồ sơ không rõ lý do.
Hầu hết sửa lỗi đơn giản là minh bạch:
- Persist các chuyển đổi trạng thái (Requested, Approved, Rejected, Paid) trong DB, kèm ai/điều gì đã thay đổi.
- Dùng idempotency key cho mọi side effect ngoài (payment, email, ticket) và lưu kết quả.
- Tách “chấp nhận yêu cầu” ra khỏi “hoàn thành công việc”: trả về nhanh, sau đó hoàn tất workflow nền.
- Chuẩn hoá timestamp (UTC), thêm correlation ID, và ghi cả request lẫn outcome.
Checklist nhanh trước khi xây
Công việc chạy dài ít về một cuộc gọi hoàn hảo hơn là giữ đúng sau chậm trễ, con người và lỗi.
Viết ra ý nghĩa của “an toàn để tiếp tục” cho quy trình của bạn. Nếu app restart giữa chừng, bạn nên có thể tiếp từ bước biết tới mà không đoán.
Checklist thực tế:
- Xác định cách resume sau crash hoặc deploy. Trạng thái nào được lưu và bước nào chạy tiếp?
- Gán cho mỗi instance một process key duy nhất (ví dụ
ExpenseRequest-10482) và model trạng thái rõ (Submitted, Waiting for Manager, Approved, Paid, Failed). - Xử lý phê duyệt như thực thể: ai phê duyệt/từ chối, khi nào, và lý do/ghi chú.
- Vẽ luật chờ: nhắc nhở, deadline, nâng cấp, hết hạn. Gán owner cho mỗi timer (manager, finance, system).
- Lên kế hoạch xử lý lỗi: retry có giới hạn và an toàn, và có trạng thái “cần xem” để con người sửa dữ liệu hoặc cho phép thử lại.
Bài kiểm tra tỉnh táo: tưởng tượng nhà cung cấp thanh toán timeout sau khi bạn đã charge thẻ. Thiết kế của bạn phải ngăn charge đôi trong khi vẫn cho phép quy trình hoàn thành.
Ví dụ: phê duyệt chi phí với deadline và thử lại thanh toán
Kịch bản: nhân viên gửi hoá đơn taxi 120$. Cần quản lý phê duyệt trong 48 giờ. Nếu duyệt, hệ thống thanh toán cho nhân viên. Nếu thanh toán lỗi, nó thử lại an toàn và để lại bản ghi rõ ràng.
Lướt qua request-response
Với request-response, app thường như một cuộc trò chuyện phải kiểm tra lại. Nhân viên bấm Submit. Server tạo bản ghi reimbursement với trạng thái "Pending approval" và trả về ID. Quản lý nhận thông báo, nhưng app của nhân viên thường phải poll để biết thay đổi, ví dụ: "GET reimbursement status by ID."
Để thực thi 48 giờ, bạn phải chạy một job định kỳ quét các yêu cầu quá hạn, hoặc lưu timestamp deadline và kiểm tra khi poll. Nếu job chạy chậm, người dùng thấy trạng thái lỗi thời.
Khi quản lý duyệt, server đổi trạng thái thành "Approved" và gọi nhà cung cấp thanh toán. Nếu Stripe trả lỗi tạm thời, server phải quyết định thử lại ngay, thử lại sau, hay fail. Nếu không cẩn thận với idempotency key, retry có thể tạo payout kép.
Lướt qua event-driven
Trong mô hình theo sự kiện, mỗi thay đổi là một sự kiện được ghi lại.
Nhân viên gửi, tạo event “ExpenseSubmitted”. Một workflow bắt đầu và chờ hoặc “ManagerApproved” hoặc timer “DeadlineReached” sau 48 giờ. Nếu timer chạy trước, workflow ghi “AutoRejected” và lý do.
Khi duyệt, workflow ghi “PayoutRequested” và thử thanh toán. Nếu Stripe timeout, nó ghi “PayoutFailed” với mã lỗi, lên lịch thử lại (ví dụ 15 phút), và chỉ ghi “PayoutSucceeded” một lần bằng idempotency key.
Giao diện người dùng vẫn đơn giản:
- Pending approval (còn 48 giờ)
- Approved, đang thanh toán
- Lịch thử lại thanh toán đã được lập
- Đã trả tiền
Audit trail đọc như một timeline: submitted, approved, kiểm tra deadline, cố gắng payout, thất bại, thử lại, thành công.
Bước tiếp theo: biến mô hình thành app hoạt động
Chọn một quy trình thực và xây end-to-end trước khi khái quát hoá. Phê duyệt chi phí, onboarding, và xử lý hoàn tiền là lựa chọn tốt vì chúng bao gồm bước con người, chờ và các đường lỗi. Giữ mục tiêu nhỏ: con đường chính hoạt động và hai ngoại lệ phổ biến nhất.
Viết quy trình dưới dạng trạng thái và event, không phải màn hình. Ví dụ: "Submitted" -> "ManagerApproved" -> "PaymentRequested" -> "Paid", với nhánh như "ApprovalRejected" hoặc "PaymentFailed." Khi thấy rõ điểm chờ và side effect, việc chọn giữa workflow theo sự kiện và API request-response trở nên thực tế.
Quyết định nơi lưu trạng thái quy trình. Database có thể đủ nếu flow đơn giản và bạn có thể đảm bảo cập nhật ở một nơi. Workflow engine hữu ích khi bạn cần timer, retry và phân nhánh, vì nó theo dõi bước tiếp theo.
Thêm trường audit ngay từ đầu. Lưu ai làm gì, khi nào và vì sao (comment hoặc mã lý do). Khi ai đó hỏi "Tại sao thanh toán này bị thử lại?" bạn cần câu trả lời rõ ràng mà không phải lục log.
Nếu bạn xây trên nền tảng no-code, AppMaster (appmaster.io) là một tùy chọn cho phép mô hình dữ liệu trong PostgreSQL và xây logic quy trình bằng giao diện trực quan, giúp phê duyệt và audit nhất quán trên web và mobile.
Câu hỏi thường gặp
Sử dụng request-response khi công việc kết thúc nhanh và dự đoán được trong lúc người dùng chờ, ví dụ tạo bản ghi hoặc kiểm tra biểu mẫu. Dùng workflow theo sự kiện khi quy trình kéo dài từ vài phút đến vài ngày, bao gồm phê duyệt con người, hoặc cần timer, thử lại và resume an toàn sau khi khởi động lại.
Các tác vụ dài không phù hợp với một HTTP request vì kết nối sẽ timeout, server có thể khởi động lại, và công việc thường phụ thuộc vào người hoặc hệ thống ngoài. Nếu bạn xử lý như một cuộc gọi duy nhất, thường sẽ mất trạng thái, sinh trùng lặp khi thử lại và phải dùng nhiều script nền rời rạc để xử lý chờ.
Một giá trị mặc định tốt là lưu trạng thái quy trình rõ ràng trong cơ sở dữ liệu và chỉ tiến trạng thông qua các chuyển đổi tường minh. Lưu process instance ID, trạng thái hiện tại, ai có thể hành động tiếp, và các mốc thời gian chính để có thể resume an toàn sau deploy, crash hoặc chậm trễ.
Mô hình phê duyệt tốt là coi đó là một bước tạm dừng và resume khi có quyết định, thay vì block hoặc poll liên tục. Ghi lại từng quyết định như dữ liệu (ai quyết định, khi nào, duyệt/từ chối và lý do) để workflow tiến lên một cách dự đoán được và có thể audit sau này.
Polling có thể chấp nhận cho các trường hợp đơn giản, nhưng tạo ra tiếng ồn và độ trễ vì client phải hỏi liên tục “xong chưa?”. Một lựa chọn tốt hơn là đẩy thông báo khi có thay đổi và để client làm mới khi cần, trong khi server là nguồn chân thực duy nhất của trạng thái.
Đối xử với thời gian như một phần của quy trình bằng cách lưu deadlines và thời điểm nhắc, rồi kiểm tra lại trạng thái hiện tại khi timer kích hoạt trước khi hành động. Điều này tránh gửi nhắc sau khi đã được phê duyệt và giữ phép nâng cấp nhất quán ngay cả khi job chạy trễ hoặc chạy hai lần.
Bắt đầu với idempotency key cho mọi side effect như charge thẻ hoặc gửi email, và lưu kết quả cho key đó. Khi retry xảy ra, lặp lại cùng intent với cùng key sẽ trả về cùng kết quả thay vì thực hiện hành động lần nữa.
Giả định rằng message có thể được giao nhiều lần và thiết kế consumer để loại trùng. Một cách thực tế là lưu event ID (hoặc business key cho bước đó) và bỏ qua các bản sao để replay không kích hoạt hành động giống nhau hai lần.
Thu thập một timeline các sự kiện: actor, timestamp, input tại thời điểm đó, kết quả, và phiên bản luật/logic đã dùng. Gán một case ID duy nhất (correlation ID) cho mọi thứ liên quan tới quy trình để support có thể trả lời “nó đang kẹt ở đâu?” mà không phải lục hết log rời rạc.
Giữ một bản ghi yêu cầu làm 'case', lưu các quyết định riêng biệt và điều khiển thay đổi trạng thái thông qua các transition được lưu trữ có thể replay. Trong công cụ no-code như AppMaster (appmaster.io), bạn có thể mô hình dữ liệu trong PostgreSQL và thực hiện logic bước bằng giao diện trực quan, giúp phê duyệt, retry và trường audit nhất quán trên web và mobile.


