Thiết kế app đúng với các loại dữ liệu trên Cleeksy DOP

Trong nhiều dự án build app, có một tình huống khá quen thuộc. Lúc đầu hệ thống nhìn rất ổn: form đã có, workflow chạy được, dữ liệu cũng lưu đầy đủ. Team demo thấy hợp lý, user cũng bắt đầu sử dụng. Nhưng sau một thời gian sử dụng, hệ thống bắt đầu lộ ra nhiều vấn đề quen thuộc: sửa một chỗ ảnh hưởng nhiều chỗ, phân quyền khó kiểm soát, app ngày càng nặng, và logic vận hành trở nên rối dần.

Trong nhiều trường hợp, nguyên nhân không nằm ở workflow hay UI, mà nằm ở một bước làm chưa đúng từ đầu: phân loại data.

Trên Cleeksy DOP, mỗi entity không chỉ là một nơi để lưu dữ liệu. Nó đại diện cho một loại đối tượng vận hành khác nhau trong doanh nghiệp. Và mỗi loại data sẽ kéo theo cách thiết kế khác nhau:

  • Có cần workflow hay không

  • Ai được phép chỉnh sửa

  • Khi nào dữ liệu phải khóa lại

  • Nên lookup live hay copy dữ liệu

Hiểu đúng nguyên lý này sẽ giúp Citizen Developer thiết kế solution gọn hơn, rõ hơn và bền hơn về sau.

1. Vì sao cần phân loại data ngay từ đầu?

Khi tạo một entity mới, có 4 câu hỏi rất quan trọng:

  • Dữ liệu này là dữ liệu gốc, dữ liệu đang xử lý, dữ liệu đã chốt hay dữ liệu tổng hợp?

  • Nó có cần thay đổi qua workflow không?

  • Nó cần phản ánh trạng thái hiện tại hay giữ nguyên theo thời điểm phát sinh?

  • Ai là owner thực sự của dữ liệu này?

Nếu những câu hỏi này không được trả lời rõ, solution rất dễ gặp những vấn đề quen thuộc:

  • Gộp nhiều bản chất dữ liệu vào cùng một entity

  • Dùng sai kiểu liên kết giữa các app

  • Đặt workflow vào nơi không phù hợp

  • Làm solution nặng và khó bảo trì về sau

Phần lớn các hệ thống trở nên phức tạp không phải vì nghiệp vụ quá khó, mà vì các loại dữ liệu khác nhau bị trộn lẫn với nhau ngay từ thiết kế ban đầu.

2. Bốn loại data chính trên Cleeksy DOP

2.1 Master Data: Dữ liệu gốc dùng chung

Đây là các đối tượng kinh doanh cốt lõi, thường được nhiều app cùng tham chiếu.

Ví dụ:

  • Khách hàng

  • Nhà cung cấp

  • Nhân viên

  • Danh mục vật tư

  • Căn hộ

Cách hiểu đúng

Master data là nguồn dữ liệu chuẩn của hệ thống. Nó không nên bị nhiều app cùng chỉnh sửa trực tiếp.

Nguyên tắc thiết kế

  • Đặt trong một app quản lý chuyên biệt

  • Có owner chịu trách nhiệm cập nhật

  • Các app khác chỉ nên tham chiếu qua Data Connection

  • Không nên đặt Rollup trên master data có fan-out cao

Lý do là vì master data thường được rất nhiều nơi tham chiếu. Nếu đặt rollup trực tiếp trên đó, mỗi thay đổi nhỏ có thể kéo theo việc tính toán lại trên diện rộng, khiến hệ thống chậm dần và khó kiểm soát hơn.

2.2 Transactional Data: Dữ liệu giao dịch đã hoàn tất

Đây là bản ghi của một sự kiện kinh doanh đã xảy ra và cần được giữ nguyên như một sự thật nghiệp vụ.

Ví dụ:

  • Hóa đơn

  • Hợp đồng đã ký

  • Phiếu thu/chi

  • Biên bản nghiệm thu

Cách hiểu đúng

Transactional data là dữ liệu đã chốt. Khi hoàn tất, nó không còn là record để tiếp tục chỉnh sửa tùy ý.

Nguyên tắc thiết kế

  • Workflow nên kết thúc ở trạng thái khóa

  • Cần lưu audit trail: ai duyệt, duyệt lúc nào

  • Dùng copy khi cần giữ nguyên thông tin tại thời điểm phát sinh

Ví dụ: tên khách hàng trên hợp đồng nên là tên tại thời điểm ký. Nếu dùng lookup live, sau này khách đổi tên thì hợp đồng cũ cũng đổi theo. Điều đó sai về mặt nghiệp vụ, thậm chí có thể sai cả về mặt pháp lý.

2.3 Operational Data: Dữ liệu vận hành đang xử lý

Đây là dữ liệu của công việc đang diễn ra, có trạng thái thay đổi liên tục qua workflow.

Ví dụ:

  • Đơn đặt hàng đang xử lý

  • Yêu cầu nghỉ phép

  • Phiếu đề xuất mua hàng

  • Lịch trình giao hàng

