Mở đầu — vì sao bài này quan trọng
Khi bạn còn là developer, bạn quen với việc đọc một tài liệu mô tả tính năng rồi tự lấp đầy các khoảng trống bằng phán đoán kỹ thuật của mình. "Đặt hàng thì chắc phải kiểm tra tồn kho", "thanh toán thất bại thì rollback giao dịch" — bạn ngầm hiểu những điều đó vì bạn là người sẽ viết code. Nhưng khi chuyển sang vai trò BA, bạn không còn là người viết code nữa. Bạn là người phải viết ra tài liệu để người khác — có thể là một dev offshore ở thành phố khác, một tester chưa từng gặp khách hàng, hay một dev mới vào dự án sau bạn ba tháng — đọc và hiểu chính xác hệ thống phải làm gì, trong mọi tình huống.
Đây chính là lúc Use Case Specification (đặc tả tình huống sử dụng) trở thành vũ khí mạnh nhất của bạn. Use Case không chỉ là "user click nút rồi hệ thống làm gì". Nó là một bản hợp đồng chi tiết giữa người dùng và hệ thống, mô tả đầy đủ luồng chính, các luồng thay thế, các luồng ngoại lệ, điều kiện trước và sau. Một Use Case Spec tốt loại bỏ được phần lớn câu hỏi "thế còn trường hợp X thì sao?" mà dev và tester thường đặt ra giữa sprint.
Tin tốt cho bạn: nền tảng developer khiến bạn viết Use Case tốt hơn hầu hết BA xuất thân từ ngành kinh doanh. Bạn vốn đã quen tư duy theo điều kiện rẽ nhánh, theo các trạng thái lỗi, theo input/output. Cái bạn cần học không phải là tư duy logic — cái đó bạn đã có — mà là cách diễn đạt logic đó bằng ngôn ngữ nghiệp vụ, ở mức trừu tượng phù hợp, theo một template chuẩn mà cả team có thể tin tưởng. Bài này dạy bạn đúng điều đó.
Khái niệm cốt lõi
Use Case là gì và khác gì với User Story
Đừng nhầm Use Case với User Story (bạn sẽ học sâu về User Story chuẩn INVEST ở bài khác). User Story ngắn gọn: "Là khách hàng, tôi muốn đặt hàng để nhận sản phẩm". Nó là lời nhắc để bắt đầu cuộc trò chuyện. Use Case thì ngược lại — nó là tài liệu đầy đủ mô tả toàn bộ tương tác giữa actor (tác nhân) và hệ thống để đạt được một mục tiêu, bao gồm mọi nhánh có thể xảy ra.
Use Case phù hợp khi nghiệp vụ phức tạp, có nhiều luồng rẽ nhánh, nhiều điều kiện, nhiều bên liên quan — ví dụ quy trình đặt hàng có thanh toán, hoàn tiền, hủy đơn; hoặc quy trình eKYC định danh khách hàng ngân hàng. Với những tính năng đơn giản, User Story là đủ; nhưng khi mỗi bước có thể "đi sai", Use Case Specification mới đủ sức chứa toàn bộ độ phức tạp đó một cách có cấu trúc.
Các thành phần của một Use Case Specification
Một Use Case Spec chuẩn gồm các trường sau. Hãy ghi nhớ template này như bạn từng ghi nhớ cấu trúc một REST endpoint:
| Trường | Mô tả | Ví dụ |
|---|---|---|
| UC ID | Mã định danh duy nhất, để truy vết | UC-001 |
| Name | Tên ngắn gọn dạng động từ + đối tượng | Customer Place Order (Khách hàng đặt hàng) |
| Description | Một câu mô tả mục tiêu nghiệp vụ | Cho phép khách hàng đã đăng nhập tạo và xác nhận đơn hàng |
| Primary Actor | Tác nhân chính khởi tạo use case | Khách hàng |
| Secondary Actors | Các hệ thống/người tham gia hỗ trợ | Cổng thanh toán VNPay, hệ thống kho |
| Stakeholders & Interests | Các bên liên quan và mối quan tâm | Khách hàng: nhận đúng hàng; Doanh nghiệp: thu đúng tiền |
| Preconditions | Điều kiện phải đúng trước khi bắt đầu | Khách đã đăng nhập, giỏ hàng có ít nhất 1 sản phẩm |
| Postconditions | Trạng thái hệ thống sau khi kết thúc thành công | Đơn hàng được tạo với trạng thái "Đã xác nhận", tồn kho đã trừ |
| Trigger | Sự kiện kích hoạt use case | Khách nhấn nút "Thanh toán" tại giỏ hàng |
| Main Flow (Basic Flow) | Luồng thành công lý tưởng, đánh số bước | (xem dưới) |
| Alternative Flows | Các nhánh thay thế hợp lệ | Khách dùng mã giảm giá |
| Exception Flows | Các luồng lỗi/ngoại lệ | Thanh toán thất bại, hết hàng |
| Business Rules | Quy tắc nghiệp vụ áp dụng | BR-01: đơn dưới 300k tính phí ship 30k |
| Non-Functional | Yêu cầu phi chức năng liên quan | Trang xác nhận phải tải trong 2 giây |
| Frequency / Priority | Tần suất dùng và mức ưu tiên | Cao / Must-have |
Main Flow: xương sống của Use Case
Main Flow là luồng đi đúng từ đầu đến cuối, không lỗi, không rẽ nhánh — thứ mà sách BABOK gọi là "happy path". Quy tắc vàng khi viết Main Flow: mỗi bước là một hành động của một tác nhân, luân phiên giữa actor và hệ thống, viết ở thể chủ động, đánh số tuần tự. Ví dụ:
- Khách hàng nhấn nút "Thanh toán".
- Hệ thống hiển thị trang xác nhận với danh sách sản phẩm, tổng tiền và địa chỉ giao hàng.
- Khách hàng chọn phương thức thanh toán và nhấn "Đặt hàng".
- Hệ thống kiểm tra tồn kho cho từng sản phẩm.
- Hệ thống tạo đơn hàng với trạng thái "Chờ thanh toán".
- Hệ thống chuyển hướng khách sang cổng thanh toán VNPay.
- Khách hàng hoàn tất thanh toán trên VNPay.
- Hệ thống nhận xác nhận thanh toán thành công, cập nhật đơn sang "Đã xác nhận" và trừ tồn kho.
- Hệ thống gửi email xác nhận đơn hàng cho khách.
Alternative Flow và Exception Flow
Đây là nơi nền dev của bạn tỏa sáng. Alternative Flow là nhánh hợp lệ thay thế — vẫn đạt mục tiêu nhưng đi đường khác (ví dụ: khách áp mã giảm giá ở bước 2). Exception Flow là khi có lỗi khiến use case không đạt mục tiêu (ví dụ: thanh toán thất bại). Cách đánh số chuẩn là gắn nhánh vào bước của Main Flow:
- 4a. Một sản phẩm hết hàng: Tại bước 4, nếu tồn kho không đủ, hệ thống hiển thị thông báo "Sản phẩm X đã hết hàng" và quay lại giỏ hàng (bước kết thúc, không tạo đơn).
- 7a. Thanh toán thất bại: Tại bước 7, nếu VNPay trả về lỗi, hệ thống giữ đơn ở trạng thái "Chờ thanh toán", hiển thị thông báo lỗi và cho phép thử lại trong 15 phút.
Tình huống thực tế
Ví dụ 1: Tiki và bài học về Exception Flow bị bỏ sót
Một nhóm BA tại một sàn thương mại điện tử lớn ở Việt Nam (tạm gọi theo mô hình Tiki) từng viết Use Case "Đặt hàng" rất gọn gàng cho Main Flow, nhưng chỉ liệt kê đúng một Exception "thanh toán thất bại". Khi tính năng lên production, họ gặp một sự cố: khách đặt hàng vào lúc 23:58, hệ thống tạo đơn ở bước 5 với trạng thái "Chờ thanh toán", nhưng khách bỏ ngang không thanh toán. Tồn kho đã bị "giữ" cho đơn đó, khiến 40 sản phẩm flash sale bị treo suốt đêm, khách khác không mua được.
Vấn đề không nằm ở code — dev viết đúng theo spec. Vấn đề là Use Case Spec thiếu một Exception Flow: "Khách không hoàn tất thanh toán trong N phút thì hủy đơn và hoàn tồn kho". BA đã quên rằng thời gian cũng là một dạng trigger ngoại lệ.
Bài học: Khi viết Exception Flow, đừng chỉ nghĩ "actor làm sai gì". Hãy hỏi thêm: điều gì xảy ra nếu actor không làm gì cả? Timeout, bỏ ngang, mất kết nối — đó là những ngoại lệ mà dev hay quên nhưng lại gây hậu quả nghiệp vụ lớn. Với nền dev, bạn có lợi thế: bạn vốn quen với khái niệm timeout và transaction rollback, hãy đem tư duy đó vào spec.
Ví dụ 2: Dự án eKYC ngân hàng và sức mạnh của Preconditions
Một ngân hàng số tại TP.HCM triển khai eKYC (định danh điện tử) cho mở tài khoản từ xa. BA — vốn là một backend developer chuyển sang — viết Use Case "UC-KYC-01: Khách hàng định danh điện tử". Điều khiến tài liệu của anh được khen ngợi là phần Preconditions cực kỳ chặt chẽ:
- Khách hàng đã tải app và cấp quyền camera.
- Thiết bị có kết nối internet ổn định tối thiểu 1 Mbps.
- Khách hàng từ 18 tuổi trở lên (kiểm tra ở use case đăng ký trước đó).
Bài học: Precondition tốt là cách bạn "thuê ngoài" độ phức tạp sang use case khác, giữ cho use case hiện tại tập trung. Và Business Rules nên được tách riêng, đánh mã, rồi tham chiếu từ trong flow — đừng nhúng cứng con số "18 tuổi" hay "1 tài khoản" vào giữa các bước, vì khi quy định thay đổi bạn chỉ cần sửa một chỗ. Đây chính là nguyên tắc DRY mà bạn đã thuộc nằm lòng từ thời viết code.
Ví dụ 3: Startup giao đồ ăn và cái bẫy "viết Use Case như viết code"
Một startup giao đồ ăn nhỏ ở Đà Nẵng có anh BA mới chuyển từ Android dev. Anh viết Use Case "Tài xế nhận đơn" nhưng vô tình mang nguyên thói quen lập trình vào: các bước mô tả "Hệ thống gọi API assignOrder()", "Hệ thống set field status = 'ASSIGNED' trong bảng orders", "Hệ thống publish message lên Kafka topic order.assigned".
Khi tài liệu này đến tay chị Product Owner (không phải dân kỹ thuật) và team vận hành, không ai hiểu nổi. Tệ hơn, khi team quyết định đổi từ Kafka sang RabbitMQ, toàn bộ Use Case phải viết lại — dù nghiệp vụ không hề thay đổi.
Sau khi được mentor góp ý, anh viết lại bước 4 từ "Hệ thống publish message lên Kafka topic order.assigned" thành "Hệ thống thông báo cho nhà hàng rằng đơn đã có tài xế nhận". Nghiệp vụ giữ nguyên, chi tiết triển khai biến mất khỏi spec.
Bài học: Use Case mô tả cái gì (what) chứ không phải làm thế nào (how). Tên API, tên bảng, tên message broker là chi tiết triển khai — chúng thuộc về tài liệu kỹ thuật của dev, không thuộc Use Case Spec. Đây là cú "shift mindset" khó nhất với dev mới chuyển: bạn phải chủ động kìm bản năng mô tả giải pháp kỹ thuật và thay bằng mô tả hành vi nghiệp vụ ở mức mà cả người không biết code cũng hiểu.
Hướng dẫn từng bước
Khi nhận một yêu cầu cần đặc tả, hãy làm theo trình tự sau:
- Xác định mục tiêu và đặt tên. Hỏi: "Actor muốn đạt được điều gì khi dùng tính năng này?" Đặt tên use case dạng động từ + đối tượng từ góc nhìn actor (ví dụ "Đặt hàng", không phải "Xử lý đơn hàng" — vế sau là góc nhìn hệ thống).
- Liệt kê actor. Ai khởi tạo (primary)? Ai/hệ thống nào hỗ trợ (secondary)? Đừng quên các hệ thống bên ngoài như cổng thanh toán, dịch vụ SMS.
- Viết Preconditions và Postconditions. Precondition là cái phải đúng trước; Postcondition là cam kết về trạng thái sau khi thành công. Viết Postcondition trước đôi khi giúp bạn rõ mục tiêu hơn.
- Viết Main Flow. Đi từ trigger đến postcondition theo happy path. Luân phiên actor–hệ thống, mỗi bước một hành động, không rẽ nhánh. Cố gắng giữ trong 7–10 bước; nếu dài hơn, có thể use case của bạn đang ôm quá nhiều việc.
- Rút ra Alternative Flows. Duyệt lại từng bước Main Flow và hỏi "có cách hợp lệ nào khác để làm bước này không?". Ví dụ: thanh toán bằng ví thay vì thẻ.
- Rút ra Exception Flows. Duyệt lại từng bước và hỏi "bước này có thể thất bại thế nào?". Nhớ ba nhóm: lỗi do actor (nhập sai), lỗi do hệ thống/bên thứ ba (timeout, API down), và "không hành động" (bỏ ngang, hết giờ).
- Tách Business Rules. Mọi con số, ngưỡng, điều kiện nghiệp vụ — đánh mã (BR-01, BR-02) và tham chiếu từ trong flow.
- Bổ sung phi chức năng và rà soát. Thêm yêu cầu hiệu năng/bảo mật liên quan, rồi đọc lại với câu hỏi: "Một dev và một tester chưa từng gặp khách hàng có đủ thông tin để build và test không?"
Lỗi thường gặp & mẹo
Lỗi 1 — Nhồi chi tiết kỹ thuật vào flow. Như ví dụ startup Đà Nẵng: tên API, tên bảng, framework không thuộc Use Case. Mẹo: nếu một câu trong flow vẫn đúng khi bạn đổi hoàn toàn công nghệ nền, thì câu đó viết đúng tầm.
Lỗi 2 — Main Flow chứa rẽ nhánh "nếu...thì". Khi thấy chữ "nếu" trong Main Flow, gần như chắc chắn bạn đang viết nhầm một Alternative/Exception Flow vào đó. Tách ra.
Lỗi 3 — Quên Exception "không hành động". Timeout, bỏ ngang, mất mạng giữa chừng. Với nền dev, đây là điểm bạn nên mạnh hơn người khác — đừng để vuột.
Lỗi 4 — Use Case quá to. Nếu Main Flow dài 20 bước với 15 exception, bạn đang gộp nhiều mục tiêu. Tách thành nhiều use case (ví dụ "Đặt hàng" tách khỏi "Quản lý địa chỉ giao hàng").
Lỗi 5 — Bước viết ở thể bị động, mơ hồ chủ ngữ. "Đơn hàng được xác nhận" — ai xác nhận? Viết "Hệ thống xác nhận đơn hàng". Mỗi bước phải rõ ai là người hành động.
Mẹo vàng: Sau khi viết xong, đưa cho một dev và một tester đọc thử. Nếu họ hỏi "thế còn trường hợp X?", đó là một exception flow bạn bỏ sót — bổ sung ngay. Use Case Spec trưởng thành qua chính những câu hỏi đó.
Bài tập thực hành
Chọn một trong hai tình huống sau và viết một Use Case Specification đầy đủ theo template trong bài (đủ các trường từ UC ID đến Business Rules):
- "Rút tiền tại ATM" — một nghiệp vụ kinh điển, nhiều exception (thẻ bị nuốt, sai PIN 3 lần, hết tiền trong máy, hết tiền trong tài khoản, mất điện giữa giao dịch).
- "Đặt lịch hẹn với bác sĩ qua app" — với secondary actor là hệ thống lịch của bác sĩ và dịch vụ gửi SMS nhắc lịch.
Tóm tắt
Use Case Specification là tài liệu mô tả đầy đủ tương tác giữa actor và hệ thống để đạt một mục tiêu nghiệp vụ, gồm Main Flow (happy path), Alternative Flows (nhánh hợp lệ), Exception Flows (lỗi và ngoại lệ), cùng các trường bao quanh như Preconditions, Postconditions, Business Rules. Nó khác User Story ở độ chi tiết: User Story khởi đầu cuộc trò chuyện, còn Use Case ghi lại toàn bộ kết quả của cuộc trò chuyện đó.
Ba nguyên tắc cốt lõi cần khắc cốt ghi tâm: (1) mô tả cái gì, không phải làm thế nào — bỏ chi tiết kỹ thuật ra khỏi flow; (2) giữ Main Flow sạch, đẩy mọi rẽ nhánh xuống Alternative/Exception; (3) đừng quên các ngoại lệ "không hành động" như timeout và bỏ ngang. Nền tảng developer cho bạn lợi thế tư duy logic, xử lý lỗi và DRY — nhiệm vụ của bạn là dịch lợi thế đó sang ngôn ngữ nghiệp vụ mà cả người không biết code cũng đọc được. Khi làm được điều đó, Use Case Spec của bạn sẽ trở thành nguồn chân lý duy nhất mà dev, tester và khách hàng đều tin tưởng.