Xây dựng Công cụ Quản lý Dự án với Prisma

Tôi đang xây dựng một công cụ quản lý dự án cộng tác giống như Trello.

Tôi đang sử dụng React, Express.js, PostgreSQL và Socket.io. Trước khi viết bất kỳ route backend nào, tôi phải thiết kế schema cơ sở dữ liệu.

Schema chính là nền tảng. Nếu schema sai, toàn bộ ứng dụng sẽ thất bại. Dưới đây là phân tích chi tiết về thiết kế schema Prisma của tôi.

Các Model

• User: Lưu trữ tên, email và mật khẩu. Tôi sử dụng cuid() cho các ID. Điều này tạo ra các chuỗi dài và duy nhất. Nó tốt hơn số vì không làm lộ số lượng người dùng của bạn trên URL.

• Project: Chứa tên và mô tả dự án. Tôi đã để phần mô tả là tùy chọn bằng cách sử dụng dấu chấm hỏi.

• ProjectMember: Đây là một bảng trung gian (junction table). Nó liên kết User với Project. Vì một người dùng có thể tham gia nhiều dự án và một dự án có nhiều người dùng, bạn cần bảng trung gian này để quản lý mối quan hệ nhiều-nhiều (many-to-many). Tôi đã thêm một ràng buộc duy nhất (unique constraint) để ngăn cùng một người dùng tham gia vào một dự án hai lần.

• Board: Các Task nằm trong các Board. Các Board nằm trong các Project. Cấu trúc phân cấp này giúp việc kéo-thả trở nên dễ dàng. Việc di chuyển một task giữa các cột chỉ đơn giản là cập nhật một trường duy nhất.

• Task: Đây là model cốt lõi. Nó có hai mối quan hệ khác nhau với model User:

• Comment: Người dùng có thể để lại bình luận trên các task. Tôi đã đặt tên mối quan hệ là "author" thay vì "user" để mã nguồn dễ đọc hơn.

• Notification: Một model đơn giản để theo dõi tin nhắn cho người dùng.

Các bài học kỹ thuật rút ra

Tôi đã gặp phải một vài lỗi trong quá trình xây dựng dự án này. Hãy lưu ý những điều sau:

Schema hiện đã được migrate. Tiếp theo, tôi sẽ xây dựng backend Express và thiết lập Socket.io để cập nhật thời gian thực.

Xây dựng một công cụ quản lý dự án từ con số 0, bắt đầu với Prisma schema

Xây dựng một công cụ quản lý dự án là một cách tuyệt vời để học về thiết kế cơ sở dữ liệu, các mối quan hệ và cách cấu trúc lớp dữ liệu của một ứng dụng. Trong bài viết này, chúng ta sẽ đi sâu vào bước đầu tiên và quan trọng nhất: thiết kế Prisma schema.

Tầm quan trọng của việc thiết kế Schema

Schema là bản thiết kế cho cơ sở dữ liệu của bạn. Một schema được thiết kế tốt sẽ giúp đảm bảo tính toàn vẹn của dữ liệu, tối ưu hóa các truy vấn và giúp việc mở rộng ứng dụng trở nên dễ dàng hơn trong tương lai. Nếu schema của bạn lộn xộn, việc phát triển các tính năng mới sẽ trở thành một cơn ác mộng.

Tại sao lại là Prisma?

Prisma là một ORM (Object-Relational Mapping) thế hệ mới giúp việc truy cập cơ sở dữ liệu trở nên dễ dàng và đảm bảo an toàn về kiểu dữ liệu (type-safe). Nó cho phép bạn định nghĩa mô hình dữ liệu của mình trong một tệp duy nhất, dễ đọc và tự động tạo ra một client an toàn về kiểu cho ứng dụng của bạn.

Bước 1: Định nghĩa User Model

Mọi công cụ quản lý dự án đều cần người dùng. Một người dùng có thể tạo các dự án, được giao các công việc và quản lý hồ sơ cá nhân của chính họ.

model User {
  id        String   @id @default(cuid())
  email     String   @unique
  name      String?
  projects  Project[]
  tasks     Task[]
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

Trong model User này:

Bước 2: Định nghĩa Project Model

Một dự án là một tập hợp các công việc và thuộc về một người dùng (chủ sở hữu).

model Project {
  id          String   @id @default(cuid())
  title       String
  description String?
  ownerId     String
  owner       User     @relation(fields: [ownerId], references: [id])
  tasks       Task[]
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt
}

Trong model Project này:

Bước 3: Định nghĩa Task Model

Công việc (Task) là cốt lõi của ứng dụng. Chúng thuộc về một dự án và có thể được giao cho một người dùng cụ thể.

model Task {
  id          String   @id @default(cuid())
  title       String
  description String?
  status      Status   @default(TODO)
  priority    Priority @default(MEDIUM)
  projectId   String
  project     Project  @relation(fields: [projectId], references: [id])
  assigneeId  String?
  assignee    User?    @relation(