Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 21 — Boundary Events — Interrupting vs Non-interrupting

Business Process Mapping and BPMN Bài 21/60

Mở đầu — vì sao bài này quan trọng

Hãy tưởng tượng bạn đang mô hình hóa quy trình "Xử lý đơn hàng" cho một sàn thương mại điện tử. Bạn vẽ một task "Chờ khách thanh toán". Câu hỏi đặt ra ngay lập tức: nếu sau 30 phút khách vẫn chưa thanh toán thì sao? Nếu trong lúc chờ, hệ thống thanh toán báo lỗi thì sao? Nếu khách bấm "Hủy đơn" giữa chừng thì sao?

Những câu hỏi "nếu... thì sao" này chính là phần khó nhất — và cũng là phần giá trị nhất — khi mô hình hóa một quy trình thật. Một sơ đồ chỉ vẽ "đường đi đẹp" (happy path) thì ai cũng vẽ được. Nhưng quy trình thật luôn có gián đoạn: timeout, lỗi kỹ thuật, yêu cầu hủy, tin nhắn đến giữa chừng. Trong BPMN 2.0, công cụ chuyên dụng để mô tả chính xác những gián đoạn này là Boundary Event — sự kiện bám biên.

Đây là một trong những phần tử mạnh nhất và cũng dễ vẽ sai nhất của BPMN. Nắm vững Boundary Event, đặc biệt là sự khác nhau giữa Interrupting (gián đoạn) và Non-interrupting (không gián đoạn), bạn sẽ phân biệt được một Business Process Analyst nghiệp dư với một người thật sự hiểu cách quy trình "chạy". Ở các bài sau (Bài 22 — Error handling, Bài 42 — Escalation pattern), bạn sẽ thấy Boundary Event xuất hiện liên tục như một nền tảng. Vậy nên bài này là viên gạch không thể bỏ qua.

Khái niệm cốt lõi

Boundary Event là gì

Boundary Event là một sự kiện trung gian (Intermediate Event) được đặt bám vào đường biên của một Activity — một Task hoặc một Sub-process. Về mặt hình ảnh, nó là một vòng tròn nhỏ nằm "dính" trên cạnh của hình chữ nhật bo góc biểu diễn activity.

Ý nghĩa của nó rất cụ thể: sự kiện này chỉ "lắng nghe" trong khoảng thời gian activity đó đang chạy (active). Nếu trong lúc activity đang thực thi mà sự kiện được kích hoạt (timer hết giờ, error được ném ra, message đến...), thì luồng sẽ rẽ theo nhánh xuất phát từ boundary event đó. Khi activity kết thúc bình thường, boundary event cũng "tắt" — nó không còn lắng nghe nữa.

Hãy ghi nhớ điểm mấu chốt này: Boundary Event luôn là Catch Event (sự kiện bắt), không bao giờ là Throw. Nó ngồi đó chờ một điều gì đó xảy đến với activity mà nó bám vào.

Interrupting — đường viền liền (nét đậm)

Boundary Event kiểu Interrupting được vẽ bằng đường viền nét liền (single solid line). Khi sự kiện này được kích hoạt:

  • Activity đang chạy bị hủy ngay lập tức (cancelled). Token đang nằm trong activity bị rút ra.
  • Token được chuyển sang nhánh xuất phát từ boundary event.
Nói cách khác, Interrupting nghĩa là "dừng việc đang làm, bỏ dở nó, đi sang hướng khác". Activity gốc không hoàn thành.

Ví dụ kinh điển: task "Chờ khách xác nhận đơn" có một boundary timer event interrupting đặt 24 giờ. Sau 24 giờ chưa xác nhận → hủy task chờ → tự động hủy đơn hàng. Việc chờ bị bỏ dở hoàn toàn.

Non-interrupting — đường viền nét đứt (gạch nối)

Boundary Event kiểu Non-interrupting được vẽ bằng đường viền nét đứt (dashed/double dashed line). Khi sự kiện này được kích hoạt:

  • Activity gốc vẫn tiếp tục chạy bình thường, không bị động đến.
  • Một token mới được sinh ra và đi theo nhánh xuất phát từ boundary event đó.
Nói cách khác, Non-interrupting nghĩa là "cứ làm tiếp việc đang làm, nhưng đồng thời kích hoạt thêm một hành động phụ song song". Quy trình phân nhánh ra một luồng phụ mà không phá luồng chính.

Ví dụ kinh điển: task "Xử lý khiếu nại của khách" có boundary timer event non-interrupting đặt 48 giờ. Cứ mỗi 48 giờ chưa xong → gửi email nhắc nhở cho trưởng nhóm (escalation), nhưng nhân viên vẫn tiếp tục xử lý khiếu nại. Việc nhắc nhở chạy song song, không làm gián đoạn việc xử lý.

