Chào bạn,
Ở bài học trước, chúng ta đã thành công trong việc sử dụng Camunda Forms để tạo các giao diện nhập liệu cho người dùng một cách nhanh chóng, giúp tách biệt logic giao diện khỏi quy trình nghiệp vụ. Đây là một công cụ mạnh mẽ để thu thập dữ liệu từ phía client.
Tuy nhiên, có những logic nghiệp vụ cần được thực thi ở phía server mỗi khi có một sự kiện xảy ra với User Task. Ví dụ: gửi email thông báo khi một task được giao, tính toán và chuẩn bị sẵn dữ liệu trước khi người dùng mở form, hoặc kiểm tra quyền hạn của người dùng khi họ hoàn thành một task.
Trong bài học hôm nay, "Implement một Task Listener để thực thi logic tùy chỉnh trong vòng đời của User Task", chúng ta sẽ học cách "gắn" các đoạn mã Java vào các sự kiện quan trọng trong vòng đời của một User Task. Kỹ thuật này cho phép bạn mở rộng chức năng của các tác vụ người dùng một cách linh hoạt và giữ cho sơ đồ BPMN của bạn sạch sẽ, dễ hiểu.
1. Task Listener là gì và tại sao lại cần đến nó?
Task Listener là một đoạn code được thực thi khi một sự kiện cụ thể xảy ra trong vòng đời của một User Task. Hãy tưởng tượng nó như một "trigger" cho phép bạn chạy logic nghiệp vụ tùy chỉnh tại các thời điểm quan trọng như khi một task được tạo, được giao, hay được hoàn thành.
Một câu hỏi thường gặp là: "Tại sao không dùng một Service Task ngay sau User Task để gửi email thông báo?"
How to send an email when a usertask is assigned
Bài viết sau từ blog của Camunda giải thích rất rõ ràng tại sao việc sử dụng Task Listener lại là một giải pháp "thanh lịch" hơn so với việc thêm các Service Task chỉ để xử lý các logic kỹ thuật, giúp cho sơ đồ quy trình của bạn tập trung vào luồng nghiệp vụ chính.
Hãy đọc phần giới thiệu đầu tiên của bài viết (đến trước phần code). Chú ý đến việc so sánh giữa việc dùng Service Task và Task Listener để giải quyết yêu cầu gửi email thông báo.
Như bạn đã đọc, việc thêm một Service Task cho mỗi logic kỹ thuật (như gửi thông báo) sẽ làm "rối" sơ đồ quy trình, khiến nó khó đọc hơn đối với các bên liên quan (ví dụ: nhà phân tích nghiệp vụ). Task Listener giúp chúng ta ẩn đi các chi tiết kỹ thuật này, gắn chúng trực tiếp vào task mà chúng liên quan, giữ cho mô hình quy trình trong sáng và đúng với mục đích ban đầu của BPMN: là cầu nối giữa nghiệp vụ và IT.
2. Vòng đời của User Task và các sự kiện
Một User Task trải qua nhiều trạng thái khác nhau từ khi được tạo ra cho đến khi được hoàn thành. Camunda cho phép chúng ta can thiệp vào các thời điểm chuyển giao trạng thái này thông qua các Event Type (Loại sự kiện).

Các sự kiện chính trong vòng đời của một User Task bao gồm:
create: Sự kiện này được kích hoạt khi một thể hiện của task được tạo ra và lưu vào cơ sở dữ liệu. Tại thời điểm này, task đã tồn tại nhưng thường chưa được gán cho ai (trừ khi được cấu hình sẵn). Đây là thời điểm lý tưởng để khởi tạo dữ liệu hoặc chuẩn bị thông tin cho task.assignment: Kích hoạt khi thuộc tínhassigneecủa task được thiết lập hoặc thay đổi. Điều này xảy ra khi một người dùng "claim" một task từ một group, hoặc khi task được gán trực tiếp cho một người dùng cụ thể. Đây là sự kiện hoàn hảo để gửi thông báo cho người được giao việc.complete: Kích hoạt ngay trước khi task được hoàn thành. Tại đây, bạn có thể truy cập tất cả các biến mà người dùng đã nhập vào form. Đây là nơi thích hợp để thực hiện các logic xác thực (validation) phức tạp, xử lý dữ liệu trước khi lưu, hoặc gọi các hệ thống bên ngoài.delete: Kích hoạt khi task bị xóa, thường là do process instance chứa nó bị hủy bỏ. Sự kiện này ít được sử dụng hơn nhưng hữu ích cho các tác vụ dọn dẹp.
Ngoài ra, còn có các sự kiện đặc biệt khác như timeout, cho phép bạn kích hoạt một listener nếu một task không được hoàn thành trong một khoảng thời gian nhất định.