Cách hiểu đúng

Operational data là nơi công việc thật sự đang chạy. Đây là loại data cần workflow nhất.

Nguyên tắc thiết kế

  • Có bước xử lý, duyệt, chuyển giao giữa các vai trò

  • Có thể theo dõi SLA hoặc SLO

  • Dùng lookup live cho những dữ liệu cần cập nhật theo thời gian thực

Operational data thường là phần “động” nhất của solution. Và trong nhiều trường hợp, khi hoàn tất, nó sẽ sinh ra hoặc chuyển thành transactional data.

Ví dụ:

  • Đơn hàng đang xử lý → hóa đơn

  • Đề xuất đã duyệt → chứng từ chính thức

2.4 Analytical Data — dữ liệu phân tích tổng hợp

Đây là dữ liệu được tính từ dữ liệu khác để phục vụ báo cáo, KPI hoặc snapshot theo kỳ.

Ví dụ:

  • Tổng doanh thu tháng

  • Báo cáo KPI quý

  • Tổng hợp công nợ

Cách hiểu đúng

Analytical data không phải để nhập tay. Nó là dữ liệu hệ thống tự tính.

Nguyên tắc thiết kế

  • Ưu tiên dùng Rollup + Formula trên entity sẵn có

  • Chỉ tạo entity riêng khi cần snapshot bất biến theo kỳ

  • Dữ liệu nên là read-only

Ví dụ: bảng lương tháng sau khi chốt có thể cần một snapshot riêng để khóa lại. Nhưng trước đó, phần lớn phép tính có thể được xử lý bằng rollup và formula.

3. Ba sai lầm rất phổ biến

Sai lầm 1: Dùng entity master để chứa luôn các công việc vận hành phát sinh theo thời gian

Ví dụ: team dùng entity Khách hàng để lưu luôn các thông tin như trạng thái follow-up hiện tại, lịch hẹn tiếp theo, người đang xử lý, hay kết quả chăm sóc gần nhất.

Vấn đề là Khách hàng chỉ nên là một hồ sơ gốc dùng chung, còn chăm sóc, onboarding, hỗ trợ hay bán hàng lại là những công việc phát sinh nhiều lần theo thời gian. Khi dồn tất cả vào một entity master, một record phải “gánh” quá nhiều lần xử lý khác nhau, rất khó lưu lịch sử đầy đủ và khó chạy workflow riêng cho từng việc.

Cách đúng là tách rõ:

  • Khách hàng là master data

  • Lần chăm sóc, ticket hỗ trợ, phiếu onboarding, cơ hội bán hàng… là operational data riêng

Nói ngắn gọn: master data lưu hồ sơ gốc, operational data lưu từng việc đang diễn ra.

Sai lầm 2: Dùng lookup cho dữ liệu cần được giữ nguyên theo thời điểm

Ví dụ: trong entity Hợp đồng, team lookup trực tiếp từ entity Khách hàng các thông tin như tên khách hàng, địa chỉ, người đại diện hay mã số thuế.

Cách này tiện, nhưng không đúng khi record cần phản ánh thông tin tại thời điểm phát sinh. Nếu sau này khách hàng đổi tên công ty hoặc đổi địa chỉ, hợp đồng cũ cũng đổi theo. Khi đó bản ghi không còn phản ánh đúng nội dung đã được chốt lúc ký nữa.

Vì vậy, cần phân biệt rất rõ:

  • Lookup dùng khi cần dữ liệu luôn theo trạng thái hiện tại

  • Copy dùng khi cần giữ nguyên dữ liệu tại thời điểm tạo hoặc chốt record

Cách nhớ ngắn gọn là: lookup = đúng theo hiện tại, copy = đúng theo thời điểm.

Sai lầm 3: Biến master data thành nơi gánh số liệu tổng hợp động từ dữ liệu vận hành

Ví dụ: trên entity Khách hàng, team đặt nhiều rollup như tổng đơn hàng đang mở, tổng công nợ quá hạn, số ticket đang xử lý, hay tổng giá trị đơn chưa giao.

Về mặt kỹ thuật, cách làm này không hẳn luôn sai. Nhưng khi dữ liệu vận hành phát sinh nhiều và nhiều app cùng tham chiếu một master record, entity master sẽ dần bị biến thành nơi gánh các chỉ số vận hành động. Khi đó, logic dữ liệu gốc bị trộn với logic theo dõi và báo cáo, làm solution khó hiểu và khó mở rộng hơn.

Cách đúng là:

  • Master data nên giữ vai trò dữ liệu gốc dùng chung

  • Các chỉ số tổng hợp nên đặt ở lớp analytical/reporting hoặc ở entity được thiết kế riêng cho mục đích đó

Nói ngắn gọn: đừng biến master data thành dashboard vận hành.

4. Một cách nhớ đơn giản khi thiết kế entity

Trước khi tạo một entity mới, hãy tự hỏi:

  • Đây là dữ liệu gốc, dữ liệu đang xử lý, dữ liệu đã chốt hay dữ liệu tổng hợp?

  • Record này có cần workflow không?

  • Nó cần live hay cần giữ nguyên theo thời điểm?

  • Ai là owner của nó?

