Bỏ qua và tới nội dung chính
Thiết kế contract và DSL

Glossary, Domain, App, Rules, Workflows: đọc cây contract như thế nào

Một cây contract tốt không phải là bộ tài liệu dài dòng, mà là cấu trúc đủ rõ để đội ngũ đọc cùng một nghĩa và thi công ra hệ thống ít bất ngờ hơn. Bài viết này giải thích cách đọc và thiết kế các lớp glossary, domain, app, rules, workflows, policy và API để contract trở thành đầu vào vận hành được cho software factory.

Huỳnh Kim Đạt Huỳnh Kim Đạt
2 lượt xem 8 phút đọc
Glossary, Domain, App, Rules, Workflows: đọc cây contract như thế nào

TL;DR

Đọc cây contract theo thứ tự glossary → domain → app → rules → workflows → policy/RBAC → API. Mỗi lớp phải có vai trò riêng, ngôn ngữ nhất quán và liên kết traceability rõ ràng để contract thực sự dùng được cho thi công.

Key Takeaways

  • Glossary khóa nghĩa của thuật ngữ trước khi đi vào domain và workflow.
  • Domain contract mô tả thực thể, trạng thái và bất biến nghiệp vụ bền vững nhất.
  • Rule contract phải có cấu trúc đủ chặt để kiểm tra và triển khai được.
  • Workflow nên tham chiếu rule thay vì nhúng lại toàn bộ logic.
  • RBAC contract cần tách rõ role, permission, scope và constraint.
  • API contract nên xuất hiện sau khi domain, rule và workflow đã đủ rõ.
  • Contract tốt giúp tăng traceability và làm đầu ra code ít bất ngờ hơn.

Trong mô hình contract coding, giá trị của contract không nằm ở việc viết thật nhiều, mà ở việc tạo ra một cây định nghĩa đủ chặt để người đọc hiểu đúng phạm vi, máy có thể kiểm tra được, và đội thi công có thể triển khai mà không phải đoán ý. Khi nhìn vào một bộ contract có các lớp glossary, domain, app, rules và workflows, câu hỏi quan trọng không phải là “có đủ giấy tờ chưa”, mà là “đọc từ đâu để ra quyết định đúng”.

Một cây contract tốt phải giúp ba việc xảy ra cùng lúc: thống nhất ngôn ngữ, khóa phạm vi, và tăng khả năng traceability từ ý định nghiệp vụ sang DSL contract, RBAC contract, workflow contract và API contract. Nếu không làm được ba việc này, contract rất dễ trở thành tài liệu mô tả cho có, tách rời khỏi phần mềm thực tế.

1. Đọc cây contract theo thứ tự nào?

Cách an toàn nhất là đọc từ trên xuống dưới, đi từ ngôn ngữ chung đến hành vi cụ thể:

  1. Glossary: xác định thuật ngữ, tránh mỗi người hiểu một kiểu.
  2. Domain: xác định đối tượng, ranh giới, trạng thái và quy tắc cốt lõi.
  3. App: xác định ứng dụng cần hỗ trợ hành vi nào trong domain.
  4. Rules: mô tả điều kiện, ràng buộc, kiểm tra và hệ quả.
  5. Workflows: mô tả trình tự xử lý, vai trò tham gia và các bước chuyển trạng thái.
  6. Policy/RBAC: mô tả ai được làm gì, trong điều kiện nào.
  7. API contract: mô tả bề mặt giao tiếp cụ thể giữa hệ thống và bên ngoài.

Thứ tự này quan trọng vì nếu chưa khóa nghĩa ở glossary và domain, mọi lớp bên dưới đều dễ trôi. Workflow sẽ thành kể chuyện, rule sẽ thành ngoại lệ chắp vá, còn API sẽ phản ánh cấu trúc code ngẫu nhiên thay vì cấu trúc nghiệp vụ.

2. Các lớp contract cốt lõi và vai trò của từng lớp

Glossary contract