Cách phân biệt nhanh trên sơ đồ

Đặc điểmInterruptingNon-interrupting
Đường viềnNét liền (solid)Nét đứt (dashed)
Activity gốcBị hủyVẫn chạy tiếp
TokenChuyển token cũ sang nhánh mớiSinh token mới, song song
Ý nghĩa"Bỏ dở, đổi hướng""Làm thêm việc phụ"
Mẹo ghi nhớ: nét đứt = không cắt đứt activity gốc. Nghe có vẻ nghịch lý nhưng nó giúp bạn nhớ rất lâu — đường viền bị "đứt" thì activity lại không bị "đứt".

Những loại sự kiện nào có thể làm Boundary Event

Không phải sự kiện nào cũng đặt lên biên được. Các loại trigger phổ biến đặt được lên boundary:

  • Timer — hết một khoảng thời gian hoặc đến một thời điểm. Cả interrupting và non-interrupting.
  • Message — nhận được tin nhắn từ bên ngoài (khách hủy đơn, đối tác báo hủy...). Cả hai kiểu.
  • Error — activity (thường là sub-process) ném ra lỗi. Chỉ interrupting — không thể non-interrupting, vì đã lỗi thì không thể "vừa lỗi vừa chạy tiếp".
  • Escalation — leo thang. Cả hai kiểu.
  • Signal — tín hiệu broadcast. Cả hai kiểu.
  • Conditional — một điều kiện dữ liệu trở thành đúng. Cả hai kiểu.
  • Compensation — bù trừ/hoàn tác (sẽ học kỹ ở Bài 23). Đặc biệt, không phân interrupting/non-interrupting theo nghĩa thông thường.
  • Cancel — chỉ dùng cho transaction sub-process, luôn interrupting.
Bạn không cần học thuộc bảng này ngay, nhưng cần nhớ một điều: Error boundary event luôn là interrupting. Đây là câu hỏi rất hay gặp khi phỏng vấn và cũng là lỗi sai phổ biến.

Tình huống thực tế

Tình huống 1 — Tiki: timeout thanh toán đơn hàng (Interrupting)

Bối cảnh: Một sàn thương mại điện tử lớn như Tiki xử lý hàng trăm nghìn đơn mỗi ngày. Khi khách chọn phương thức "Chuyển khoản ngân hàng", hệ thống tạo một mã đơn và chờ tiền về. Nhưng kho hàng đã được giữ (reserve) cho đơn đó, nên không thể chờ vô hạn — nếu chờ quá lâu, hàng bị khóa ảo, khách khác không mua được.

Mô hình hóa: Có một task "Chờ xác nhận thanh toán". Trên biên của task này, đội phân tích đặt một Boundary Timer Event interrupting với thời lượng PT15M (15 phút). Diễn giải:

  • Nếu trong vòng 15 phút tiền về → task hoàn thành bình thường → luồng đi tiếp tới "Đóng gói & giao hàng".
  • Nếu hết 15 phút mà tiền chưa về → timer interrupting kích hoạt → task "Chờ xác nhận thanh toán" bị hủy → token đi theo nhánh timer tới "Hủy đơn & nhả kho".
Bài học rút ra: dùng interrupting vì khi đã timeout thì việc chờ trở nên vô nghĩa — không có lý do gì để vừa hủy đơn vừa tiếp tục chờ. Đây là dấu hiệu kinh điển của interrupting: hai nhánh loại trừ nhau, không thể cùng tồn tại.

Tình huống 2 — Ngân hàng số tại Việt Nam: nhắc nhở phê duyệt khoản vay (Non-interrupting)

Bối cảnh: Một ngân hàng số (giả định mô hình tương tự các nhà băng đang số hóa mạnh tại Việt Nam) có quy trình "Thẩm định hồ sơ vay tín chấp". Một chuyên viên thẩm định nhận hồ sơ và có nhiệm vụ xử lý. Theo cam kết dịch vụ (SLA), hồ sơ nên xong trong 4 giờ làm việc. Nhưng nếu vượt quá, ngân hàng vẫn muốn chuyên viên hoàn thành đúng hồ sơ đó — không được vứt bỏ hồ sơ đang dở.

Mô hình hóa: Task "Thẩm định hồ sơ vay" có một Boundary Timer Event non-interrupting đặt PT4H. Diễn giải:

  • Chuyên viên cứ tiếp tục thẩm định bình thường.
  • Khi đủ 4 giờ mà task chưa kết thúc → timer non-interrupting kích hoạt → sinh một token mới đi tới task "Gửi cảnh báo SLA cho Trưởng phòng thẩm định". Trong khi đó, task thẩm định vẫn đang chạy.
