[Database Blueprint 03/10] Mạng xã hội và cộng đồng: bài viết, bình luận, follow, reaction, report và thông báo

31 lượt xem · 3 trả lời

1. 10:37 01/09/2026
BỘ DATABASE BLUEPRINT — WEB/APP DEVELOPMENT (MySQL 8.x/9.x) Mỗi bài là một schema khởi đầu có thể copy–dán và tùy biến. Trước production, hãy dùng migration có version, user database tối thiểu quyền, sao lưu có kiểm tra khôi phục và đánh giá bảo mật/pháp lý. Không lưu mật khẩu, token đăng nhập hay khóa API ở dạng thô. # 03/10 — Schema cho mạng xã hội hoặc cộng đồng Mô hình này dùng cho forum hiện đại, cộng đồng nội bộ hoặc ứng dụng có feed. Những phần cần thiết kế từ đầu là quyền xem, moderation/soft-delete, chống reaction trùng, thông báo và báo cáo nội dung. Không đưa dữ liệu hồ sơ nhạy cảm vào bảng `users` nếu nó không cần cho trải nghiệm cộng đồng. ## Quan hệ chính ```text users ─1─1 profiles users ─< posts ─< comments users >─< users (follows) users >─< posts/comments (reactions) users ─< notifications users ─< content_reports >─ posts/comments ``` ## Schema SQL ```sql CREATE DATABASE IF NOT EXISTS community_app CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE community_app; CREATE TABLE users ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, email VARCHAR(255) NOT NULL, password_hash VARCHAR(255) NOT NULL, username VARCHAR(50) NOT NULL, status ENUM('active','restricted','suspended','deleted') NOT NULL DEFAULT 'active', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, deleted_at DATETIME NULL, UNIQUE KEY uq_users_email (email), UNIQUE KEY uq_users_username (username), KEY idx_users_status_created (status, created_at) ) ENGINE=InnoDB; CREATE TABLE profiles ( user_id BIGINT UNSIGNED PRIMARY KEY, display_name VARCHAR(100) NOT NULL, bio VARCHAR(500) NULL, avatar_path VARCHAR(500) NULL, visibility ENUM('public','members','private') NOT NULL DEFAULT 'public', updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_profiles_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ) ENGINE=InnoDB; CREATE TABLE follows ( follower_id BIGINT UNSIGNED NOT NULL, followed_id BIGINT UNSIGNED NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (follower_id, followed_id), CONSTRAINT fk_follows_follower FOREIGN KEY (follower_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_follows_followed FOREIGN KEY (followed_id) REFERENCES users(id) ON DELETE CASCADE, CHECK (follower_id <> followed_id), KEY idx_follows_followed_created (followed_id, created_at) ) ENGINE=InnoDB; CREATE TABLE posts ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, author_id BIGINT UNSIGNED NOT NULL, body TEXT NOT NULL, visibility ENUM('public','followers','private') NOT NULL DEFAULT 'public', status ENUM('published','hidden','deleted') NOT NULL DEFAULT 'published', reply_to_post_id BIGINT UNSIGNED NULL, comments_count INT UNSIGNED NOT NULL DEFAULT 0, reactions_count INT UNSIGNED NOT NULL DEFAULT 0, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, deleted_at DATETIME NULL, CONSTRAINT fk_posts_author FOREIGN KEY (author_id) REFERENCES users(id), CONSTRAINT fk_posts_parent FOREIGN KEY (reply_to_post_id) REFERENCES posts(id) ON DELETE SET NULL, KEY idx_posts_feed (status, visibility, created_at, id), KEY idx_posts_author_created (author_id, created_at), KEY idx_posts_parent_created (reply_to_post_id, created_at) ) ENGINE=InnoDB; CREATE TABLE comments ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, post_id BIGINT UNSIGNED NOT NULL, author_id BIGINT UNSIGNED NOT NULL, parent_comment_id BIGINT UNSIGNED NULL, body TEXT NOT NULL, status ENUM('published','hidden','deleted') NOT NULL DEFAULT 'published', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, CONSTRAINT fk_comments_post FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE, CONSTRAINT fk_comments_author FOREIGN KEY (author_id) REFERENCES users(id), CONSTRAINT fk_comments_parent FOREIGN KEY (parent_comment_id) REFERENCES comments(id) ON DELETE SET NULL, KEY idx_comments_post_created (post_id, status, created_at), KEY idx_comments_author_created (author_id, created_at) ) ENGINE=InnoDB; CREATE TABLE reactions ( user_id BIGINT UNSIGNED NOT NULL, target_type ENUM('post','comment') NOT NULL, target_id BIGINT UNSIGNED NOT NULL, reaction_type ENUM('like','love','helpful','laugh') NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (user_id, target_type, target_id), CONSTRAINT fk_reactions_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, KEY idx_reactions_target (target_type, target_id, reaction_type) ) ENGINE=InnoDB; CREATE TABLE notifications ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, user_id BIGINT UNSIGNED NOT NULL, actor_id BIGINT UNSIGNED NULL, type VARCHAR(60) NOT NULL, entity_type VARCHAR(60) NULL, entity_id BIGINT UNSIGNED NULL, payload_json JSON NULL, read_at DATETIME NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT fk_notifications_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE, CONSTRAINT fk_notifications_actor FOREIGN KEY (actor_id) REFERENCES users(id) ON DELETE SET NULL, KEY idx_notifications_unread (user_id, read_at, created_at) ) ENGINE=InnoDB; CREATE TABLE content_reports ( id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, reporter_id BIGINT UNSIGNED NOT NULL, target_type ENUM('post','comment','user') NOT NULL, target_id BIGINT UNSIGNED NOT NULL, reason_code VARCHAR(60) NOT NULL, detail VARCHAR(1000) NULL, status ENUM('open','reviewing','resolved','rejected') NOT NULL DEFAULT 'open', reviewer_id BIGINT UNSIGNED NULL, resolution_note VARCHAR(1000) NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, resolved_at DATETIME NULL, CONSTRAINT fk_reports_reporter FOREIGN KEY (reporter_id) REFERENCES users(id), CONSTRAINT fk_reports_reviewer FOREIGN KEY (reviewer_id) REFERENCES users(id) ON DELETE SET NULL, KEY idx_reports_status_created (status, created_at), KEY idx_reports_target (target_type, target_id) ) ENGINE=InnoDB; ``` ## Quy tắc ở service layer - Khóa chính `reactions(user_id, target_type, target_id)` ngăn reaction trùng. Vì FK không thể trỏ đồng thời tới `posts` và `comments`, service phải kiểm tra mục tiêu tồn tại và quyền xem trước khi insert. - Xóa nội dung thường là đổi `status='deleted'`, che body khỏi người dùng thường và lưu dấu vết moderation. Chỉ xóa cứng theo chính sách lưu trữ đã phê duyệt. - Feed phải lọc status **và** visibility/quan hệ follow tại server; không dựa vào CSS ở client để “ẩn” dữ liệu. - Khi cập nhật `comments_count`/`reactions_count`, làm trong cùng transaction với insert/delete; định kỳ đối soát lại bằng `COUNT(*)`. Bài 04 thiết kế CMS/blog có bản nháp, revision, taxonomy và media.
0 Facebook
2. 16:52 09/10/2026
Trả lời Quản Trị Viên BỘ DATABASE BLUEPRINT — WEB/APP DEVELOPMENT (MySQL 8.x/9.x) Mỗi bài là một schema khởi đầu có thể copy–dán và tùy biến. …
Blueprint này rất đầy đủ cho một diễn đàn hiện đại hoặc mạng xã hội nội bộ, đặc biệt với cách bạn tách biệt cấu hình người dùng và các mối quan hệ rõ ràng
0
3. 17:14 09/10/2026
Trả lời Grace Anderson Blueprint này rất đầy đủ cho một diễn đàn hiện đại hoặc mạng xã hội nội bộ, đặc biệt với cách bạn tách biệt cấu hình ngư…
Tôi hoàn toàn đồng ý rằng việc tách riêng cấu hình người dùng khỏi các mối quan hệ rõ ràng giúp việc mở rộng và duy trì cấu trúc trở nên dễ dàng hơn nhiều. Việc phân chia này giúp việc điều chỉnh các thuộc tính cá nhân không ảnh hưởng đến logic follow, phản ứng hoặc báo cáo, điều này rất hữu ích khi triển khai các tính năng mới. Tuy nhiên, bạn đã cân nhắc cách xử lý các trường hợp tăng đột biến về số lượng phản ứng chưa—chẳng hạn như hàng triệu lượt thích trong một sự kiện hoặc chiến dịch chưa? Và bạn đã nghĩ gì về việc tối ưu hóa các truy vấn thông báo khi có hàng nghìn lượt theo dõi mới mỗi ngày chưa? Việc tách biệt các mối quan hệ có ảnh hưởng như thế nào đến hiệu suất của hệ thống thông báo trong các tình huống tải cao không?
0
4. 16:06 10/10/2026
Trả lời Grace Anderson Blueprint này rất đầy đủ cho một diễn đàn hiện đại hoặc mạng xã hội nội bộ, đặc biệt với cách bạn tách biệt cấu hình ngư…
Tôi hoàn toàn đồng ý rằng blueprint này rất toàn diện cho một diễn đàn hoặc mạng xã hội nội bộ hiện đại. Việc tách riêng cấu hình người dùng khỏi các mối quan hệ rõ ràng (ví dụ: bảng Follow, Reaction, Report) giúp việc mở rộng và bảo trì trở nên dễ dàng hơn nhiều, vì mỗi khía cạnh của hệ thống đều có cấu trúc riêng biệt và có thể được truy vấn một cách độc lập. Một điểm mạnh nữa là cách bạn mô hình hóa các thông báo, vì chúng có thể được liên kết linh hoạt với nhiều loại sự kiện (bài đăng mới, phản hồi, báo cáo, v.v.) mà không cần phải thay đổi cấu trúc cốt lõi. Bạn có gặp khó khăn gì khi đồng bộ hóa các mối quan hệ nhiều-nhiều (chẳng hạn như người dùng có thể phản ứng bằng nhiều cảm xúc
0
Vui lòng đăng nhập để tham gia trả lời chủ đề này.
Điều hướng nhanh

Bạn muốn khám phá điều gì?

Tìm chủ đề, bài viết hoặc đi thẳng đến một khu vực của cộng đồng.

Esc để đóng Ctrl K để mở lại