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

10 lỗi thường gặp khi mới viết contract theo DSL

Khi mới tiếp cận contract-first và DSL, nhiều đội ngũ dễ biến contract thành tài liệu dài dòng, khó thi công hoặc bám sát code đến mức mất giá trị thiết kế. Bài viết này chỉ ra 10 lỗi thường gặp và cách viết contract ngắn, rõ, có cấu trúc để tăng traceability và giảm bất ngờ ở đầu ra.

Huỳnh Kim Đạt Huỳnh Kim Đạt
8 lượt xem 8 phút đọc
10 lỗi thường gặp khi mới viết contract theo DSL

TL;DR

Người mới viết contract theo DSL thường mắc lỗi ở phạm vi, thuật ngữ, cấu trúc rule, workflow, policy và API. Muốn contract dùng được cho thi công, cần khóa ngôn ngữ, tách lớp contract rõ ràng, mô tả có cấu trúc và duy trì traceability từ nghiệp vụ đến code và test.

Key Takeaways

  • Contract theo DSL phải phục vụ thi công, không phải chỉ để mô tả cho đẹp.
  • Cần tách rõ các lớp domain, app, rule, workflow, policy và API.
  • Phạm vi quá rộng và ngôn ngữ không nhất quán là hai lỗi nền tảng.
  • Rule, workflow và RBAC contract phải có cấu trúc đủ rõ để kiểm chứng.
  • API contract nên ổn định ở mức giao tiếp, không bám chặt chi tiết nội bộ của code.
  • Ví dụ đúng-sai, mã lỗi và traceability giúp contract trở thành tài sản dùng được.

Viết contract theo DSL không phải để tạo thêm giấy tờ. Mục tiêu của contract là giúp đội ngũ chốt được phạm vi, ngôn ngữ, quy tắc và cách thi công sao cho đầu ra ít bất ngờ hơn. Trong cách làm contract-first của Midi Coder hay mô hình software factory, contract tốt phải đủ rõ để người phân tích, người viết code, người kiểm thử và người vận hành cùng hiểu một cách gần như giống nhau.

Khi mới bắt đầu, lỗi phổ biến nhất là hoặc viết quá chung chung, hoặc viết quá sát code. Cả hai thái cực này đều làm contract mất tác dụng. Muốn tránh điều đó, trước hết cần hiểu các lớp contract cốt lõi và vai trò của từng lớp.

Các lớp contract cốt lõi trong DSL

  • Domain contract: định nghĩa thuật ngữ, thực thể, trạng thái, ràng buộc dữ liệu và các khái niệm nghiệp vụ nền.
  • App contract: mô tả năng lực hệ thống ở mức use case, module, trách nhiệm và ranh giới ứng dụng.
  • Rule contract: diễn đạt các quy tắc tính toán, kiểm tra, điều kiện hợp lệ hoặc điều kiện từ chối.
  • Workflow contract: mô tả luồng trạng thái, bước xử lý, tác nhân tham gia, điều kiện chuyển bước và điểm kiểm soát.
  • Policy hoặc RBAC contract: nêu rõ ai được làm gì, trên dữ liệu nào, trong điều kiện nào.
  • API contract: chốt đầu vào, đầu ra, lỗi, mã trạng thái, cấu trúc payload và kỳ vọng tích hợp.

Mỗi lớp có nhiệm vụ riêng. Nếu trộn lẫn tất cả vào một chỗ, contract sẽ vừa dài vừa khó dùng cho thi công. Ngược lại, nếu chia lớp hợp lý, đội ngũ sẽ có traceability tốt hơn từ nghiệp vụ đến code và test.

10 lỗi thường gặp khi mới viết contract theo DSL

1. Ôm phạm vi quá rộng ngay từ đầu

Nhiều người mới muốn contract phải bao hết toàn bộ hệ thống trong một tài liệu. Kết quả là phạm vi mơ hồ, nhiều chỗ ghi cho có, không đủ chi tiết để thi công.

Cách tránh: khóa phạm vi rõ cho từng contract: một domain, một use case, một workflow hay một API cụ thể. Contract nhỏ nhưng dùng được sẽ tốt hơn contract lớn nhưng không ai dám thi công.

2. Không khóa ngôn ngữ và thuật ngữ

Nếu cùng một khái niệm mà lúc gọi là đơn hàng, lúc gọi là yêu cầu mua, lúc gọi là order, contract sẽ nhanh chóng gây hiểu sai. DSL chỉ có giá trị khi từ vựng được khóa và dùng nhất quán.

