Bài viết này tôi sẽ mô tả các bước thiết kế CSDL quan hệ cho một shop bán hàng trực tuyến sử dụng công cụ dbdiagram.io, cuối cùng sinh mã SQL vào CSDL MySQL. Shop b

1. Hãy phân tích kỹ trước khi dùng công cụ

Thiết kế quan hệ bảng bằng công cụ đôi khi mang lại hưng phấn nào đó cho khá nhiều lập trình viên, nó cũng giống như hacker mới vào nghề mua ngón tay gõ trên terminal vậy. Hãy bình tĩnh, công cụ chỉ là thứ yếu, chúng ta hãy cùng nhau phân tích nghiệp vụ bằng lời. Một web site bán hàng trực tuyến có:

  • Bảng product chứa thông tin sản phẩm. Bảng này có một số cột luôn chắc chắn có nhưng id, name, description, madein, manufacturer. Phần thông tin thuộc tính sản phẩm sẽ rất động tuỳ vào từng sản phẩm cụ thể. Tivi có kích thước đường chéo tính theo inch, có độ phân giải HD, FullHD, 2K, 4K. Tủ lạnh thì lại có dung tích tính bằng lít, có hay không có tính năng inverter, điều hoà thì có tính năng diệt khuẩn bằng ion, hoặc lọc không khí bằng màng HEPA....

  • Bảng category để phân loại sản phẩm product. Một số web site đơn giản chỉ có 1 cấp độ phân loại. Nhưng một số web site có danh mục sản phẩm rất lớn, có thể có đến 3 cấp độ phân loại.

  • Bảng customer lưu thông tin khách hàng. Nếu hệ thống có một bảng user để lưu tất cả thông tin người dùng đăng nhập. Thì bảng customer là một subset của user. Nhưng nếu web site không yêu cầu khách hàng phải tạo tài khoản mà vẫn mua được hàng thì chúng ta sẽ lưu thông tin khách hàng trực tiếp trong phần đơn hàng order.

  • Bảng orders lưu thông tin hiện tại của đơn hàng. Tôi cố tình đặt tên bảng là orders chứ không phải là order vì trong câu lệnh SQL order là một key word liên quan đến sắp xếp dữ liệu. Đơn hàng bị huỷ thì không được xoá, mà đặt trạng thái huỷ.

  • Bảng lineitem: một đơn hàng có thể có nhiều hơn một mặt hàng.

  • Bảng order_action: ghi lại mọi hành động của khách hàng liên quan đến đơn hàng: đặt hàng, sửa đơn hàng (thêm bớt mặt hàng, đổi địa chỉ giao hàng), huỷ đơn hàng. Chú ý bảng orders chỉ là nơi lưu thông tin hiện tại cuối cùng của đơn hàng. Nó phục vụ mục đích báo cáo.

2. Những bài toán thực tế cần giải quyết

Nếu bạn muốn phát triển một web site bán hàng cho nhiều loại mặt hàng, thì bạn cần đối mặt với nhiều thách thức thì thiết kế:

  • Bài toán 1 (No SQL): Làm sao sản phẩm có thể lưu linh hoạt các thuộc tính sản phẩm và tìm kiếm được sản phẩm theo từng thuộc tính.

  • Bài toán 2 (Price History): Một sản phẩm có giá thay đổi theo thời gian. Khách hàng đặt hàng ở thời điểm nào, thì lúc thanh toán phải theo giá lúc đặt hàng. Nếu lúc thanh toán, giá sản phẩm tăng, khách hàng sẽ vui vì mua được lúc giá rẻ. Nhưng khi giá sản phẩm có khuyến mại chắc chắn khách hàng sẽ không vui, có thể chỉ thanh toán bằng đúng giá hiện tại khi đã giảm.

  • Bài toán 3 (Race to Order): trong kho, một mặt hàng bán rất chạy chỉ còn 1 đơn vị. Rất nhiều khách hàng cùng tiến hành đặt hàng. Từ lúc đặt hàng, cho đến lúc thanh toán thành công có một khoảng thời gian từ 2 phút (thanh toán trực tuyến) đến 2 ngày (chuyển khoản khác ngân hàng vào ngày cuối tuần). Lúc khách hàng đặt, kho vẫn báo còn hàng. Nhưng khi thanh toán thành công, kho không đủ hàng, vậy tính sao?

Sẽ còn nhiều bài toán thách thức nếu chỉ sử dụng CSDL thì không thể giải quyết triệt để. Do đó cần thận trọng với mô hình thiết kế database centric (mô hình hoá mọi nghiệp vụ bằng quan hệ các bảng trong CSDL quan hệ).

3. Thiết kế CSDL bằng dbdiagram.io

Xây dựng ứng dụng lớn cần bắt đầu mô hình nhỏ. Trước tiên chúng ta thử nghiệm với mô hình nhỏ đủ cho một shop bán hàng online cá nhân. Sau đó sẽ bổ xung dần các tính năng, không nhất thiết phải bằng CSDL, mà dùng đến các công nghệ khác để tránh tình trạng database centric design (thiết kế lấy CSDL làm trung tâm). Tôi sử dụng dbdiagram.io thay vì các công cụ kéo thả thiết kế khác vì:

  • Kiểm soát thiết kế bằng code luôn chính xác
  • Miễn phí
  • Thiết kế trực tiếp trên trình duyệt
  • Xuất ra PDF, PNG, MySQL, Postgresql, MSSQL Server
  • Sản phẩm made in Việt nam. Chất lượng và trải nghiệm cực tốt.

