Menu
ESC

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

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

Đang tải...

Bài 19 — Data Objects và Data Store

Business Process Mapping and BPMN Bài 19/60

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

Hãy hình dung bạn vẽ một quy trình "Xử lý đơn hàng" rất đẹp: có Pool, có Lane, có Task, có Gateway rẽ nhánh hợp lý. Sếp nhìn vào gật gù. Nhưng rồi một câu hỏi tưởng đơn giản lại làm cả phòng đứng hình: "Đến bước duyệt tín dụng, nhân viên dựa vào tài liệu nào để duyệt? Tờ đơn hàng gốc hay bản đã được kế toán chỉnh sửa? Và sau khi duyệt xong, thông tin đó được lưu ở đâu để bộ phận giao hàng đọc lại?"

Đây chính là khoảng trống mà rất nhiều người vẽ BPMN bỏ qua. Họ mô tả được các hoạt động diễn ra theo trình tự nào, nhưng không mô tả được dữ liệu chảy qua quy trình ra sao. Mà trong thực tế, phần lớn lỗi vận hành — duyệt nhầm, giao nhầm, mất chứng từ, không truy vết được — đều bắt nguồn từ việc dữ liệu không rõ ràng, chứ không phải vì trình tự sai.

BPMN 2.0 có hẳn một nhóm ký hiệu dành riêng cho dữ liệu: Data Object, Data Input/Output, Data Store, và Data Association. Đây là phần mà ở Bài 14 (Activities) và Bài 18 (Sequence Flow vs Message Flow) chúng ta cố tình chưa đụng tới, để dành riêng cho hôm nay. Khi bạn nắm vững nhóm ký hiệu này, sơ đồ quy trình của bạn không còn là một bức tranh tĩnh về "ai làm gì", mà trở thành một mô tả sống động về "thông tin đi từ đâu đến đâu, được tạo ra lúc nào, lưu ở đâu, và ai cần nó". Đó là sự khác biệt giữa một quy trình trông có vẻ đúng và một quy trình thực sự chạy được.

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

Data Object — dữ liệu trong phạm vi một process instance

Data Object được vẽ bằng hình tờ giấy gấp góc trên bên phải (giống biểu tượng "tài liệu" quen thuộc). Nó biểu diễn một mẩu dữ liệu — thường là một tài liệu hoặc một bản ghi — mà quy trình tạo ra, đọc, hoặc cập nhật.

Điểm mấu chốt cần khắc cốt ghi tâm: Data Object chỉ tồn tại trong phạm vi (scope) của MỘT process instance. Nghĩa là khi một thực thể quy trình bắt đầu (ví dụ: khách hàng A đặt đơn số 1001), một Data Object "Đơn hàng" được sinh ra cho riêng instance đó. Khi instance kết thúc, Data Object cũng biến mất theo. Nó giống như một biến cục bộ (local variable) trong lập trình: sống và chết cùng với lần chạy của hàm.

Ví dụ điển hình của Data Object: tờ "Invoice doc" (hóa đơn), "Order form" (phiếu đặt hàng), "Đơn xin nghỉ phép", "Hồ sơ ứng tuyển". Mỗi lần quy trình chạy lại sinh ra một bản riêng.

Một chi tiết tinh tế: Data Object có thể mang trạng thái (state), được ghi trong cặp ngoặc vuông bên dưới tên. Ví dụ: cùng một Data Object "Đơn hàng" nhưng ở đầu quy trình là Đơn hàng [Nháp], sau bước nhập liệu thành Đơn hàng [Đã xác nhận], sau bước duyệt thành Đơn hàng [Đã duyệt]. Cách dùng state này cực kỳ hữu ích vì nó cho người đọc thấy dữ liệu biến đổi qua từng bước, không chỉ là một tờ giấy bất biến.

Data Input và Data Output — dữ liệu ở ranh giới quy trình