Cách tránh: tạo glossary ngắn trong domain contract, định nghĩa rõ thuật ngữ chính, tên trạng thái, tên hành động và tên vai trò. Một thuật ngữ nên có một nghĩa ưu tiên.

3. Trộn mục tiêu nghiệp vụ với chi tiết cài đặt

Người mới thường viết những câu như “sử dụng Redis để lưu trạng thái chờ duyệt” ngay trong contract nghiệp vụ. Đây là quyết định kỹ thuật, không phải lúc nào cũng nên có trong DSL nghiệp vụ.

Cách tránh: contract nên chốt điều cần đạt và ràng buộc cần giữ. Chi tiết triển khai chỉ nên xuất hiện khi nó là một phần của cam kết giao tiếp hoặc ảnh hưởng trực tiếp đến hành vi hệ thống.

4. Viết rule bằng văn xuôi, không có cấu trúc

Quy tắc kiểu “nếu hợp lệ thì cho qua, còn không thì báo lỗi phù hợp” nghe có vẻ đúng nhưng không giúp được cho code hay test.

Cách tránh: rule contract cần có cấu trúc tối thiểu: điều kiện đầu vào, biểu thức kiểm tra, kết quả hợp lệ, mã lỗi hoặc lý do từ chối. Những phần này nên ngắn nhưng phải rõ.

5. Không chỉ rõ đầu vào, đầu ra và trạng thái

Nhiều contract mô tả hành động nhưng bỏ qua dữ liệu cần có trước khi thực hiện, dữ liệu nào được tạo ra sau khi thực hiện, hay trạng thái nào thay đổi.

Cách tránh: với mỗi use case hoặc API, cần nêu rõ input bắt buộc, output kỳ vọng, side effect và trạng thái trước-sau. Đây là nền tảng để làm traceability sang test case.

6. Workflow chỉ có các bước, không có điều kiện chuyển bước

Liệt kê “tạo, duyệt, xử lý, đóng” chưa đủ. Nếu không có điều kiện chuyển trạng thái, workflow sẽ chỉ là sơ đồ đẹp mắt nhưng không dùng được.

Cách tránh: workflow contract nên ghi rõ actor, precondition, postcondition, trigger, guard condition và các nhánh lỗi. Khi đó workflow mới thi công được và không mơ hồ với QA.

7. RBAC hoặc policy viết quá chung chung

Các câu như “admin có toàn quyền” hoặc “manager được duyệt hồ sơ” thường thiếu bối cảnh dữ liệu, điều kiện và phạm vi thao tác.

Cách tránh: policy contract nên diễn đạt theo mẫu: vai trò nào, hành động nào, trên loại tài nguyên nào, trong điều kiện nào, bị từ chối trong trường hợp nào. Đây là điểm rất quan trọng với workflow contract và API contract.

8. API contract bám sát code quá mức

Một lỗi khác là lấy thẳng tên class, tên method, hoặc cấu trúc nội bộ của service đưa vào API contract. Điều này khiến contract đổi theo code thay vì dẫn dắt code.

Cách tránh: API contract nên tập trung vào giao tiếp ổn định với bên dùng API: endpoint, method, input, output, validation, lỗi và versioning. Tránh lộ chi tiết nội bộ nếu chúng không phải cam kết công khai.

9. Thiếu ví dụ hợp lệ, ví dụ lỗi và dấu vết kiểm chứng

Contract chỉ có mô tả mà không có ví dụ sẽ khiến mỗi người tự diễn giải theo cách riêng. Thiếu traceability cũng làm khó việc đối chiếu giữa contract, code và test.

Cách tránh: thêm ví dụ payload đúng, payload sai, tình huống biên và mã lỗi mong đợi. Đồng thời liên kết rule với workflow, workflow với API, API với test. Đó là cách biến contract thành tài sản dùng được trong software factory.

10. Không chốt nguyên tắc thay đổi và versioning

Người mới thường nghĩ viết xong contract là đủ. Nhưng khi nghiệp vụ thay đổi, không có quy tắc versioning thì các đội tích hợp sẽ gặp rủi ro lớn.

Cách tránh: nêu rõ phần nào được phép mở rộng, phần nào là breaking change, cách đặt version và cách thông báo thay đổi. Contract tốt không chỉ mô tả hiện tại mà còn chuẩn bị cho thay đổi có kiểm soát.

