Từ User Stories đến Schema Cơ Sở Dữ Liệu: Phương Pháp Hướng Dẫn bởi AI
Tìm hiểu phương pháp thực tiễn để chuyển user stories, thực thể và workflow thành sơ đồ cơ sở dữ liệu rõ ràng, và cách suy luận AI giúp kiểm tra lỗ hổng và ràng buộc.

Những gì bạn đang xây: một schema phù hợp với công việc thực tế
Một sơ đồ cơ sở dữ liệu là kế hoạch cho cách app của bạn sẽ ghi nhớ thông tin. Nói ngắn gọn, nó là:
- Bảng (Tables): các “xô” thông tin (Customers, Orders, Tickets)
- Trường (Fields / columns): các chi tiết bạn lưu về mỗi thứ (customer_name, order_date)
- Quan hệ (Relationships): cách các xô kết nối với nhau (một Order thuộc về một Customer; một Customer có thể có nhiều Orders)
Khi schema phản ánh công việc thực tế, nó mô tả những gì mọi người thực sự làm—tạo, xem xét, duyệt, lên lịch, phân công, hủy—thay vì những gì nghe có vẻ gọn gàng trên bảng trắng.
Tại sao bắt đầu từ user stories?
User stories và acceptance criteria mô tả nhu cầu thực bằng ngôn ngữ đơn giản: ai làm gì, và “xong” nghĩa là gì. Nếu bạn lấy chúng làm nguồn, schema sẽ ít có khả năng bỏ sót chi tiết quan trọng (ví dụ: “chúng ta phải theo dõi ai đã duyệt hoàn tiền” hoặc “một booking có thể được lên lịch lại nhiều lần”).
Bắt đầu từ stories cũng giúp bạn trung thực về phạm vi. Nếu điều gì đó không nằm trong stories (hoặc workflow), hãy coi nó là tuỳ chọn thay vì âm thầm xây dựng một mô hình phức tạp “phòng khi cần”.
AI có thể và không thể làm gì ở đây
AI có thể giúp bạn nhanh hơn bằng cách:
- Trích xuất các thực thể ứng viên (những “điều” quan trọng trong stories)
- Gợi ý các trường ngụ ý trong acceptance criteria (timestamps, statuses, references)
- Phát hiện các quan hệ và lỗ hổng khả dĩ (“bạn nhắc đến approvals nhưng không lưu approver”)
AI không thể đáng tin hoàn toàn để:
- Biết các quy tắc nghiệp vụ ẩn hoặc các trường hợp biên bạn không ghi lại
- Chọn mức chi tiết “đúng” mà không có đánh đổi (đơn giản vs linh hoạt)
- Đảm bảo schema phù hợp nhu cầu báo cáo, bảo mật, hoặc tuân thủ
Hãy coi AI là trợ thủ mạnh, không phải người quyết định.
Nếu bạn muốn biến trợ thủ đó thành động lực, một nền tảng vibe-coding như Koder.ai có thể giúp bạn đi từ quyết định schema tới một app React + Go + PostgreSQL hoạt động nhanh hơn—và vẫn giữ bạn kiểm soát mô hình, ràng buộc và migrations.
Thiết lập kỳ vọng: lặp đi lặp lại, không phải một lần xong
Thiết kế schema là một vòng lặp: phác thảo → kiểm tra với stories → tìm dữ liệu thiếu → tinh chỉnh. Mục tiêu không phải là đầu ra hoàn hảo ngay lần đầu; mà là một mô hình bạn có thể truy nguồn về từng user story và tự tin nói: “Có, chúng ta có thể lưu mọi thứ workflow này cần—và giải thích vì sao mỗi bảng tồn tại.”
Đầu vào: User Stories, Acceptance Criteria và Ví dụ Thực tế
Trước khi biến yêu cầu thành bảng, hãy làm rõ bạn đang mô tả gì. Một schema tốt hiếm khi bắt đầu từ trang trắng—nó bắt đầu từ công việc cụ thể mọi người làm và bằng chứng bạn sẽ cần sau này (màn hình, kết xuất, và các trường hợp biên).
Các đầu vào bạn nên gom vào một nơi
User stories là tiêu đề, nhưng không đủ. Gom:
- User stories + roles (ai làm gì và vì sao)
- Acceptance criteria (những điều “phải đúng”)
- Forms/screens (các trường người dùng nhập, chọn, hoặc nhìn thấy)
- Reports/exports (cần tóm tắt, nhóm, lọc gì)
- Ví dụ thực tế (mẫu đơn hàng, hóa đơn, ticket, lịch—bất cứ thứ gì đại diện)
Nếu dùng AI, những đầu vào này giữ cho mô hình có cơ sở. AI có thể đề xuất thực thể và trường nhanh, nhưng nó cần artifacts thực tế để tránh phát minh cấu trúc không khớp sản phẩm.
Acceptance criteria: nguồn ẩn của các ràng buộc
Acceptance criteria thường chứa những quy tắc cơ sở dữ liệu quan trọng nhất, ngay cả khi không nói trực tiếp về dữ liệu. Tìm các câu như:
- “Email phải là duy nhất” (uniqueness)
- “Status có thể là Draft, Submitted, Approved” (giá trị cho phép)
- “Chỉ managers được duyệt” (permissions, có thể cần trường audit)
- “Không được xóa invoice có payments” (quy tắc tham chiếu)
Những sai lầm thường gặp cần sửa sớm
Stories mơ hồ (“As a user, I can manage projects”) che khuất nhiều thực thể và workflow. Lỗ hổng thường gặp khác là các trường hợp biên thiếu như cancellations, retries, partial refunds, hoặc reassignment.
Kiểm tra nhanh chất lượng story (trước khi mô hình hóa)
- Actor/role rõ ràng.
- Đối tượng cụ thể (không phải “data” hay “things”).
- Có ít nhất một ví dụ thực tế.
- Acceptance criteria bao gồm validations và ranh giới.
- Các trường hợp lỗi và “nếu thì” được đề cập (hoặc được hoãn rõ ràng).
Bước 1 — Trích xuất Thực thể từ Stories (Các Danh từ)
Trước khi nghĩ đến bảng hoặc sơ đồ, đọc user stories và gạch chân các danh từ. Trong viết yêu cầu, danh từ thường chỉ đến các “thứ” hệ thống phải nhớ—chúng thường trở thành thực thể trong schema.
Một mô hình tinh thần nhanh: danh từ thành thực thể, còn động từ thành hành động hoặc workflow. Nếu story nói “A manager assigns a technician to a job,” các thực thể có thể là manager, technician, và job—và “assigns” gợi ý một quan hệ bạn sẽ mô hình hóa sau.
Làm sao biết một danh từ là thực thể thật sự
Không phải danh từ nào cũng xứng đáng có bảng riêng. Một danh từ là ứng viên mạnh khi nó:
- Có danh tính riêng: bạn có thể chỉ vào một thực thể cụ thể (Job #1042, Customer A).
- Thay đổi theo thời gian: nó có lifecycle (một job chuyển từ scheduled → completed).
- Được dùng ở nhiều nơi: nhiều stories tham chiếu tới nó, hoặc nhiều workflow chạm tới nó.
Nếu một danh từ chỉ xuất hiện một lần, hoặc chỉ mô tả thứ khác (“nút đỏ”, “Thứ Sáu”), có thể nó không phải thực thể.
Thuộc tính vs thực thể riêng (bài test “Address” và “Tag”)
Sai lầm phổ biến là biến mọi chi tiết thành bảng. Quy tắc vàng:
- Nếu đó là một giá trị mô tả một thực thể, thường là thuộc tính (ví dụ Customer.phone_number).
- Nếu nó có thể lặp lại, được chia sẻ hoặc có cấu trúc, thường là thực thể riêng.
Hai ví dụ cổ điển:
- Address: Nếu bạn lưu shipping và billing addresses, giữ lịch sử, hoặc tái sử dụng địa chỉ giữa khách hàng/địa điểm, Address có khả năng là thực thể riêng. Nếu chỉ cần một địa chỉ gửi thư duy nhất và không tái sử dụng, có thể để như thuộc tính.
- Tag: Tags hầu như luôn là thực thể riêng vì chúng lặp lại và có quan hệ many-to-many (một Job có nhiều Tags; một Tag áp dụng cho nhiều Jobs).
Dùng AI gợi ý thực thể (cẩn thận)
AI có thể tăng tốc khám phá thực thể bằng cách quét stories và trả về danh sách danh từ theo chủ đề (people, work items, documents, locations). Một prompt hữu ích: “Extract nouns that represent data we must store, and group duplicates/synonyms.”
Đối xử với kết quả như điểm khởi đầu, không phải đáp án. Hỏi tiếp:
- “Trong số này, cái nào có lifecycle hoặc cần ID riêng?”
- “Cái nào thực chất là statuses, categories, hoặc attributes?”
- “Có từ đồng nghĩa không (ví dụ ‘client’ vs ‘customer’)?”
Mục tiêu Bước 1 là một danh sách ngắn, sạch sẽ các thực thể mà bạn có thể bảo vệ bằng cách trỏ lại các câu chuyện thực tế.
Bước 2 — Biến Chi tiết thành Fields (Những thứ bạn phải lưu)
Khi đã đặt tên các thực thể (như Order, Customer, Ticket), việc tiếp theo là nắm được các chi tiết bạn sẽ cần sau này. Trong database, những chi tiết đó là fields (còn gọi là attributes)—những nhắc nhở hệ thống không thể quên.
Cách chọn fields (không đoán mò)
Bắt đầu từ user story, sau đó đọc acceptance criteria như một danh sách những gì phải được lưu.
Nếu yêu cầu nói “Users can filter orders by delivery date,” thì delivery_date không phải tuỳ chọn—nó phải tồn tại như một field (hoặc có thể được suy ra đáng tin từ dữ liệu khác). Nếu nói “Show who approved the request and when,” bạn sẽ cần approved_by và approved_at.
Kiểm nghiệm thực tế: Người nào đó có cần giá trị này để hiển thị, tìm kiếm, sắp xếp, kiểm toán, hoặc tính toán không? Nếu có, nó có lẽ thuộc về một field.
Quy tắc đơn giản cho fields sạch
- Giá trị nguyên tử: lưu “First name” và “Last name” riêng nếu bạn sẽ tìm kiếm hoặc sắp xếp theo chúng. Tránh nhồi nhiều giá trị vào một field (ví dụ “red, blue”).
- Dùng kiểu nhất quán: ngày dưới dạng date, tiền dưới dạng decimal, boolean là true/false—không trộn định dạng như “$10”, “10 USD”, và “10”.
- Tránh sao chép văn bản: đừng copy địa chỉ khách hàng vào mọi order line. Lưu một lần ở nơi đúng và tham chiếu tới nó.
Từ điển có kiểm soát: statuses, types, categories
Nhiều story có từ như “status,” “type,” hoặc “priority.” Xem chúng như tập giá trị cho phép—một tập giá trị hạn chế.
Nếu tập nhỏ và ổn định, một trường enum đơn giản có thể ổn. Nếu có thể mở rộng, cần labels, hoặc yêu cầu phân quyền (ví dụ: categories do admin quản lý), hãy dùng bảng tra cứu riêng (ví dụ status_codes) và lưu tham chiếu.
Đây là cách stories biến thành fields bạn có thể tin tưởng—có thể tìm, báo cáo và khó nhập sai.
Bước 3 — Kết nối Thực thể bằng Quan hệ
Khi đã liệt kê thực thể (User, Order, Invoice, Comment, v.v.) và phác thảo các trường của chúng, bước tiếp theo là kết nối chúng. Quan hệ là lớp “các thứ tương tác với nhau” được ngụ ý bởi stories.
Ba dạng quan hệ (nói đơn giản)
Một-một (1:1) nghĩa là “một thứ có đúng một thứ khác.”
- Cụm từ story: “Each user has one profile.”
- Ý tưởng mô hình:
User↔Profile(thường có thể gộp nếu không có lý do tách).
Một-nhiều (1:N) nghĩa là “một thứ có thể có nhiều thứ khác.” Đây là dạng phổ biến nhất.
- Cụm từ story: “A user can have many orders.”
- Ý tưởng mô hình:
User→Order(lưuuser_idtrênOrder).
Nhiều-nhiều (M:N) nghĩa là “nhiều thứ liên quan tới nhiều thứ.” Cần một bảng phụ.
- Cụm từ story: “An order can include many products, and a product can be in many orders.”
Nhiều-nhiều: mẹo dùng bảng nối
Database không thuận tiện khi lưu “list of product IDs” trong Order mà không rắc rối về tìm kiếm, cập nhật, báo cáo. Thay vào đó, tạo bảng nối đại diện cho chính quan hệ.
Ví dụ:
OrderProductOrderItem(bảng nối)
OrderItem thường chứa:
order_idproduct_id- các chi tiết từ story như
quantity,unit_price,discount
Chú ý: nhiều khi chi tiết story (“quantity”) thuộc về mối quan hệ, chứ không phải thực thể nào riêng lẻ.
Bắt buộc vs tuỳ chọn (không dùng biệt ngữ)
Stories nói cho bạn biết liệu một kết nối có bắt buộc hay có thể thiếu.
- “An order must belong to a user” → mọi
Ordercầnuser_id(không cho phép rỗng). - “A user may have a phone number” →
phonecó thể rỗng. - “An order can have a shipping address (if physical goods)” →
shipping_address_idcó thể rỗng với hàng số hóa.
Kiểm tra nhanh: nếu story ngụ ý bạn không thể tạo bản ghi mà không có liên kết, coi đó là bắt buộc. Nếu story dùng “can”, “may”, hoặc cho ngoại lệ, coi nó là tuỳ chọn.
Viết lại câu story thành câu quan hệ
Khi đọc story, viết lại nó thành một cặp đơn giản:
- “A user can leave many comments” →
User1:NComment - “A comment belongs to one user” →
CommentN:1User
Làm điều này cho mọi tương tác trong stories. Cuối cùng bạn sẽ có mô hình kết nối phản ánh cách công việc thực sự diễn ra—trước khi mở công cụ ER.
Bước 4 — Dùng Workflows để Tìm Trạng thái, Sự kiện và Lỗ hổng
User stories nói bạn muốn gì. Workflows cho thấy công việc chạy như thế nào, từng bước. Chuyển workflow thành dữ liệu là cách nhanh để phát hiện “chúng ta quên lưu cái này” trước khi xây dựng.
Bắt đầu với một workflow đơn giản
Viết workflow như chuỗi hành động và thay đổi trạng thái. Ví dụ:
- Create request → Draft
- Submit request → Submitted
- Manager reviews → Approved hoặc Rejected
- If approved, work is scheduled → In progress
- Completed → Done
Những từ in đậm thường trở thành trường status (hoặc một bảng “state” nhỏ), với các giá trị cho phép rõ ràng.
Workflows phơi bày các field thiếu
Khi đi qua từng bước, hỏi: “Chúng ta cần biết gì để dùng sau này?” Workflows thường lộ ra các field như:
- timestamps:
submitted_at,approved_at,completed_at - ownership:
created_by,assigned_to,approved_by - lý do/ngữ cảnh:
rejection_reason,approval_note - thứ tự:
sequencecho quy trình nhiều bước
Nếu workflow bao gồm chờ, escalation, hoặc handoffs, thường bạn sẽ cần ít nhất một timestamp và một trường “hiện tại ai đang giữ”.
Workflows phơi bày bảng thiếu
Một số bước workflow không chỉ là field—chúng là cấu trúc dữ liệu riêng:
- Audit log / history cho “ai thay đổi trạng thái khi nào”
- Approvals cho multi-approver hoặc quy tắc phức tạp
- Attachments khi người dùng upload file
- Comments khi thảo luận là một phần của quy trình
Dùng AI để đối chiếu các lỗ hổng
Đưa cho AI cả: (1) user stories và acceptance criteria, và (2) các bước workflow. Yêu cầu nó liệt kê mọi bước và nêu dữ liệu cần cho mỗi bước (state, actor, timestamps, outputs), rồi đánh dấu bất kỳ yêu cầu nào không được hỗ trợ bởi fields/bảng hiện tại.
Trong nền tảng như Koder.ai, việc “kiểm tra lỗ hổng” này đặc biệt thực tế vì bạn có thể lặp nhanh: điều chỉnh giả định schema, tái sinh scaffolding, và tiếp tục mà không tốn nhiều thời gian cho boilerplate thủ công.
Khóa chính, Độ duy nhất và Các ràng buộc cơ bản (không dùng biệt ngữ)
Khi biến user stories thành bảng, bạn không chỉ liệt kê fields—bạn còn quyết định cách dữ liệu được nhận diện và nhất quán theo thời gian.
Primary keys: “chứng minh thư” ổn định cho mỗi hàng
Một primary key nhận diện duy nhất một bản ghi—tưởng tượng như chứng minh thư của hàng đó.
Tại sao mỗi hàng cần một PK: stories ngụ ý cập nhật, tham chiếu, và lịch sử. Nếu story nói “Support can view an order and issue a refund,” bạn cần một cách ổn định để chỉ tới đơn đó—kể cả khi khách hàng đổi email, sửa địa chỉ, hay trạng thái đơn thay đổi.
Thực tế thường là một id nội bộ (số hoặc UUID) không đổi.
Foreign keys: con trỏ giữa các bảng
Một foreign key cho phép một bảng trỏ an toàn tới bảng khác. Nếu orders.customer_id tham chiếu customers.id, database có thể đảm bảo mọi order thuộc về một customer thực.
Điều này khớp với các story như “As a user, I can see my invoices.” Invoice không lơ lửng; nó gắn với một customer (và thường với order hoặc subscription).
Quy tắc duy nhất: biến “phải là duy nhất” thành ràng buộc
User stories thường ẩn chứa yêu cầu độ duy nhất:
- “Users sign up with email” → enforce unique email (hoặc unique per tenant nếu hỗ trợ nhiều account)
- “Finance searches by invoice number” → enforce unique invoice_number
Các ràng buộc này ngăn các bản sao gây rối sau này.
Indexing (ở mức cao): làm truy vấn phổ biến nhanh
Indexes làm nhanh các tìm kiếm như “tìm khách hàng theo email” hoặc “liệt kê orders theo customer.” Bắt đầu với các index liên quan tới truy vấn phổ biến và các quy tắc uniqueness.
Cái nên hoãn: index nặng cho báo cáo hiếm hoặc bộ lọc phỏng đoán. Ghi lại nhu cầu trong stories, xác nhận schema, rồi tối ưu khi có dữ liệu thực và evidence truy vấn chậm.
Giữ dữ liệu nhất quán: Checklist chuẩn hóa thực tế
Mục tiêu của chuẩn hóa: ngăn trùng lặp mâu thuẫn. Nếu cùng một sự thật được lưu ở hai nơi, sớm muộn sẽ mâu thuẫn (hai cách viết, hai giá, hai địa chỉ “hiện tại”). Schema chuẩn lưu mỗi sự thật một lần và tham chiếu.
Checklist nhanh cho bất kỳ schema nháp nào
1) Dò các nhóm lặp
Nếu thấy “Phone1, Phone2, Phone3” hoặc “ItemA, ItemB, ItemC”, đó là tín hiệu cho bảng riêng (ví dụ CustomerPhones, OrderItems). Các nhóm lặp làm tìm kiếm, validate và scale khó.
2) Đừng sao chép cùng một chi tiết vào nhiều bảng
Nếu CustomerName xuất hiện trong Orders, Invoices, và Shipments, bạn đã tạo nhiều nguồn chân thực. Giữ chi tiết khách hàng trong Customers, và chỉ lưu customer_id ở nơi khác.
3) Tránh nhiều cột cho cùng một thứ
Cột như billing_address, shipping_address, home_address ổn nếu thật sự khác nhau. Nhưng nếu bạn đang mô tả “nhiều địa chỉ với loại”, dùng bảng Addresses với trường type.
4) Tách lookup khỏi văn bản tự do
Nếu người dùng chọn từ một tập đã biết (status, category, role), mô hình hoá nhất quán: enum hạn chế hoặc bảng lookup. Tránh “Pending” vs “pending” vs “PENDING”.
5) Kiểm tra mọi field không phải ID phụ thuộc vào đúng thứ
Một kiểm tra trực giác: trong một bảng, nếu một cột mô tả điều không phải thực thể chính của bảng, nó có lẽ thuộc nơi khác. Ví dụ: Orders không nên lưu product_price trừ khi nó là “giá tại thời điểm order” (snapshot lịch sử).
Khi denormalize chấp nhận được (lựa chọn sau này)
Đôi khi bạn lưu trùng có chủ ý:
- Báo cáo/hiệu năng: bảng tổng hợp hoặc pre-aggregated
- Caching: giá trị tính sẵn để tránh tính lại nặng
- Audit/history: copy “tên tại thời điểm mua” để giữ thực tế quá khứ
Chìa khoá là làm rõ: trường nào là nguồn chân thực và cách cập nhật các bản sao.
AI giúp ở đâu—và con người quyết ở đâu
AI có thể gợi ý các chỗ trùng lặp đáng ngờ (cột lặp, tên field giống nhau, fields status không nhất quán) và đề xuất tách thành bảng. Con người vẫn quyết trade-off—đơn giản vs linh hoạt vs hiệu năng—dựa trên cách sản phẩm sẽ được dùng.
Lưu hay Tính toán: Cái gì thuộc database
Quy tắc hữu ích: lưu các sự thật bạn không thể tái tạo đáng tin cậy sau này; tính toán phần còn lại.
Lưu vs tính toán (derived)
Dữ liệu lưu là nguồn chân thực: line items, timestamps, thay đổi trạng thái, ai làm gì. Dữ liệu tính là sinh ra từ các sự kiện đó: totals, counters, flags như “is overdue”, rollups như “current inventory”.
Nếu hai giá trị có thể tính từ cùng sự kiện cơ sở, ưu tiên lưu sự kiện và tính giá trị còn lại. Nếu không bạn có nguy cơ mâu thuẫn.
Tại sao lưu giá trị dẫn xuất dễ gây lệch
Giá trị dẫn xuất thay đổi khi input thay đổi. Nếu bạn lưu cả input và kết quả, bạn phải đồng bộ ở mọi workflow và trường hợp biên (sửa, refund, partial shipment, backdated change). Một cập nhật bỏ sót sẽ khiến DB kể hai câu chuyện khác nhau.
Ví dụ: lưu order_total đồng thời với order_items. Nếu ai đó thay quantity hoặc áp discount mà tổng không được cập nhật đúng, finance thấy một số, cart thấy số khác.
Dùng workflow để quyết nên lưu gì (lịch sử và snapshot)
Workflows cho bạn biết khi nào cần sự thật lịch sử, không chỉ “sự thật hiện tại”. Nếu người dùng cần biết giá trị tại thời điểm đó, hãy lưu snapshot.
Với order, bạn có thể lưu:
- Line items và prices (facts)
order_totalđược chụp tại checkout (snapshot), vì thuế, giảm giá, quy tắc giá có thể thay đổi sau đó
Với inventory, “inventory level” thường tính từ các chuyển động (receipts, sales, adjustments). Nhưng nếu cần audit, lưu các movement và (tuỳ chọn) snapshot định kỳ để báo cáo nhanh.
Với tracking đăng nhập, lưu last_login_at là một fact (timestamp sự kiện). Câu hỏi “active trong 30 ngày gần nhất?” nên tính toán.
Ví dụ thực hành: Từ 5 User Stories đến mô hình ER
Dùng ví dụ ứng dụng support ticket. Chúng ta sẽ đi từ năm user story đến một ER model đơn giản (thực thể + trường + quan hệ), rồi kiểm tra với một workflow.
5 user stories → danh từ → thực thể
- As a customer, I can create a support ticket with a subject, description, and category.
- As an agent, I can assign a ticket to myself or another agent.
- As an agent, I can add internal notes and public replies to a ticket.
- As a customer, I can see when my ticket is updated and when it’s closed.
- As a manager, I can track how long tickets stay open and who closed them.
Từ các danh từ này, ta có thực thể lõi:
- Người dùng (User) (customers, agents, managers)
- Ticket
- Message (public replies + internal notes)
- Category
- TicketEvent (audit/history)
Trường và quan hệ (mô hình ER ngắn gọn)
- User: id, name, email, role
- Category: id, name
- Ticket: id, subject, description, status, created_at, updated_at, closed_at
- quan hệ: Ticket.category_id → Category.id
- quan hệ: Ticket.requester_id → User.id (customer)
- quan hệ: Ticket.assignee_id → User.id (agent, nullable)
- Message: id, ticket_id, author_id, body, is_internal, created_at
- quan hệ: Message.ticket_id → Ticket.id
- quan hệ: Message.author_id → User.id
- TicketEvent: id, ticket_id, actor_id, type, from_status, to_status, created_at
Ánh xạ workflow: tạo → cập nhật → đóng
- Create: insert Ticket (status = “open”, created_at), insert TicketEvent(type = “created”).
- Update (assign, reply): insert Message hoặc update Ticket.assignee_id, và insert TicketEvent(type = “assigned”/“replied”, updated_at).
- Close: update Ticket.status = “closed”, set closed_at, insert TicketEvent(type = “closed”, actor_id = closer).
“Trước và sau”: AI phát hiện ràng buộc thiếu
Trước (thường gặp): Ticket có assignee_id, nhưng ta quên đảm bảo chỉ agents mới là assignee.
Sau: AI gợi ý và bạn thêm quy tắc thực tế: assignee phải là một User có role = “agent” (thực hiện bằng validate phía ứng dụng hoặc constraint/policy ở DB, tuỳ stack). Điều này ngăn “gán cho customer” làm hỏng báo cáo.
Xác thực Schema: Truy nguồn trở lại từng Story
Schema chỉ “xong” khi mỗi user story có thể trả lời với dữ liệu bạn thực sự có thể lưu và truy vấn. Bước xác thực đơn giản nhất là lấy từng story và hỏi: “Chúng ta có thể trả lời câu này từ database, đáng tin, cho mọi trường hợp không?” Nếu câu trả lời là “có thể”, mô hình còn thiếu.
Biến mỗi story thành câu hỏi database
Viết lại mỗi user story thành một hoặc nhiều câu hỏi kiểm thử—những điều bạn mong đợi một báo cáo, màn hình, hay API có thể hỏi. Ví dụ:
- Báo cáo: “Hiện tất cả orders đang mở theo khách hàng, với tổng trong 30 ngày gần nhất.”
- Phân quyền: “Những user nào được duyệt hoàn tiền cho cửa hàng này?”
- Trường hợp biên: “Một order có thể tồn tại không có shipping address? Còn hàng số thì sao?”
- Xóa: “Nếu xóa một customer, chuyện gì xảy ra với orders, invoices, và notes?”
Nếu bạn không thể diễn đạt story thành câu hỏi rõ ràng, story chưa đủ rõ. Nếu có thể diễn đạt—nhưng không thể trả lời bằng schema hiện tại—bạn thiếu field, quan hệ, status/event, hoặc ràng buộc.
Dùng dữ liệu mẫu để kiểm tra nhanh
Tạo một dataset nhỏ (5–20 hàng cho các bảng chính) gồm các trường hợp bình thường và những trường rắc rối (trùng lặp, thiếu giá trị, hủy). Sau đó “chạy thử” các story với dữ liệu đó. Bạn sẽ nhanh phát hiện vấn đề như “không biết địa chỉ nào được dùng tại thời điểm mua” hoặc “không có chỗ lưu người đóng ticket”.
Để AI giúp tìm các trường hợp chưa xử lý
Yêu cầu AI sinh các câu hỏi xác thực cho mỗi story (bao gồm edge cases và tình huống xóa), và liệt kê dữ liệu cần để trả lời. So sánh danh sách đó với schema: mọi chênh lệch là hành động cụ thể, không phải cảm giác “có gì đó sai”.
Dùng AI an toàn và giữ schema dễ bảo trì
AI có thể tăng tốc mô hình dữ liệu, nhưng cũng có nguy cơ rò rỉ thông tin nhạy cảm hoặc cài đặt các giả định sai. Hãy coi nó như trợ lý rất nhanh: hữu ích, nhưng cần có guardrails.
Nên chia sẻ với AI gì (và tránh gì)
Chia sẻ đầu vào đủ thực tế để mô hình hoá, nhưng được làm sạch để an toàn:
- User stories đã làm sạch (đổi tên khách hàng, sản phẩm, địa điểm)
- Acceptance criteria và edge cases (“refund within 14 days”, “one active subscription per account”)
- Ví dụ trường với dữ liệu giả (ví dụ
invoice_total: 129.50,status: "paid") - Header CSV / bảng hiện tại (cấu trúc thường an toàn; nội dung thường không)
Tránh mọi thứ có thể nhận diện cá nhân hoặc tiết lộ hoạt động nhạy cảm:
- Tên thật, email, số điện thoại, địa chỉ thật
- Lịch sử đơn hàng thật, ticket hỗ trợ thật, ghi chú nội bộ
- API keys, credentials, ảnh chụp màn hình chứa dữ liệu riêng
Nếu cần độ thực, tạo dữ liệu tổng hợp phù hợp định dạng và khoảng—không copy hàng production.
Ghi lại giả định bên cạnh schema
Schema thất bại vì mọi người “đều cho rằng” khác nhau. Bên cạnh ER model (hoặc trong cùng repo), giữ một nhật ký quyết định ngắn:
- Định nghĩa (“Tài khoản 'active' nghĩa là gì?”)
- Ràng buộc (“Một user có thể thuộc nhiều tổ chức”)
- Đánh đổi (“Lưu currency_code trên mỗi invoice để audit”)
Điều này biến output AI thành tri thức nhóm thay vì artifact một lần rồi quên.
Lập kế hoạch cho thay đổi: versioning và migrations
Schema sẽ tiến hoá theo stories mới. Giữ an toàn bằng:
- Version hóa thay đổi schema (migrations trong Git)
- Viết migration có thể đảo ngược khi có thể
- Cập nhật seeds và truy vấn mẫu để các thay đổi có thể test
- Review các migration do AI sinh như mọi code khác
Nếu dùng nền tảng như Koder.ai, tận dụng guardrails như snapshots và rollback khi lặp schema, và export source code khi cần tuỳ chỉnh sâu hơn hoặc review truyền thống.
Một workflow lặp đơn giản
- Làm sạch stories + tạo 5–10 ví dụ giả.
- Yêu cầu AI đề xuất thực thể, trường, quan hệ, và ràng buộc.
- Review với team; ghi giả định.
- Thực hiện migrations; chạy test “truy nguồn story” (mỗi story có thể được đáp ứng bởi mô hình).
- Lặp khi stories thay đổi; giữ schema và ghi chú đồng bộ.
Câu hỏi thường gặp
Làm sao để trích xuất các thực thể cơ sở dữ liệu từ user story?
Bắt đầu từ các user story và gạch chân những danh từ đại diện cho các thực thể mà hệ thống phải nhớ (ví dụ: Ticket, User, Category).
Một danh từ nên thành thực thể khi nó:
- cần một ID riêng
- thay đổi theo thời gian (có lifecycle/trạng thái)
- xuất hiện trong nhiều user story khác nhau
Giữ một danh sách ngắn những thực thể bạn có thể chứng minh bằng các câu trong user story.
Khi nào thì nên để thành field và khi nào nên làm thành bảng riêng?
Dùng bài kiểm tra “thuộc tính vs thực thể":
- Làm thành field nếu đó là một giá trị đơn mô tả một bản ghi (ví dụ:
customer.phone_number). - Làm thành bảng riêng nếu nó có thể lặp lại, được chia sẻ, có cấu trúc hoặc cần lưu lịch sử (ví dụ: nhiều địa chỉ, tags, attachments).
Mẹo nhanh: nếu bạn cần “nhiều” cái đó, rất có khả năng bạn cần một bảng khác.
Acceptance criteria dịch sang các field và ràng buộc như thế nào?
Đối xử với acceptance criteria như một danh sách kiểm tra lưu trữ. Nếu một yêu cầu nói bạn phải lọc/hiển thị/kiểm toán thứ gì đó, bạn phải lưu nó (hoặc có thể suy ra nó một cách đáng tin cậy).
Ví dụ:
- “Show who approved and when” →
approved_by,approved_at - “Filter by delivery date” →
delivery_date - “Email must be unique” → ràng buộc/chỉ mục duy nhất trên
email
Làm sao để chuyển văn bản user story thành các quan hệ bảng (1:1, 1:N, M:N)?
Viết lại câu chuyện thành câu mô tả mối quan hệ:
- “A customer can have many orders” → 1:N (đặt
customer_idtrênorders) - “An order includes many products” → M:N (thêm bảng nối như
order_items)
Nếu bản thân mối quan hệ có dữ liệu (quantity, price, role), dữ liệu đó thuộc về bảng nối.
Cách mô hình hóa quan hệ nhiều-nhiều đúng là gì?
Mô hình hoá M:N với một bảng nối lưu hai khóa ngoại và các trường liên quan đến quan hệ.
Mẫu điển hình:
ordersproductsorder_itemschứaorder_id,product_id,quantity,unit_price
Tránh lưu “một danh sách ID” trong một cột đơn — việc truy vấn, cập nhật và đảm bảo toàn vẹn sẽ rất khó chịu.
Workflow giúp tìm bảng hoặc field thiếu bằng cách nào?
Đi qua workflow từng bước và hỏi: “Để chứng minh điều này đã xảy ra sau này, chúng ta cần gì?”
Những bổ sung thường gặp:
- timestmaps:
submitted_at,closed_at - actors:
created_by,assigned_to,closed_by - lý do/ghi chú:
rejection_reason
Nếu bạn cần biết “ai thay đổi gì khi nào”, thêm bảng event/audit thay vì ghi đè một trường duy nhất.
Nên thêm ràng buộc nào trước (khóa, duy nhất, chỉ mục)?
Bắt đầu với:
- một primary key ổn định cho mỗi bảng (
id) - foreign keys cho các quan hệ (
orders.customer_id → customers.id) - các quy tắc uniqueness từ yêu cầu (email, invoice number)
Sau đó thêm index cho các truy vấn phổ biến nhất (ví dụ email, customer_id, status + created_at). Hoãn các index suy đoán cho đến khi có dữ liệu thực tế.
Làm sao biết schema đã chuẩn hóa đủ mà không lạm dụng?
Làm bài kiểm tra nhanh:
- Nếu thấy nhóm lặp như
Phone1/Phone2, tách thành bảng con. - Nếu cùng một sự thật xuất hiện ở nhiều bảng, chọn một nguồn chân thực và tham chiếu tới nó.
- Nếu một cột mô tả thứ khác (không phải thực thể chính của bảng), di chuyển nó.
Chỉ denormalize khi có lý do rõ ràng (hiệu năng, báo cáo, snapshot audit) và ghi chú trường nào là nguồn chân thực.
Cái gì nên lưu trong database và cái gì nên tính toán?
Lưu những sự kiện và dữ kiện bạn không thể tái tạo đáng tin cậy; tính toán phần còn lại.
Nên lưu:
- các sự kiện và timestamp
- các line item và giá lịch sử
- “ai làm gì” để audit
Tốt để tính toán:
- totals từ line items
- flags như “is overdue” từ ngày
Nếu lưu giá trị dẫn xuất (ví dụ order_total), xác định rõ cách đồng bộ hóa và kiểm thử các trường hợp rìa (refunds, edits).
Làm sao dùng AI an toàn để tăng tốc thiết kế schema mà không tạo ra giả định sai?
Dùng AI để làm bản nháp, rồi kiểm tra với tài liệu thực tế.
Gợi ý thực tế:
- “Extract candidate entities and synonyms from these stories.”
- “List fields implied by acceptance criteria (timestamps, actors, statuses).”
- “Given this workflow, what data is required at each step?”
Hạn chế:
- làm sạch dữ liệu đầu vào (không đưa PII, credentials, hay dữ liệu production)
- giữ nhật ký quyết định cạnh schema
- coi migrations do AI sinh ra như code cần review và version trong Git