3. Implement Task Listener trong Spring Boot
Trong môi trường Spring Boot, cách tốt nhất để implement một Task Listener là tạo một Spring Bean. Điều này cho phép bạn tận dụng cơ chế Dependency Injection để đưa các service khác (ví dụ: RestTemplate để gọi API, hoặc các Repository của bạn) vào trong listener một cách dễ dàng.
Để tạo một Task Listener, bạn cần:
- Tạo một class Java.
- Đánh dấu nó là một Spring bean bằng annotation
@Component. - Implement interface
org.camunda.bpm.engine.delegate.TaskListener. - Ghi đè (override) phương thức
notify(DelegateTask delegateTask).
Phương thức notify là nơi bạn sẽ viết logic của mình. Tham số delegateTask là một đối tượng cực kỳ quan trọng, nó cho phép bạn:
- Lấy thông tin về task:
delegateTask.getId(),delegateTask.getName(),delegateTask.getAssignee(). - Truy cập các biến quy trình (process variables):
delegateTask.getVariable("variableName"),delegateTask.setVariable("variableName", value). - Tương tác với các service của Camunda engine.
How to send an email when a usertask is assigned
Bài viết chúng ta đã xem ở trên cung cấp một ví dụ code Java hoàn chỉnh cho một Task Listener. Hãy cùng phân tích nó.
Hãy xem kỹ đoạn code trong phần thứ hai của bài viết. Chú ý đến cách class TaskAssignmentListener implement interface TaskListener và cách phương thức notify sử dụng đối tượng delegateTask để lấy thông tin người được gán (assignee) và ID của task.
Ví dụ trong bài viết sử dụng một thư viện để gửi email thực sự. Trong bài thực hành của chúng ta, để đơn giản hóa, chúng ta sẽ chỉ ghi một thông báo ra console log để xác nhận rằng listener đã được kích hoạt thành công.
4. Thực hành: Xây dựng Listener thông báo
Bây giờ, hãy áp dụng những gì đã học để tạo một listener có chức năng ghi log mỗi khi một task được gán cho người dùng trong dự án Spring Boot của bạn.
-
Tạo class Listener:
Trong package của ứng dụng, tạo một class Java mới tên làAssignmentNotificationListener. -
Implement code:
Chỉnh sửa class của bạn với nội dung sau. Chú ý annotation@Componentvà việc implement interfaceTaskListener.package com.example.camunda.yourpackage; // Thay thế bằng package của bạn import org.camunda.bpm.engine.delegate.DelegateTask; import org.camunda.bpm.engine.delegate.TaskListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Component("assignmentNotificationListener") // Tên bean để tham chiếu từ BPMN public class AssignmentNotificationListener implements TaskListener { private static final Logger LOGGER = LoggerFactory.getLogger(AssignmentNotificationListener.class); @Override public void notify(DelegateTask delegateTask) { String assignee = delegateTask.getAssignee(); String taskId = delegateTask.getId(); String taskName = delegateTask.getName(); if (assignee != null) { LOGGER.info("✅ Task Assigned! Task '{}' (ID: {}) has been assigned to user '{}'.", taskName, taskId, assignee); } } }Lưu ý:
@Component("assignmentNotificationListener"): Chúng ta đặt tên cho Spring bean này làassignmentNotificationListener. Tên này sẽ được dùng trong file BPMN để tham chiếu đến listener.- Chúng ta sử dụng
SLF4J Loggerđể ghi log, đây là một thực hành tốt trong các ứng dụng Java.
5. Cấu hình Listener trong Camunda Modeler
Sau khi đã có code Java, bước cuối cùng là "bảo" cho Camunda Modeler biết phải sử dụng nó khi nào và ở đâu.
-
Mở file BPMN của bạn trong Camunda Modeler.
-
Chọn User Task mà bạn muốn gắn listener (ví dụ: task "Submit Leave Request").
-
Trong
Properties Panelở bên phải, chọn tabListeners. -
Nhấn vào nút
+để thêm một listener mới. -
Cấu hình các thuộc tính sau:
- Event Type: Chọn
assignment. - Listener Type: Chọn
Delegate Expression. Đây là lựa chọn tốt nhất khi dùng Spring, vì nó cho phép Camunda sử dụng Spring bean mà chúng ta đã tạo, thay vì tự tạo một instance mới. - Delegate Expression: Nhập vào
${assignmentNotificationListener}. Cú pháp${...}là Expression Language (EL), vàassignmentNotificationListenerchính là tên bean bạn đã định nghĩa trong annotation@Component.
Phần cấu hình của bạn sẽ trông tương tự như hình ảnh minh họa trong bài viết
How to send an email when a usertask is assigned(phần "assign the class as a task listener"). - Event Type: Chọn
-
Lưu file BPMN.
6. Deploy và Kiểm tra
Bây giờ là lúc kiểm tra thành quả!
- Khởi động lại ứng dụng Spring Boot của bạn để deploy phiên bản quy trình mới.
- Mở Camunda Tasklist (
http://localhost:8080/camunda/app/tasklist/). - Start a process cho quy trình của bạn.
- Nếu task của bạn được tạo ra mà chưa gán cho ai, hãy vào task đó và nhấn nút Claim. Hành động "Claim" sẽ set giá trị cho trường
assigneevà do đó sẽ kích hoạt sự kiệnassignment. - Kiểm tra Console Log của ứng dụng Spring Boot. Bạn sẽ thấy một dòng log tương tự như sau:
✅ Task Assigned! Task 'Submit Leave Request' (ID: a1b2c3d4-...) has been assigned to user 'demo'.
Nếu thấy dòng log này, bạn đã thành công! Bạn vừa implement thành công một Task Listener để thực thi logic tùy chỉnh trong vòng đời của một User Task.
Tổng kết
Trong bài học này, chúng ta đã khám phá một khái niệm rất mạnh mẽ trong Camunda là Task Listeners. Chúng cho phép bạn thực thi code Java tùy chỉnh tại các điểm then chốt trong vòng đời của một User Task, giúp mở rộng chức năng của quy trình mà không làm phức tạp hóa sơ đồ BPMN.
Những điểm chính cần nhớ:
- Task Listeners được kích hoạt bởi các sự kiện như
create,assignment,complete, vàdelete. - Chúng giúp tách biệt logic kỹ thuật ra khỏi luồng nghiệp vụ chính, giữ cho mô hình quy trình gọn gàng.
- Trong Spring Boot, cách tốt nhất để implement listener là tạo một Spring Bean (
@Component) implement interfaceTaskListener. - Sử dụng Delegate Expression (
${myBeanName}) trong Camunda Modeler để liên kết User Task với Spring bean của bạn.
Xem trước Module tiếp theo
Bạn đã đi qua một chặng đường dài, từ việc mô hình hóa quy trình, tích hợp với Spring Boot, quản lý dữ liệu, điều khiển luồng, xây dựng giao diện và giờ là thêm logic tùy chỉnh với listeners. Quy trình của bạn giờ đây đã khá hoàn chỉnh.
Bước tiếp theo trong hành trình của một nhà phát triển là học cách quản lý, vận hành và bảo trì những quy trình này trong môi trường thực tế. Trong module tiếp theo, "Vận hành, Giám sát và Bảo trì", chúng ta sẽ bắt đầu với một chủ đề thiết yếu: tìm hiểu cách Camunda quản lý các phiên bản khác nhau của một quy trình và làm thế nào để nâng cấp các quy trình đang chạy lên phiên bản mới một cách an toàn.