Khi một dữ liệu là đầu vào bắt buộc để khởi động cả quy trình, hoặc là sản phẩm cuối cùng quy trình bàn giao ra ngoài, ta dùng Data InputData Output. Về hình dạng, chúng vẫn là tờ giấy gấp góc, nhưng có thêm một mũi tên nhỏ ở góc trên bên trái: mũi tên rỗng (trắng) cho Data Input, mũi tên đặc (đen) cho Data Output.

Hãy hiểu thế này: Data Object là dữ liệu nội bộ, sinh ra và tiêu thụ bên trong quy trình. Còn Data Input/Output là dữ liệu nằm ở ranh giới — thứ quy trình nhận từ thế giới bên ngoài để bắt đầu, và thứ nó trả ra khi kết thúc. Ví dụ quy trình "Tuyển dụng": Data Input là "CV ứng viên" (nhận từ bên ngoài), Data Output là "Hợp đồng lao động đã ký" (bàn giao cho HR lưu trữ).

Data Store — dữ liệu vượt ra ngoài một process instance

Data Store được vẽ bằng hình trụ tròn nằm ngang có các đường kẻ ngang (giống biểu tượng database cylinder kinh điển trong ngành IT). Đây là điểm khác biệt quan trọng nhất so với Data Object.

Trong khi Data Object sống-chết cùng một instance, thì Data Store tồn tại bền vững (persistent), vượt ra ngoài vòng đời của bất kỳ instance nào. Nó là nơi dữ liệu được lưu lại để nhiều instance khác nhau, thậm chí nhiều quy trình khác nhau cùng đọc và ghi. Hãy nghĩ đến: cơ sở dữ liệu khách hàng (CRM), kho hồ sơ nhân sự, bảng tồn kho, sổ cái kế toán.

Một cách phân biệt đơn giản tôi hay dạy học viên: "Nếu hôm nay tôi tắt máy, ngày mai mở lên dữ liệu vẫn còn — đó là Data Store. Nếu dữ liệu chỉ có ý nghĩa trong một lần xử lý rồi mất — đó là Data Object." Đơn hàng cụ thể số 1001 là Data Object; bảng chứa tất cả đơn hàng từ trước đến nay là Data Store.

Data Association — sợi dây nối dữ liệu vào hoạt động

Bạn không nối Data Object/Data Store vào Task bằng Sequence Flow (mũi tên liền nét điều khiển trình tự — đã học ở Bài 18). Thay vào đó, bạn dùng Data Association: một đường mũi tên chấm chấm (dotted line) có đầu mũi tên hở.

Hướng của mũi tên cho biết quan hệ:

  • Mũi tên đi từ Data Object vào Task = Task đọc dữ liệu đó (input).
  • Mũi tên đi từ Task ra Data Object = Task tạo ra hoặc cập nhật dữ liệu đó (output).
Điểm quan trọng cần ghi nhớ: Data Association không bao giờ thay đổi luồng điều khiển (token flow). Nó chỉ là chú thích cho biết "hoạt động này dùng dữ liệu kia". Token vẫn chạy theo Sequence Flow, còn dữ liệu thì được mô tả song song bằng đường chấm chấm. Đừng bao giờ nhầm lẫn hai loại đường này — đây là lỗi phổ biến nhất chúng ta sẽ bàn ở phần sau.

Tình huống thực tế

Tình huống 1 — Tiki và đường đi của "Phiếu đặt hàng"

Giả sử đội Business Analyst tại Tiki mô hình hóa quy trình "Xử lý đơn nội thành TP.HCM". Ban đầu sơ đồ chỉ có chuỗi Task: Nhận đơn → Kiểm tra tồn kho → Đóng gói → Bàn giao shipper. Trông gọn gàng, nhưng khi đưa cho đội vận hành review, một anh quản lý kho hỏi: "Lúc đóng gói, nhân viên nhìn vào đâu để biết đóng món gì?"