4. Product và các bảng liên quan

Một sản phẩm được sản xuất (made in) ở một nước. Một nước sản xuất nhiều sản phẩm.

Table product {
  id int [pk, increment]
  name varchar [not null]
  description varchar [not null]
  madein varchar(2) [ref: > country.code]
  price int
}

Table country {
  code varchar(2) [pk, not null]
  name varchar [not null]
}

Một sản phẩm cần lưu lại lịch sử giá theo thời gian

Một sản phẩm sẽ có danh sách bản ghi gía biến động theo thời gian. Trường product.price phản ánh giá cập nhật lần cuối cùng.

Table product_price {
  id int [pk, increment]
  product_id int [ref: > product.id]
  price int
  created_at datetime [default: `now()`]
}

Một sản phẩm có nhiều ảnh, video, file hướng dẫn sử dụng PDF

Bảng product_media sẽ chứa thông tin ảnh, video, file dạng PDF.

Table product_media {
  id int [pk, increment]
  product_id int [ref: > product.id]
  uri varchar [not null]
  media_type MediaType
}

Enum MediaType {
  photo
  video
  PDF
}

Một sản phẩm do một nhà sản xuất

Cần phân biệt rõ nhà sản xuất với nhà phân phối. Ví dụ Tivi Xiaomi 65 inch do Xiaomi sản xuất. Khi nhập khẩu về VN, có thể nhiều hơn một nhà nhập khẩu - phân phối. Phía người mua, họ chỉ quan tâm hãng sản xuất. Còn với shop, cần quan tâm nhà nhập khẩu để đảm báo giá tốt nhất, có hàng khi cần và chính sách bảo hành tốt.

Nhà sản xuất thành lập và có trụ sở chính ở một quốc gia X. Nhưng sản phẩm có thể được sản xuất ở quốc gia Y != X.

Table product {
  id int [pk, increment]
  name varchar [not null]
  description varchar [not null]
  madein varchar(2) [ref: > country.code]
  price int
  manufacturer int [ref: > manufacturer.id]
}

Table country {
  code varchar(2) [pk, not null]
  name varchar [not null]
}

Table manufacturer {
  id int [pk, increment]
  name varchar
  country varchar(2) [ref: > country.code]
}

Phân loại sản phẩm

Trên giao diện web site bán hàng, chúng ta thường thấy danh mục dạng 1 cấp hoặc 2, 3 cấp để chia nhỏ danh mục hơn. Việc xây dựng danh mục cực kỳ quan trọng. Danh mục dễ hiểu, phân loại hợp lý giúp khách hàng tìm kiếm sản phẩm dễ dàng, tốn ít lần di chuyển sâu.

  • Một sản phẩm gắn với duy nhất một phân loại
  • Một sản phẩm có thể nhiều phần loại. Một phân loại thì bao hàm nhiều sản phẩm.

Sản phẩm liên quan

Khi khách hàng chọn một sản phẩm A, web sit cần gợi ý sản phẩm B, C, D hay được mua cùng, hoặc là cách đẩy hàng tốn kho, hoặc khuyến mãi.

  • Bia --> Mực, snack nhắm
  • Tivi --> đầu phát Smart Tivi
  • Lều --> Bàn, ghế dã ngoại, bếp nấu dã ngoại
  • Bikini --> Phao bơi, SUP, kem chống nắng
Table suggest_prodct {
  product_id int [ref: > product.id]
  suggest_id int [ref: > product.id]
}

Lưu các thuộc tính động của bảng Product

1. Sử dụng bảng lưu property name và property value

Một product sẽ có nhiều product_properties tương ứng.

  • Tủ lạnh
    • "dung tích (lít)", integer, "400"
    • "inverter", bool, "true"
    • "điện áp (vôn)", array, {"220"}
  • Tivi
    • "kích thước (inches)", integer, "65"
    • "USB 2.0", integer, "2"
    • "Bluetooth", bool, "true"
    • "HDMI", integer, "3"
    • "OS", string, "Android"
    • "điện áp (vôn)", array, {"110", "220"}
  • Ô tô
    • "động cơ", string, "diesel"
    • "chuyển động", string, "2 cầu"
    • "cấp hộp số", integer, "6"
Enum PropertyType {
  0 [note: "string"]
  1 [note: "integer"]
  2 [note: "float"]
  3 [note: "bool"]
    4 [note: "array"]
}

Table product_properies {
  id int [pk, increment]
  product_id int [ref: > product.id]
  key varchar [not null]
  value varchar [not null]
  type PropertyType
}

Kết quả mã SQL tạo bảng là

CREATE TABLE `product_properies` (
  `id` int PRIMARY KEY AUTO_INCREMENT,
  `product_id` int,
  `key` varchar(255) NOT NULL,
  `value` varchar(255) NOT NULL,
  `type` ENUM ('0', '1', '2', '3', '4')
);

2. Dùng dynamics column hoặc JSON column trong MySQL

3. Kết hợp MongoDB với MySQL

Chúng ta có thể sử dụng MongoDB để lưu thông tin động

Tham khảo

Ví dụ mẫu của dbdiagram.io