Gần đây tôi tham gia thiết kế một dự án. Các thành viên trong nhóm cứ tạo những bảng có trường status, rồi thêm trường create_date và modified_date nhìn rất giống trong sách. Nhưng họ không hiểu rõ khác biệt giữa Action (Hành động) - Event (Sự kiện) - Status (Trạng thái) trong phân tích thiết kế phần mềm. Kết quả là bảng nhiều, cột nhiều nhưng không xử lý hết yêu cầu nghiệp vụ. Bàn tới bàn lui, mà không giải quyết được nghiệp vụ.

Action là động từ, Event là danh từ, còn Status là tính từ

Action: Khách hàng đặt hàng - customer orders an item

Event: Bản ghi đơn hàng được tạo ra - order record is created

Status: Trạng thái của đơn hàng là "được tạo" - order status is "created"

Khi chúng ta lấy yêu cầu từ khách hàng, tạo user story thì khách hàng thường mô tả: Ai đó làm gì? Ví dụ:

Khách hàng đăng nhập -> khách hàng truy vấn tài khoản -> khách hàng nhập tài khoản cần chuyển khoản -> khách hàng chọn ngân hàng -> khách hàng gõ số tiền cần chuyển -> Hệ thống gửi mã OTP -> khách hàng nhập xác nhận -> tiến hành chuyển khoản -> Hệ thống gửi biên lai chuyển khoản.

Action mô tả một người (role) hành động hay hệ thống (system) thực hiện tác vụ. Ở đây tôi cố ý phân biệt:

  • Người tác động lên một đối tượng - User does some thing

  • Hệ thống (phần mềm) thì thực hiện tác vụ - System run a task

    Nếu bạn sử CSDL quan hệ bạn có thể lưu lại từng dòng action theo thời gian. Tuy nhiên sau một hành động có ít nhất 2 sự kiện: thành công hoặc thất bại. Nếu bạn chỉ quan tâm đến sự kiện hữu ích với hệ thống thì bạn nên dùng event.

Liệt kê đủ sự kiện / tác vụ phát sinh sau một hành động ban đầu.

Khi khách hàng đặt mua một mặt hàng ~ action: customer orders an item. Có những sự kiện và tác vụ phát sinh:

1- Event: Đơn hàng được tạo ra ~ order is created.

2- Action: Hệ thống kho kiểm tra số lượng hàng tồn kho - inventory check item availability. Hoặc với shop nhỏ, chỉ cần truy vấn số lượng hàng có sẵn.

3- Nếu mặt hàng ở trạng thái hết hàng (status out of stock) --> action: hệ thống tự động đặt mua bổ xung hàng hoặc trả về thông báo hết hàng hoặc email thông báo cho phòng đặt hàng.

Liệt kê đủ thì cơ bản hiểu được yêu cầu nghiệp vụ. Nhưng việc chúng ta hiện thực hoá nghiệp vụ như thế nào xin mời đọc tiếp.

CSDL thần thánh làm được những gì?

Hồi mới lập trình, khi insert một bản ghi vào CSDL rồi lại truy vấn được ra, chế biến thành các báo cáo khác nhau, tôi vô cùng phân khích cảm giác mãn nguyện với phần mềm mình viết. Nhưng càng dùng thấy nó càng thiếu sót nhiều trường hợp, thậm chí dữ liệu sai lệch. Mà rõ ràng là khi phân tích nghiệp vụ đã liệt kê chi tiết.

Một lỗi căn bản tôi thường mắc là chỉ lưu trạng thái, giá trị cuối cùng của một sự kiện. Ví dụ khách hàng mua một đôi giày ngày 1/10/2021, lúc đó giá là 100VND. Nhưng đến ngày 10/10, giá tăng lên 110VND. Cuối tháng hệ thống cứ báo là đơn hàng này thu thiếu 10VND.

Khách hàng mua 1 sản phẩm không được giảm giá, nhưng mua 2 mặt hàng được giảm 500VND. Nhưng mua xong, họ hoàn lại 1. Nếu chỉ lưu trạng thái cuối cùng đơn hàng là một sản phẩm. Chúng ta lại thu thiếu 500VND!

**Sai lầm lớn nhất của lập trình viên CSDL chỉ lưu trạng thái (status) cuối cùng mà bỏ qua các hành động (action) - sự kiện (event) dẫn đến trạng thái đó. **