Đội BA bổ sung Data Object. Họ vẽ tờ "Phiếu đặt hàng [Đã xác nhận]" với một Data Association đi vào Task "Đóng gói" — nghĩa là bước đóng gói đọc phiếu này. Sau Task "Kiểm tra tồn kho", họ thêm một Data Association đi ra tạo Data Object "Phiếu đặt hàng [Đã trừ kho]". Quan trọng nhất, họ vẽ một Data Store "Hệ thống tồn kho" — vì số lượng tồn không thuộc về riêng đơn 1001, mà là dữ liệu dùng chung cho mọi đơn. Task "Kiểm tra tồn kho" có Data Association hai chiều với Data Store này: đọc số tồn rồi ghi lại số tồn mới sau khi trừ.

Bài học rút ra: chính nhờ tách bạch Data Object (phiếu của riêng đơn này) với Data Store (kho tồn dùng chung), đội BA phát hiện ra một lỗ hổng race condition tiềm ẩn — hai đơn cùng lúc trừ một sản phẩm tồn kho cuối cùng. Nếu sơ đồ chỉ có Task suông, không ai nhìn ra điểm tranh chấp dữ liệu này.

Tình huống 2 — Ngân hàng và quy trình duyệt vay tín chấp

Một ngân hàng tầm trung tại Việt Nam (gọi là Ngân hàng X) mô hình hóa quy trình "Phê duyệt khoản vay tín chấp dưới 200 triệu". Quy trình có Data Input là "Hồ sơ vay" (khách hàng nộp từ ngoài vào — vẽ tờ giấy có mũi tên rỗng). Trong quy trình, Data Object "Hồ sơ vay" biến đổi trạng thái qua các bước: [Mới nhận][Đã thẩm định][Đã chấm điểm tín dụng][Đã phê duyệt].

Điểm hay nằm ở Task "Chấm điểm tín dụng". Task này có Data Association đọc từ một Data Store "CIC — Trung tâm Thông tin Tín dụng". Đây rõ ràng phải là Data Store chứ không phải Data Object, vì dữ liệu lịch sử tín dụng quốc gia tồn tại độc lập, phục vụ hàng triệu hồ sơ vay khác nhau. Cuối quy trình, Data Output là "Hợp đồng tín dụng [Đã ký]", đồng thời một Data Association ghi bản sao vào Data Store "Kho hồ sơ khoản vay" để phục vụ kiểm toán sau này.

Bài học rút ra: việc dùng Data Store cho CIC giúp người đọc hiểu ngay rằng đây là điểm tích hợp với hệ thống bên ngoài, có độ trễ và chi phí truy vấn — một thông tin cực kỳ giá trị khi sau này muốn tự động hóa. Và việc tách Data Output (hợp đồng bàn giao) khỏi Data Store lưu trữ (kho kiểm toán) làm rõ trách nhiệm tuân thủ.

Tình huống 3 — Công ty bảo hiểm và bài học "tờ giấy bất biến"

Một công ty bảo hiểm ở Singapore mở rộng sang thị trường Việt Nam thuê tư vấn vẽ lại quy trình "Bồi thường tai nạn xe". Bản nháp đầu tiên chỉ vẽ một Data Object duy nhất tên "Hồ sơ bồi thường", nối vào tất cả các Task. Nhìn thì gọn, nhưng khi diễn tập, đội nghiệp vụ bối rối: "Hồ sơ ở bước giám định khác hẳn hồ sơ ở bước duyệt chi — sao lại là cùng một tờ?"

Tư vấn sửa lại bằng cách dùng trạng thái (state): cùng một Data Object "Hồ sơ bồi thường" nhưng hiển thị [Chờ giám định], [Đã giám định], [Đề xuất mức chi], [Đã duyệt chi] ở từng giai đoạn. Cách này vừa giữ sơ đồ gọn, vừa thể hiện rõ dữ liệu tiến hóa qua quy trình.