Glossary là lớp chống nhập nhằng. Nó không cần dài, nhưng phải dứt khoát. Mỗi thuật ngữ nên có một nghĩa ưu tiên, một ngữ cảnh sử dụng, và nếu cần thì có các từ bị cấm dùng lẫn.

Ví dụ, nếu hệ thống có các từ “đơn”, “yêu cầu”, “hồ sơ” và “phiếu”, glossary phải nói rõ khi nào là cùng một khái niệm, khi nào là các thực thể khác nhau. Nếu bỏ qua bước này, cùng một API có thể bị đặt tên sai ngay từ đầu.

Domain contract

Domain là nơi mô tả mô hình nghiệp vụ ở mức bền vững nhất: thực thể, giá trị, quan hệ, vòng đời và bất biến. Đây là phần trả lời câu hỏi: hệ thống đang quản lý cái gì và điều gì luôn phải đúng.

Một domain contract tốt thường nêu rõ:

  • Thực thể chính là gì.
  • Trạng thái nào hợp lệ.
  • Sự kiện nào làm đổi trạng thái.
  • Ràng buộc nào không được vi phạm.
  • Điểm nào cần traceability sang rule hoặc workflow.

App contract

App contract không thay domain, mà ánh xạ domain vào bề mặt sản phẩm cụ thể. Ở đây cần nói rõ ứng dụng hỗ trợ vai trò nào, màn hình hoặc module nào, hành động nào người dùng có thể thực hiện, và dữ liệu nào được hiển thị hay chỉnh sửa.

App contract giúp tránh lỗi phổ biến: mang toàn bộ sự phức tạp của domain đổ vào giao diện, hoặc ngược lại, để cấu trúc giao diện lấn ngược làm méo domain.

Rule contract

Rule contract là nơi viết các điều kiện và hệ quả có thể kiểm tra được. Đây phải là phần có cấu trúc cao, tránh văn phong mơ hồ kiểu “thường thì”, “có thể cân nhắc”, “nếu phù hợp”.

Một rule tốt thường có dạng:

  • Điều kiện đầu vào
  • Ngữ cảnh áp dụng
  • Biểu thức kiểm tra
  • Kết quả nếu đạt
  • Lỗi hoặc hệ quả nếu không đạt

Đây cũng là nơi DSL contract phát huy tác dụng, vì rule càng chuẩn hóa thì càng dễ kiểm thử, sinh validator hoặc đối chiếu với code.

Workflow contract

Workflow contract mô tả luồng đi của công việc theo thời gian. Nó nên trả lời bốn câu hỏi: ai khởi tạo, bước nào xảy ra trước, điều kiện nào cho phép chuyển bước, và khi thất bại thì quay về đâu.

Workflow không nên nhồi toàn bộ rule vào từng bước. Rule thuộc rule contract; workflow chỉ tham chiếu đến rule cần áp dụng ở mỗi chặng. Làm vậy mới giữ được cây contract rõ ràng và tái sử dụng được.

Policy và RBAC contract

RBAC contract cần mô tả vai trò, quyền, phạm vi dữ liệu và hạn chế hành động. Đây là phần rất dễ bị viết sơ sài, dẫn tới chênh lệch giữa tài liệu và thực thi. Cần tách rõ:

  • Role: ai là ai.
  • Permission: được làm gì.
  • Scope: được làm trên dữ liệu nào.
  • Constraint: giới hạn theo trạng thái, đơn vị, thời gian hoặc ownership.

Khi RBAC contract rõ, traceability từ nghiệp vụ sang triển khai phân quyền sẽ tốt hơn nhiều.

API contract

API contract chỉ nên xuất hiện sau khi domain, rules và workflow đã đủ chắc. Nếu viết API trước, nhóm rất dễ chốt endpoint theo nhu cầu màn hình hoặc thói quen framework, rồi sau đó phải vá ngược domain cho khớp code.

API contract cần nói rõ tài nguyên, hành động, input, output, lỗi, idempotency nếu có, và mối liên hệ với rule hoặc workflow tương ứng.

3. Cách khóa phạm vi và ngôn ngữ để contract dùng được cho thi công