Những phần nên viết ngắn, những phần bắt buộc phải rõ

Nên viết ngắn: bối cảnh, mục tiêu, mô tả module, giải thích tổng quan. Những phần này chỉ cần đủ để đặt ngữ cảnh.

Bắt buộc phải rõ và có cấu trúc: thuật ngữ domain, input-output, trạng thái, rule, điều kiện chuyển bước, quyền truy cập, lỗi, ví dụ và versioning. Đây là các phần quyết định contract có thi công được hay không.

Ví dụ một contract tốt và một contract tệ

Ví dụ tệ:

Đơn nghỉ phép cần được duyệt bởi quản lý.
Nếu hợp lệ thì hệ thống cập nhật trạng thái.
Admin có thể thao tác toàn bộ dữ liệu.

Đoạn này quá ngắn nhưng không rõ: hợp lệ là thế nào, ai được duyệt, chuyển sang trạng thái nào, khi nào bị từ chối, admin được làm gì trong điều kiện nào.

Ví dụ tốt:

domain LeaveRequest {
  states: Draft, Submitted, Approved, Rejected
}

rule SubmitLeaveRequest {
  input: employee_id, from_date, to_date, reason
  valid_when: from_date <= to_date and reason is not empty
  on_invalid: LEAVE_REQUEST_INVALID
  output: leave_request_id, state=Submitted
}

workflow LeaveApproval {
  actor: Manager
  transition: Submitted -> Approved when manager_of(employee_id)=current_user
  transition: Submitted -> Rejected when manager_of(employee_id)=current_user
}

policy LeaveRequestAccess {
  Employee can create own LeaveRequest
  Manager can approve team LeaveRequest
  Admin can read all LeaveRequest
}

Ví dụ tốt không cần dài, nhưng đủ cấu trúc để đội phát triển hiểu việc phải làm, đội QA biết phải kiểm gì, và đội tích hợp biết API hoặc hành vi nào là cam kết.

Cách viết contract để dùng được cho thi công

  1. Chốt phạm vi thật nhỏ và rõ.
  2. Khóa thuật ngữ trước khi khóa cấu trúc.
  3. Tách domain, rule, workflow, policy và API theo đúng vai trò.
  4. Mọi rule quan trọng đều phải có điều kiện và kết quả rõ ràng.
  5. Mọi workflow đều phải có trạng thái và điều kiện chuyển bước.
  6. Mọi policy đều phải nêu rõ actor, action, resource, condition.
  7. Mọi API đều cần ví dụ request, response và error.
  8. Luôn để lại dấu vết kiểm chứng giữa contract, code và test.

Điểm quan trọng nhất là không biến DSL thành nơi chứa mọi thứ. DSL không thay thế hoàn toàn tài liệu kỹ thuật, càng không phải nơi sao chép code. Nó là lớp contract đủ chặt để nhiều bên cùng thi công trên một cách hiểu thống nhất.

Kết luận

Contract tốt là contract khiến code đầu ra ít bất ngờ hơn. Nếu một contract giúp đội ngũ thống nhất ngôn ngữ, khóa phạm vi, mô tả được rule, workflow, RBAC contract và API contract theo cách có thể kiểm chứng, thì đó là contract có giá trị. Nếu contract chỉ để đọc cho biết, quá rộng hoặc quá bám code, nó sẽ nhanh chóng trở thành giấy tờ vô dụng. Khi mới bắt đầu với DSL, hãy ưu tiên tính rõ ràng, tính cấu trúc và khả năng traceability hơn là độ dài.

Frequently Asked Questions

Viết contract theo DSL có phải là viết tài liệu thay cho code không?

Không. Contract theo DSL là lớp cam kết và thiết kế có cấu trúc để định hướng code, test và tích hợp. Nó không thay thế hoàn toàn code hay tài liệu kỹ thuật chi tiết.

Nên bắt đầu từ loại contract nào khi đội ngũ mới áp dụng contract-first?

Nên bắt đầu từ domain contract và một workflow hoặc API nhỏ, vì đây là nơi dễ nhìn thấy giá trị của việc khóa thuật ngữ, input-output và quy tắc chuyển trạng thái.

Làm sao biết một contract đã đủ tốt để thi công?

Nếu từ contract có thể suy ra rõ input, output, rule, trạng thái, quyền truy cập, lỗi và ví dụ kiểm thử mà không cần đoán thêm quá nhiều, contract đó đã đủ tốt để đưa vào thi công.