Trong software engineering có một kỹ thuật gọi là Event Sourcing luôn lưu sự kiện chân thực nhất theo thực tế đã xảy ra, không sửa đổi, không xoá. Để đơn giản hoá, Action ban đầu cũng coi là một sự kiện ban đầu (initial event hay starting event). Kết thúc một event, một hay nhiều đối tượng thay đổi trạng thái. Lưu hay không lưu CSDL, tuỳ thuộc nhu cầu báo cáo. Nếu không có hiệu ứng phụ, sai lệch số liệu, hệ thống chỉ cần chạy lại play back lại chuỗi event ghi trung thực những gì đã diễn ra thì có được trạng thái cuối cùng.

Lưu lại chân thực các sự kiện đã diễn ra - store immutable events

Nếu bạn thiết kế một hệ thống lớn, microservice phức tạp. Ở đó các microservice trao đổi với nhau qua những event, message thi bạn cần phải đọc khá nhiều pattern này nọ, phức tạp. Bài viết này dành cho những bạn sinh viên đang học nghề, code với một CSDL:

  • Hãy tạo bảng lưu sự kiện: order_event, member_event, inventory_event
  • Tạo bảng lưu giá trị theo thời gian ví dụ lịch sử giá mặt hàng. Mỗi dòng trong đơn hàng, tham chiếu đến giá tại thời điểm đặt hàng. Thay đổi giá hay tạo khuyến mại thì tạo bản ghi lưu giá mới, không sửa trực tiếp vào bản ghi cũ.

Khi lưu sự kiện theo thời gian, đôi khi chúng ta cần lưu cả trạng thái kết quả của sự kiện đó. Vậy các bạn cần liệt kê các trạng thái có thể xảy ra.

Finite State Machine

Liệt kê các sự kiện (event) một lớp học ở Techmaster:

  1. Giáo vụ lên lịch khai giảng một lớp học - Staff schedules a class
  2. Giáo vụ điều chỉnh lịch khai giảng - Staff reschedules a class
  3. Giáo vụ thu học phí và chuyển sinh viên vào lớp - Staff adds student to class
  4. Giáo vụ chọn giảng viên dạy lớp - Staff assigns teacher to class
  5. Giáo vụ khai giảng một lớp học - Staff opens class
  6. Giáo vụ đánh dấu một lớp học đã hoàn tất - Staff marks class completed
  7. Giáo vụ ghép hai lớp học lại thành một - Staff merges two classes into one
  8. Giáo vụ huỷ lịch khai giảng - Staff cancels a class
  9. Giáo vụ dừng một lớp học đang diễn ra - Staff aborts a class
  10. Giáo vụ rút học viên ra khỏi lớp - Staff moves a student out class

Liệt kê các trạng thái (status) một lớp học:

  1. scheduled: lập lịch để khai giảng
  2. active: đang học
  3. canceled: huỷ khi chưa diễn ra
  4. aborted: dừng giữa chừng
  5. merged: bị ghép chuyển sang lớp khác
  6. completed: hoàn tất

Rõ ràng không phải sự kiện nào cũng thay đổi trạng thái của lớp học, hoặc có lúc thay đổi có lúc không.

  • Ví dụ 1: thêm sinh viên vào lớp, không chuyển lớp từ scheduled sang active.
  • Ví dụ 2: file được tạo ra trong ổ cứng (file is created) là sự kiện tác động lên ổ cứng. Ổ cứng chuyển sang trạng thái đầy disk full sau khi rất nhiều file kích thước khủng tạo ra.

State Transition

Tóm lại

  1. Cần phiên biệt rõ Action - Event - Status.
  2. Đừng chỉ lưu trạng thái cuối cùng, mà lưu chuỗi các sự kiện theo đúng trình tự thực tế diễn ra. Không sửa, xoá sự kiện chân thực, chỉ tạo thêm: only append immutable event.
  3. Cần liệt kê đầy đủ các sự kiện - trạng thái.
  4. Vẽ lược đồ chuyển đổi trạng thái để hình dung và lập trình đúng.
  5. Đừng lưu trường created_datemodified_date cùng một bảng. Hay tạo bảng lưu sự kiện chỉ với created_date và không sửa đổi nó. Xem mục 2.
  6. Nếu event_id của một sự kiện sẽ broad cast sang nhiều microservice khác nhau hãy dùng global unique id nhưng sắp xếp được theo thời gian. Còn nếu event_id chỉ là sự kiện trong một hệ thống nhỏ monolithic, single database, bạn có thẻ