Contract-first không có nghĩa là viết càng nhiều càng tốt. Điều quan trọng là khóa phạm vi đúng mức để software factory có thể dùng contract như nguồn sự thật làm việc.

Một số nguyên tắc hữu ích:

  • Mỗi lớp chỉ trả lời một loại câu hỏi. Glossary không ôm workflow. Workflow không viết lại domain. API không thay rule.
  • Dùng từ nhất quán. Một khái niệm chỉ nên có một tên ưu tiên.
  • Phân biệt bắt buộc và tham khảo. Cái gì máy hoặc người thi công phải tuân theo thì phải viết có cấu trúc.
  • Chỉ rõ biên giới. Phần nào thuộc domain, phần nào là quyết định UI, phần nào thuộc hạ tầng.
  • Liên kết được giữa các lớp. Mỗi workflow nên tham chiếu rule; mỗi API nên tham chiếu hành động hoặc bước workflow liên quan.

Khi làm được việc này, traceability không còn là khẩu hiệu. Ta có thể lần theo từ một hành động trên app, xuống rule, sang domain invariant, rồi ra API và kiểm thử.

4. Phần nào nên viết ngắn, phần nào bắt buộc phải có cấu trúc?

Không phải phần nào trong contract cũng cần chi tiết ngang nhau.

Nên viết ngắn

  • Mục tiêu bài toán hoặc bối cảnh kinh doanh tổng quan.
  • Giải thích tại sao tồn tại một module.
  • Ghi chú định hướng triển khai, miễn là không biến thành chỉ đạo vi mô cho code.

Những phần này chỉ cần đủ để người đọc hiểu bối cảnh, không nên kéo dài thành văn bản mơ hồ.

Bắt buộc phải rõ và có cấu trúc

  • Thuật ngữ nghiệp vụ.
  • Thực thể và trạng thái.
  • Ràng buộc và điều kiện kiểm tra.
  • Luồng chuyển bước.
  • Phân quyền.
  • Input/output và mã lỗi của API.

Nói ngắn gọn: phần nào ảnh hưởng trực tiếp đến quyết định thi công, kiểm thử hoặc vận hành thì phải có cấu trúc chặt.

5. Lỗi thường gặp khi viết contract

Ôm hết mọi thứ vào một chỗ

Nhiều tài liệu gọi là contract nhưng thực ra là một bản mô tả lớn chứa mọi thứ từ business goal, màn hình, API, database, rule, phân quyền và chi tiết code. Kết quả là không ai biết đâu là nguồn sự thật cho từng loại quyết định.

Bám sát code quá mức

Nếu contract chỉ phản chiếu tên class, tên service, tên table hoặc cấu trúc framework hiện tại, nó sẽ chết cùng vòng đời implementation. Contract đúng nghĩa phải bền hơn code và dẫn dắt code, không chỉ sao chép code.

Viết rule bằng ngôn ngữ mơ hồ

Những câu như “hệ thống nên kiểm tra hợp lệ” hoặc “nếu cần thì yêu cầu phê duyệt” không giúp ai triển khai đúng. Rule phải đo được, test được, và phản ánh được lỗi cụ thể.

Workflow kể chuyện nhưng không có điều kiện chuyển bước

Nếu workflow chỉ ghi “bộ phận A xử lý, sau đó chuyển bộ phận B”, nhóm triển khai vẫn phải tự đoán khi nào được chuyển, ai có quyền chuyển, và nếu thiếu dữ liệu thì sao.

Không nối được traceability

Một cây contract yếu thường khiến các lớp đứng rời nhau: domain một nơi, API một nơi, phân quyền một nơi. Khi phát sinh lỗi, rất khó truy ra nó bắt nguồn từ quyết định nào trong contract.

6. Ví dụ contract tốt và contract tệ khác nhau ở đâu?

Ví dụ tệ

Đơn hàng cần được xử lý đúng quy trình.
Nhân viên phù hợp có thể duyệt.
Hệ thống kiểm tra dữ liệu trước khi chuyển bước.
API trả về kết quả thành công hoặc thất bại.