Một biến thể hay gặp: dùng boundary timer non-interrupting có chu kỳ lặp (cycle), ví dụ R/PT4H — cứ mỗi 4 giờ lại nhắc một lần cho đến khi task xong. Đây chính là pattern escalation mà ta sẽ đào sâu ở Bài 42.

Bài học rút ra: dùng non-interrupting vì hai việc "thẩm định" và "nhắc nhở cấp trên" cần xảy ra song song. Hủy việc thẩm định chỉ vì trễ giờ là điều vô lý với nghiệp vụ.

Tình huống 3 — Grab: khách hủy chuyến giữa lúc đang tìm tài xế (Interrupting Message)

Bối cảnh: Trên một nền tảng gọi xe như Grab, khi khách đặt chuyến, hệ thống bước vào trạng thái "Tìm tài xế phù hợp" — quét các tài xế gần đó, gửi yêu cầu, chờ chấp nhận. Quá trình này có thể kéo dài vài chục giây đến vài phút. Trong lúc đó, khách hoàn toàn có thể bấm nút "Hủy chuyến".

Mô hình hóa: Task "Tìm tài xế phù hợp" có một Boundary Message Event interrupting lắng nghe message "Khách hủy chuyến". Diễn giải:

  • Bình thường: tìm được tài xế → task hoàn thành → đi tới "Bắt đầu chuyến đi".
  • Nếu khách bấm hủy: message đến → boundary message interrupting kích hoạt → task "Tìm tài xế" bị hủy ngay → đi tới "Hoàn tất hủy chuyến & thông báo".
Bài học rút ra: đây là minh họa rằng boundary event không chỉ là timer. Message boundary event cho phép quy trình phản ứng với hành động bên ngoài (từ khách hàng, từ hệ thống đối tác) ngay giữa lúc một activity đang chạy. Và vì khách đã hủy thì việc tìm tài xế phải dừng — nên đây là interrupting.

Hướng dẫn từng bước

Khi bạn cần quyết định có dùng Boundary Event hay không, và dùng loại nào, hãy đi theo trình tự sau:

Bước 1 — Xác định activity có nguy cơ bị gián đoạn. Nhìn vào từng task/sub-process và tự hỏi: "Trong lúc việc này đang chạy, có điều gì bên ngoài có thể xảy đến và buộc quy trình phải phản ứng không?" Các activity "chờ" (chờ thanh toán, chờ phê duyệt, chờ phản hồi) là ứng viên hàng đầu.

Bước 2 — Liệt kê các "biến cố" có thể xảy ra. Hết giờ? Lỗi kỹ thuật? Tin nhắn hủy từ khách? Một điều kiện dữ liệu thay đổi? Mỗi biến cố sẽ tương ứng một loại trigger (timer/message/error/conditional...).

Bước 3 — Với mỗi biến cố, trả lời câu hỏi vàng: "Khi biến cố này xảy ra, activity gốc còn cần chạy tiếp nữa không?"

  • Nếu KHÔNG (việc đang làm trở nên vô nghĩa hoặc phải bỏ) → dùng Interrupting (nét liền).
  • Nếu (chỉ cần làm thêm việc phụ song song) → dùng Non-interrupting (nét đứt).
Bước 4 — Vẽ boundary event đúng loại đường viền lên biên activity, rồi kéo một sequence flow từ nó tới nhánh xử lý tương ứng.

Bước 5 — Đặt thuộc tính chi tiết. Với timer: là duration (PT15M), thời điểm cụ thể (date), hay chu kỳ lặp (R3/PT1H)? Với message/signal: tên message là gì, ai gửi? Với conditional: biểu thức điều kiện là gì?

Bước 6 — Kiểm tra lại bằng "token test". Tưởng tượng một token đi vào activity. Khi boundary event nổ, token đi đâu? Activity gốc còn token không? Nếu là non-interrupting, bạn phải thấy được hai token song song. Nếu là interrupting, chỉ còn một token ở nhánh mới.

Lỗi thường gặp & mẹo

Lỗi 1 — Vẽ nhầm interrupting thành non-interrupting (và ngược lại). Đây là lỗi số một. Người mới hay vẽ tất cả thành nét liền vì... đẹp hơn. Hậu quả: một timeout nhắc nhở vô tình hủy luôn task đang chạy. Hãy luôn áp dụng "câu hỏi vàng" ở Bước 3 trước khi chọn kiểu đường viền.

Lỗi 2 — Dùng Error boundary non-interrupting. Sai về mặt chuẩn BPMN. Error luôn interrupting, vì lỗi đồng nghĩa với việc activity không thể hoàn thành. Công cụ tốt (Camunda, Bizagi) sẽ không cho bạn vẽ kiểu này.