Chỉ cần trả lời rõ 4 câu hỏi đó, chất lượng thiết kế sẽ khác hẳn.

Kết luận

Thiết kế solution tốt không bắt đầu từ việc tạo thật nhiều field hay workflow thật chi tiết. Nó bắt đầu từ việc hiểu đúng bản chất dữ liệu.

Khi Citizen Developer phân biệt rõ giữa master data, transactional data, operational data và analytical data, việc thiết kế app trên Cleeksy DOP sẽ trở nên rõ ràng hơn rất nhiều:

  • Biết chỗ nào cần workflow

  • Biết chỗ nào phải khóa

  • Biết khi nào dùng lookup, khi nào dùng copy

  • Biết dữ liệu nào là nguồn chuẩn, dữ liệu nào chỉ là kết quả tổng hợp

Và đó là khác biệt giữa một app “chạy được” với một solution “vận hành được”.

Trong các tình huống thực tế, có trường hợp nào vừa cần lookup live vừa cần snapshot theo thời điểm không ạ? nhờ anh cho ví dụ cụ thể ạ

Phân loại data sẽ giúp solution gọn hơn và bền hơn về sau, tuy nhiên với trải nghiệm thực tế thì anh thấy data model, workflow hay UI sẽ ảnh hưởng đến khả năng mở rộng của system nhiều nhất?

Không có tình huống như vậy trong thực tế đó em. Chỉ hoặc live lookup hoặc snapshot.

Ví dụ, trong một Đơn hàng, mình cần capture dữ liệu: Khách hàng, danh sách mặt hàng cùng với số lượng, đơn giá của mỗi mặt hàng.

Chúng ta hãy phân tích các thông tin
1. Khách hàng: Nên là master data, được lưu trữ tách rời với Đơn hàng và field Khách hàng trong Đơn hàng nên lookup đến Khách hàng để lấy ra tên khách hàng. Lookup này nên là live để khi thông tin khách hàng thay đổi thì đơn hàng được tự động cập nhật tương ứng.
2. Mặt hàng: Thông tin này cũng nên xử lý tương tự như Khách hàng**
3. Đơn giá**: Đơn giá cho mặt hàng bán cần được lookup từ ứng dụng Bảng giá. Khi lookup này nên được snapshot thay vì live bởi vì khi một đơn hàng được chốt thì đơn giá mặt hàng đó cần được xác định và không nên được thay đổi khi Bảng giá thay đổi.

Trong thực tế có rất nhiều case tương tự ví dụ này nên khi áp dụng cũng sẽ rất dễ khi đã hiểu nguyên tắc.

Data model, Workflow, và UI: 3 thành phần này trong ứng dụng phần mềm nào cũng đều phải có và khi thiết kế, có thể tham khảo kiến trúc 3-layer architecture, là một kiến trúc phổ quát sử dụng để phân tách nhiệm vụ: Data Access ←→ Business Logic ←→ Presentation. Khi mở rộng thì theo nhu cầu, cần mở rộng ở layer nào sẽ giải quyết ở layer đó. Nếu phải mở rộng cả 3 layers thì phải rà soát và điều chỉnh thiết kế cho cả 3 layers. Thông thường layer Data Access mang tính ổn định hơn 2 layer còn lại.

Cảm ơn anh, ví dụ đơn hàng rất dễ hiểu ạ.

Cho em hỏi thêm một chút để rõ hơn: trong một số hệ thống em thấy người ta vẫn giữ reference tới entity gốc (ví dụ customer_id), nhưng các thông tin hiển thị trên record như tên khách hàng lại được snapshot tại thời điểm phát sinh.

Ví dụ trong Hợp đồng:

  • lưu tên khách hàng tại thời điểm ký để sau này không bị thay đổi nếu khách đổi tên
  • nhưng vẫn có field liên kết tới hồ sơ Khách hàng để có thể xem thêm các hợp đồng hoặc giao dịch khác của khách đó.

Như vậy record vừa giữ được dữ liệu đúng theo thời điểm, vừa không mất liên kết với master data.

Không biết trong Cleeksy DOP khi thiết kế solution mình có hay dùng cách này không anh?

Hi Hạnh,

Nhu cầu thiết kế như em mô tả ít xảy ra trên thực tế nhưng nếu muốn thiết kế như vậy trên Cleeksy DOP, có thể thực hiện như sau:

  • Tạo thêm field Văn bản (Text field) dùng để lưu tên khách hàng.
  • Từ app Đơn hàng, tạo field Tham chiếu 1 chiều (OneWayReference) đến app Khách hàng để liên kết và biết được đơn hàng là của khách hàng nào. Thiết lập ánh xạ (mapping) để khi khách hàng được chọn, hệ thống tự động sao chép tên khách hàng và lưu vào field Tên khách hàng ở trên.

Từ cách làm này, em có thể ứng dụng tương tự cho nhiều trường hợp phát sinh trong thực tế đặc thù của từng doanh nghiệp khác nhau.