Đoạn này nghe có vẻ hợp lý nhưng gần như không thi công được. “Đúng quy trình” là gì, “phù hợp” là vai trò nào, “kiểm tra dữ liệu” là kiểm tra điều gì, “thất bại” gồm các mã lỗi nào — tất cả đều bỏ ngỏ.

Ví dụ tốt

Glossary
- Request: yêu cầu nghiệp vụ do người dùng khởi tạo.
- Reviewer: vai trò có quyền thẩm định request trong phạm vi đơn vị của mình.

Domain
- Request có các trạng thái: draft, submitted, approved, rejected.
- Request chỉ được chuyển từ submitted sang approved hoặc rejected.

Rule
- R-REQ-001: Chỉ request có ít nhất 1 line item mới được submit.
- R-REQ-002: Reviewer chỉ được approve request thuộc đơn vị mình quản lý.

Workflow
- WF-REQ-APPROVAL:
  1. User tạo request ở trạng thái draft.
  2. User submit request nếu thỏa R-REQ-001.
  3. Reviewer approve hoặc reject nếu thỏa R-REQ-002.

RBAC
- role:user => create, edit own draft request, submit own draft request.
- role:reviewer => approve/reject submitted request within assigned scope.

API
- POST /requests/{id}/submit => áp dụng R-REQ-001.
- POST /requests/{id}/approve => áp dụng R-REQ-002.

Khác biệt lớn nhất là tính thi công được. Bản tốt không dài hơn quá nhiều, nhưng nó khóa được nghĩa, chỉ ra quy tắc, nối được workflow với RBAC contract và API contract, đồng thời hỗ trợ traceability tốt hơn.

7. Một cách nhìn thực dụng cho Midi Coder và contract coding

Trong bối cảnh Midi Coder, contract coding hiệu quả khi contract đủ chặt để trở thành đầu vào ổn định cho software factory, nhưng vẫn đủ gọn để con người có thể duy trì. Điều này đặc biệt đúng với DSL contract: chỉ nên đưa vào DSL những thứ cần chuẩn hóa, kiểm tra hoặc tái sử dụng; đừng cố biến mọi câu chữ thành cú pháp.

Nói cách khác, contract-first không phải là thay code bằng tài liệu. Nó là cách đưa ý định nghiệp vụ vào một cấu trúc có thể đọc, có thể kiểm tra, có thể truy vết và có thể triển khai nhất quán.

Kết luận

Muốn đọc cây contract đúng, hãy đi từ ngôn ngữ chung đến hành vi cụ thể: glossary, domain, app, rules, workflows, policy và cuối cùng là API. Muốn thiết kế cây contract tốt, hãy buộc từng lớp trả lời đúng một loại câu hỏi và nối chúng bằng traceability rõ ràng. Contract tốt không phải contract dày hơn, mà là contract khiến code đầu ra ít bất ngờ hơn.

Frequently Asked Questions

Nên đọc cây contract bắt đầu từ đâu?

Nên bắt đầu từ glossary để khóa thuật ngữ, sau đó sang domain để hiểu mô hình nghiệp vụ, rồi mới đọc app, rules, workflows, policy/RBAC và API.

Vì sao không nên viết API contract trước?

Nếu viết API trước, thiết kế rất dễ bị dẫn dắt bởi màn hình hoặc framework thay vì bởi domain và nghiệp vụ. Kết quả là API phản ánh code ngẫu nhiên hơn là ý định nghiệp vụ.

Phần nào trong contract bắt buộc phải có cấu trúc chặt?

Các phần ảnh hưởng trực tiếp đến thi công và kiểm thử như glossary, domain states, rule, workflow transitions, RBAC và input/output của API cần được viết có cấu trúc rõ ràng.

Dấu hiệu của một contract tệ là gì?

Contract tệ thường ôm quá nhiều thứ vào một chỗ, dùng ngôn ngữ mơ hồ, bám sát code hiện tại và không tạo được traceability giữa nghiệp vụ, rule, workflow, phân quyền và API.