Lỗi 3 — Quên rằng boundary event chỉ lắng nghe khi activity đang active. Nếu activity đã hoàn thành thì boundary event không còn tác dụng. Đừng kỳ vọng một boundary timer bắt được sự kiện xảy ra sau khi task kết thúc — lúc đó dùng một event riêng trên luồng, không phải boundary.

Lỗi 4 — Lạm dụng boundary event thay cho Gateway. Boundary event dùng cho sự kiện xảy ra trong khi activity chạy. Còn việc rẽ nhánh dựa trên kết quả sau khi activity xong là việc của Exclusive Gateway (Bài 15). Đừng nhầm hai thứ này.

Mẹo 1 — Dùng boundary event trên Sub-process để "bọc" cả một nhóm task. Thay vì gắn timer cho từng task nhỏ, hãy gom chúng vào một sub-process rồi đặt một boundary timer lên biên sub-process. Khi timer nổ, toàn bộ sub-process bị hủy. Cách này gọn và mạnh hơn nhiều.

Mẹo 2 — Đặt tên cho nhánh boundary thật rõ. Ví dụ "Hết 15 phút", "Khách hủy", "SLA quá 4 giờ". Người đọc sơ đồ hiểu ngay điều kiện kích hoạt mà không cần mở thuộc tính kỹ thuật.

Mẹo 3 — Non-interrupting hầu như luôn đi kèm pattern escalation. Mỗi khi bạn thấy "nhắc nhở", "cảnh báo cấp trên", "gửi thông báo song song" — gần như chắc chắn đó là non-interrupting.

Bài tập thực hành

Bài tập 1 — Phân loại. Với mỗi tình huống sau, hãy quyết định dùng boundary event Interrupting hay Non-interrupting, và loại trigger nào:

  • a) Task "Chờ giám đốc duyệt ngân sách". Sau 2 ngày chưa duyệt → gửi nhắc nhở nhưng vẫn chờ tiếp.
  • b) Task "Gọi API cổng thanh toán". Nếu API trả về lỗi → chuyển sang quy trình xử lý lỗi.
  • c) Task "Chuẩn bị món ăn" tại một chuỗi nhà hàng. Nếu khách bấm hủy đơn trên app → dừng chuẩn bị.
Bài tập 2 — Vẽ sơ đồ. Hãy mô hình hóa (vẽ tay hoặc dùng draw.io/Bizagi) quy trình sau cho một phòng khám đặt lịch online: Task "Chờ bệnh nhân xác nhận lịch hẹn" có hai boundary event cùng lúc: (1) một timer interrupting 24 giờ → hủy lịch và nhả slot; (2) một message non-interrupting "Bệnh nhân đổi giờ" → cập nhật giờ hẹn nhưng vẫn tiếp tục chờ xác nhận. Vẽ rõ hai loại đường viền.

Bài tập 3 — Phản biện. Một đồng nghiệp gửi bạn sơ đồ trong đó task "Xử lý hoàn tiền" có một boundary timer interrupting đặt 1 giờ để "nhắc kế toán xử lý nhanh". Hãy chỉ ra sai sót và đề xuất sửa.

(Gợi ý đáp án: BT1 → a) Non-interrupting, Timer; b) Interrupting, Error; c) Interrupting, Message. BT3 → Sai vì interrupting sẽ hủy luôn task hoàn tiền sau 1 giờ thay vì chỉ nhắc; phải đổi sang non-interrupting.)

Tóm tắt

Boundary Event là sự kiện bám vào biên của một activity, chỉ lắng nghe trong lúc activity đó đang chạy, và luôn là catch event. Có hai kiểu mà bạn phải phân biệt rạch ròi:

  • Interrupting (nét liền): hủy activity gốc, chuyển token sang nhánh mới. Dùng khi việc đang làm trở nên vô nghĩa — timeout hủy đơn, lỗi API, khách hủy chuyến. Lưu ý Error luôn interrupting.
  • Non-interrupting (nét đứt): activity gốc chạy tiếp, sinh thêm token song song. Dùng cho các việc phụ chạy đồng thời — nhắc nhở SLA, escalation, thông báo. Mẹo nhớ: "nét đứt = không cắt đứt".
Câu hỏi vàng để chọn đúng: "Khi sự kiện xảy ra, activity gốc còn cần chạy tiếp không?" Nếu không → interrupting; nếu có → non-interrupting. Cùng với "token test", hai công cụ này đủ để bạn vẽ chính xác mọi tình huống gián đoạn trong quy trình thật. Ở Bài 22, ta sẽ dùng chính nền tảng này để đào sâu cơ chế Throw và Catch Error.