Bài học rút ra: Data Object không phải là một tờ giấy chết. Dùng state để kể câu chuyện dữ liệu biến đổi sẽ làm sơ đồ vừa chính xác vừa dễ đọc, tránh phải vẽ năm sáu tờ giấy riêng biệt gây rối mắt.

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

Đây là quy trình tôi khuyên bạn áp dụng mỗi khi muốn thêm lớp dữ liệu vào một sơ đồ BPMN đã có sẵn luồng điều khiển:

  • Hoàn thiện luồng điều khiển trước. Đảm bảo Task, Gateway, Event và Sequence Flow đã đúng. Đừng vẽ dữ liệu khi trình tự còn lộn xộn — bạn sẽ phải sửa cả hai lớp cùng lúc.
  • Liệt kê mọi "thứ thông tin" mà quy trình động đến. Đi qua từng Task và tự hỏi: "Bước này cần đọc gì? Tạo ra gì? Cập nhật gì?" Ghi ra một danh sách thô.
  • Phân loại mỗi thứ là Data Object hay Data Store. Áp dụng phép thử "tắt máy ngày mai còn không": nếu dữ liệu thuộc riêng một lần chạy quy trình → Data Object; nếu dữ liệu dùng chung, bền vững qua nhiều instance → Data Store.
  • Đánh dấu dữ liệu ở ranh giới. Thứ nào quy trình nhận từ ngoài để khởi động → Data Input (mũi tên rỗng). Thứ nào bàn giao ra ngoài khi kết thúc → Data Output (mũi tên đặc).
  • Vẽ Data Association bằng đường chấm chấm. Nối từng Data Object/Store vào Task tương ứng. Hướng mũi tên: vào Task = đọc, ra khỏi Task = ghi. Tuyệt đối không dùng mũi tên liền nét (Sequence Flow) cho việc này.
  • Gắn trạng thái (state) khi dữ liệu biến đổi. Nếu cùng một Data Object đi qua nhiều bước và thay đổi bản chất, dùng cú pháp Tên [Trạng thái] thay vì vẽ nhiều tờ giấy.
  • Review chéo với người vận hành. Hỏi họ: "Nhìn sơ đồ này, bạn có biết mỗi bước lấy dữ liệu từ đâu và lưu vào đâu không?" Nếu họ ngập ngừng, lớp dữ liệu của bạn chưa đủ rõ.

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

Lỗi 1 — Dùng Sequence Flow để nối Data Object vào Task. Đây là lỗi số một. Data Object phải nối bằng Data Association (chấm chấm), không bao giờ bằng mũi tên liền nét. Mũi tên liền nét điều khiển token; nếu bạn nối nhầm, công cụ như Camunda hay Bizagi sẽ báo lỗi hoặc hiểu sai luồng thực thi.

Lỗi 2 — Nhầm Data Object với Data Store. Vẽ "kho hàng tồn" thành tờ giấy gấp góc, hoặc vẽ "đơn hàng số 1001" thành trụ database. Hãy luôn chạy phép thử persistence: dữ liệu có sống sót qua nhiều instance không?

Lỗi 3 — Tưởng Data Object điều khiển luồng. Nhiều người nghĩ "Data Object chưa có thì Task không chạy". Sai. Data Association thuần túy là mô tả, không phải điều kiện kích hoạt. Nếu bạn muốn diễn tả "chờ tài liệu mới chạy tiếp", phải dùng Event hoặc Gateway, không phải dựa vào Data Object.

Lỗi 4 — Vẽ quá nhiều, làm sơ đồ rối như tơ vò. Không phải dữ liệu nào cũng cần vẽ. Chỉ vẽ những Data Object/Store quan trọng để hiểu quy trình hoặc gây tranh cãi giữa các bên. Một sơ đồ chi chít tờ giấy còn khó đọc hơn không có gì.

Mẹo 1 — Dùng trạng thái thay vì nhân bản. Một Data Object với nhiều state đọc dễ hơn nhiều Data Object trùng tên.

Mẹo 2 — Đặt tên Data Store theo hệ thống thật. Thay vì ghi "Cơ sở dữ liệu", hãy ghi "CRM Salesforce" hoặc "Hệ thống Core Banking T24". Điều này giúp sơ đồ kết nối với hiện thực kỹ thuật và rất hữu ích khi bước sang giai đoạn tự động hóa.

Mẹo 3 — Data Store dùng chung nên đặt ngoài Pool. Vì Data Store thường được nhiều participant truy cập, đặt nó ở vị trí trung lập (thường phía dưới sơ đồ) sẽ trực quan hơn là nhét vào một Lane cụ thể.

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

Hãy lấy quy trình "Đăng ký và cấp lại mật khẩu tài khoản học viên" của một nền tảng học trực tuyến và bổ sung lớp dữ liệu cho nó. Luồng cơ bản: Học viên gửi yêu cầu cấp lại mật khẩu → Hệ thống kiểm tra email tồn tại → Gửi mã OTP → Học viên nhập OTP → Hệ thống đặt lại mật khẩu → Thông báo thành công.

Yêu cầu của bạn:

  • Xác định ít nhất một Data Input (gợi ý: email do học viên cung cấp).
  • Xác định ít nhất một Data Store và lý giải vì sao nó là Data Store chứ không phải Data Object (gợi ý: bảng tài khoản người dùng).
  • Tạo ít nhất một Data Object có trạng thái biến đổi qua tối thiểu hai bước (gợi ý: "Yêu cầu đặt lại" từ [Chờ xác thực] sang [Đã xác thực OTP]).
  • Vẽ các Data Association đúng hướng cho từng Task: đánh dấu rõ Task nào đọc, Task nào ghi.
  • Trả lời bằng lời: tại sao "mã OTP" nên là Data Object (sống ngắn, riêng một phiên) chứ không phải Data Store?
Sau khi hoàn thành, hãy tự kiểm tra bằng cách đưa sơ đồ cho một người không tham gia vẽ và hỏi: "Mỗi bước trong quy trình này lấy thông tin từ đâu và lưu vào đâu?" Nếu họ trả lời được mà không cần bạn giải thích thêm, bạn đã làm đúng.

Tóm tắt

Trong bài này, chúng ta đã bóc tách nhóm ký hiệu dữ liệu của BPMN 2.0 — phần thường bị bỏ quên nhưng lại quyết định một sơ đồ có thực sự chạy được hay không.

  • Data Object (tờ giấy gấp góc) biểu diễn dữ liệu trong phạm vi một process instance — sống và chết cùng lần chạy quy trình. Nó có thể mang trạng thái để thể hiện sự biến đổi qua từng bước.
  • Data Input/Output (tờ giấy có mũi tên rỗng/đặc) là dữ liệu ở ranh giới quy trình: thứ nhận vào để khởi động và thứ bàn giao ra khi kết thúc.
  • Data Store (trụ database) là dữ liệu bền vững, vượt qua nhiều instance — như CRM, kho tồn, CIC, core banking. Dùng phép thử "tắt máy ngày mai còn không" để phân biệt với Data Object.
  • Data Association (đường chấm chấm) nối dữ liệu vào Task để mô tả đọc/ghi, nhưng không bao giờ điều khiển luồng token — đó là việc của Sequence Flow.
Khi bạn thành thạo lớp dữ liệu này, sơ đồ của bạn không chỉ trả lời "ai làm gì theo thứ tự nào" mà còn trả lời "thông tin đi từ đâu đến đâu" — và đó chính là thứ giúp bạn phát hiện sớm những lỗ hổng như tranh chấp dữ liệu, mất chứng từ, hay thiếu điểm truy vết. Ở các bài kế tiếp về Artifacts (Group và Annotation) và các pattern xử lý sự kiện, bạn sẽ thấy lớp dữ liệu này tiếp tục là nền tảng để mô tả một quy trình đầy đủ và đáng tin cậy.