Giới thiệu dự án
Trong kỷ nguyên số hóa, tốc độ truyền tải thông tin và khả năng tối ưu hóa công cụ tìm kiếm (Search Engine Optimization - SEO) đóng vai trò quyết định đến sự sống còn của các nền tảng xuất bản nội dung số. Theo các báo cáo về hành vi người dùng trực tuyến, hơn 53% lưu lượng truy cập web toàn cầu đến từ tìm kiếm tự nhiên (Organic Search), và độ trễ tải trang vượt quá 3 giây có thể làm tăng tỷ lệ thoát trang (Bounce Rate) lên đến 32%.
Tuy nhiên, phần lớn các trang tin tức truyền thống xây dựng trên nền tảng Single Page Application (SPA) thuần túy (như ReactJS hoặc VueJS Client-Side Rendering) gặp phải rào cản nghiêm trọng về SEO do các công cụ tìm kiếm khó thu thập dữ liệu từ mã nguồn JavaScript được kết xuất phía máy khách. Ngược lại, các hệ thống CMS nguyên khối (Monolithic CMS như WordPress, Drupal) thường gặp vấn đề về hiệu năng khi lưu lượng truy cập tăng đột biến, tiêu tốn nhiều tài nguyên máy chủ và khó mở rộng theo mô hình kiến trúc hiện đại.
Đồ án tốt nghiệp "Xây dựng trang web thương mại điện tử xem tin tức" của tác giả Trần Nguyên Quang, dưới sự hướng dẫn của TS. Nguyễn Thiên Bảo tại Trường Đại học Sư phạm Kỹ thuật TP.HCM (HCMUTE), tập trung giải quyết bài toán cốt lõi: Xây dựng một nền tảng báo điện tử và quản lý nội dung đa người dùng hiệu năng cao, tối ưu hóa toàn diện cho SEO và trải nghiệm di động (Mobile-First), dựa trên sự kết hợp giữa NestJS và Next.js.
KIẾN TRÚC TỔNG QUAN HỆ THỐNG
Mục tiêu của dự án
- Thiết kế và triển khai kiến trúc Server-Side Rendering (SSR): Sử dụng Next.js 13 nhằm render mã nguồn HTML trực tiếp từ máy chủ, giúp công cụ tìm kiếm (Googlebot, Bingbot) thu thập chỉ mục (indexing) 100% nội dung bài viết và tối ưu hóa các chỉ số Core Web Vitals.
- Xây dựng hệ thống Backend kiến trúc module hóa: Ứng dụng NestJS 9.0 theo nguyên lý Dependency Injection (DI) và Inversion of Control (IoC), thiết lập hệ thống RESTful API an toàn, có khả năng mở rộng (scalable) và bảo trì cao.
- Chuẩn hóa cơ sở dữ liệu quan hệ: Tối ưu hóa lược đồ MySQL 8.0 phục vụ quản lý bài viết, danh mục, phân quyền người dùng (RBAC) và theo dõi số liệu thống kê lượt đọc thời gian thực.
- Tự động hóa kiểm thử phần mềm: Tích hợp bộ kiểm thử tự động hóa Cypress (Automation E2E Testing) nhằm đảm bảo tính ổn định của các luồng nghiệp vụ trọng yếu.
Phạm vi và giới hạn
- Phạm vi: Hệ thống phục vụ hai nhóm đối tượng chính gồm Độc giả/Cộng tác viên (Client UI) và Quản trị viên/Biên tập viên (Admin/Staff Portal), bao gồm quy trình xuất bản, kiểm duyệt bài viết, phân loại theo thẻ tag, tìm kiếm theo tên và thống kê truy cập.
- Giới hạn: Phiên bản hiện tại tập trung vào xử lý luồng dữ liệu văn bản và hình ảnh tĩnh; chưa tích hợp hệ thống phát video trực tiếp (Livestream) hoặc hệ thống phân tán Redis Caching nâng cao cho lớp cơ sở dữ liệu.
Phân tích và thiết kế giải pháp
Phân tích hiện trạng
Khảo sát thực tế các trang báo điện tử và cổng thông tin phổ biến tại Việt Nam như VnExpress, ZingNews (Znews), và Kênh 14 cho thấy: các trang này sở hữu khối lượng dữ liệu khổng lồ nhưng cấu trúc thường bị đè nặng bởi mạng lưới quảng cáo phức tạp, script theo dõi của bên thứ ba, và giao diện máy tính để bàn (Desktop) cồng kềnh chưa tối ưu hoàn toàn cho các thiết bị di động cấu hình yếu.
| Tiêu chí phân tích |
Hệ thống CMS truyền thống (WordPress/PHP) |
Ứng dụng SPA thuần túy (ReactJS CSR) |
Giải pháp Đề tài (Next.js 13 + NestJS 9.0) |
| Cơ chế hiển thị |
Server-rendered (Monolithic) |
Client-Side Rendering (CSR) |
Hybrid SSR & Static Export |
| Khả năng SEO |
Tốt nhưng TTFB (Time to First Byte) cao |
Kém, bot khó đọc dynamic JS |
Xuất sắc, pre-rendered HTML chuẩn SEO |
| Tốc độ phản hồi UI |
Chậm khi chuyển trang (tải lại toàn trang) |
Nhanh sau khi tải xong bundle |
Tức thì nhờ cơ chế SPA routing sau hydrate |
| Tổ chức mã nguồn |
Thường phân tán, khó kiểm soát type |
Dễ lộn xộn nếu thiếu state pattern |
Chuẩn Ducks Pattern & NestJS Modules |
| Bảo mật API |
Cookie/Session truyền thống |
Token-based (JWT) |
JWT Authentication + NestJS Guards (RBAC) |
| Độ trễ API Backend |
Trung bình (150ms - 350ms) |
Tùy thuộc cấu hình Backend |
Rất thấp (30ms - 80ms trên Node runtime) |
Phân loại yêu cầu theo mô hình MoSCoW
- Must Have: Hệ thống xác thực JWT và OAuth (Google, Facebook); Xem bài viết theo danh mục; Đăng bài và kiểm duyệt bài viết; Bộ đếm lượt xem theo danh mục (
Statistical); Trang quản trị Admin/Staff.
- Should Have: Tự động tăng view ngầm qua API; Tìm kiếm bài viết theo từ khóa; Giao diện tối ưu Mobile-First Responsive.
- Could Have: Tích hợp Progressive Web App (PWA) offline caching; Trình soạn thảo văn bản Rich Text Editor.
- Won't Have (Giai đoạn này): Cổng thanh toán trực tuyến cho bài viết có phí; Hệ thống khuyến nghị bài viết bằng Machine Learning.
Thiết kế hệ thống
Technology Stack & Khai báo phiên bản
- Frontend Framework: Next.js 13.x (React 18.x), Bootstrap 4.6, Redux Toolkit (Ducks Pattern).
- Backend Framework: NestJS 9.0.x, Node.js 18 LTS Runtime, TypeScript 5.0.x.
- Database: MySQL Server 8.0.x (InnoDB Engine, utf8mb4 encoding).
- Automation Testing Framework: Cypress 12.x E2E Testing Suite.
- Authentication & Security: JSON Web Token (JWT), Bcrypt 5.x, Passport-JWT.
Thiết kế cơ sở dữ liệu quan hệ (Database Schema)
Lược đồ cơ sở dữ liệu được chuẩn hóa về dạng 3NF (Third Normal Form) nhằm loại bỏ trùng lặp và bảo đảm tính toàn vẹn dữ liệu:
-- Bảng lưu trữ thông tin tài khoản người dùng và phân quyền RBAC
CREATE TABLE `users` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(100) NOT NULL UNIQUE,
`email` VARCHAR(150) NOT NULL UNIQUE,
`password` VARCHAR(255) NOT NULL,
`role` ENUM('ADMIN', 'STAFF', 'USER') DEFAULT 'USER',
`avatar` VARCHAR(255) NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX `idx_users_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Bảng lưu trữ bài viết tin tức
CREATE TABLE `posts` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`title` VARCHAR(255) NOT NULL,
`slug` VARCHAR(255) NOT NULL UNIQUE,
`summary` TEXT NOT NULL,
`content` LONGTEXT NOT NULL,
`thumbnail` VARCHAR(255) NOT NULL,
`status` ENUM('PENDING', 'APPROVED', 'REJECTED') DEFAULT 'PENDING',
`author_id` INT UNSIGNED NOT NULL,
`category_id` INT UNSIGNED NOT NULL,
`views_count` INT UNSIGNED DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
`updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (`author_id`) REFERENCES `users`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`category_id`) REFERENCES `tags`(`id`) ON DELETE RESTRICT,
INDEX `idx_posts_slug` (`slug`),
INDEX `idx_posts_status` (`status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Bảng lưu trữ danh mục / TagName
CREATE TABLE `tags` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`name` VARCHAR(100) NOT NULL UNIQUE,
`slug` VARCHAR(100) NOT NULL UNIQUE,
`total_views` INT UNSIGNED DEFAULT 0,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- Bảng thống kê số liệu truy cập theo danh mục và mốc thời gian
CREATE TABLE `statistical` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`tag_id` INT UNSIGNED NOT NULL,
`post_id` INT UNSIGNED NOT NULL,
`views_date` DATE NOT NULL,
`views_increment` INT UNSIGNED DEFAULT 1,
FOREIGN KEY (`tag_id`) REFERENCES `tags`(`id`) ON DELETE CASCADE,
FOREIGN KEY (`post_id`) REFERENCES `posts`(`id`) ON DELETE CASCADE,
UNIQUE KEY `uk_stats_tag_post_date` (`tag_id`, `post_id`, `views_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
Thiết kế RESTful API Specification
| HTTP Method |
API Endpoint |
Mục đích nghiệp vụ |
Phân quyền truy cập |
POST |
/api/v1/auth/register |
Đăng ký tài khoản thành viên mới |
Public |
POST |
/api/v1/auth/login |
Xác thực người dùng, cấp phát Bearer Token |
Public |
GET |
/api/v1/posts |
Lấy danh sách bài viết theo trang/danh mục |
Public |
GET |
/api/v1/posts/:slug |
Lấy chi tiết bài viết và kích hoạt SSR render |
Public |
POST |
/api/v1/posts |
Tạo bài viết mới (trạng thái PENDING) |
USER, STAFF, ADMIN |
PATCH |
/api/v1/posts/:id/approve |
Duyệt bài viết xuất bản lên trang chủ |
STAFF, ADMIN |
DELETE |
/api/v1/posts/:id |
Xóa bài viết khỏi cơ sở dữ liệu |
ADMIN |
POST |
/api/v1/analytics/increment-view |
Tăng lượt xem ngầm cho bài viết và tag |
Public (Internal client call) |
Methodology & Quy trình phát triển
Dự án áp dụng phương pháp luận phát triển phần mềm Agile/Scrum rút gọn trong chu kỳ 14 tuần (từ ngày 24/02/2023 đến 29/05/2023):
- Giai đoạn 1 (Tuần 1 - 3): Khảo sát hiện trạng, phân tích nghiệp vụ, thiết kế lược đồ Use Case, Sequence Diagram và cơ sở dữ liệu MySQL.
- Giai đoạn 2 (Tuần 4 - 8): Xây dựng hệ thống Backend với NestJS 9.0 (Cấu trúc Controller, Service, Module, Middleware, JWT Guard).
- Giai đoạn 3 (Tuần 9 - 12): Lập trình giao diện Frontend bằng Next.js 13 theo kiến trúc Ducks Pattern; tích hợp SSR và xây dựng trang quản trị Admin Dashboard.
- Giai đoạn 4 (Tuần 13 - 14): Viết kịch bản kiểm thử tự động với Cypress, tối ưu hóa hiệu năng, đóng gói ứng dụng và bảo vệ đồ án.
Implementation và kết quả
Development Process & Mã nguồn kỹ thuật
1. Backend: Kiến trúc Module và Service trong NestJS 9.0
NestJS tận dụng tối đa cơ chế Dependency Injection giúp mã nguồn tách biệt hoàn toàn giữa tầng xử lý HTTP request và tầng nghiệp vụ logic:
// posts.service.ts - Xử lý nghiệp vụ bài viết và cập nhật thống kê
import { Injectable, NotFoundException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Post } from './entities/post.entity';
import { Tag } from '../tags/entities/tag.entity';
import { CreatePostDto } from './dto/create-post.dto';
@Injectable()
export class PostsService {
constructor(
@InjectRepository(Post)
private readonly postRepository: Repository<Post>,
@InjectRepository(Tag)
private readonly tagRepository: Repository<Tag>,
) {}
async incrementViews(postId: number): Promise<void> {
const post = await this.postRepository.findOne({ where: { id: postId } });
if (!post) {
throw new NotFoundException(`Post with ID ${postId} does not exist`);
}
// Tăng lượt xem của bài viết
await this.postRepository.increment({ id: postId }, 'views_count', 1);
// Đồng bộ tăng tổng lượt xem của danh mục tương ứng
await this.tagRepository.increment({ id: post.category_id }, 'total_views', 1);
}
async getPublishedPostBySlug(slug: string): Promise<Post> {
const post = await this.postRepository.findOne({
where: { slug, status: 'APPROVED' },
relations: ['author', 'category'],
});
if (!post) {
throw new NotFoundException(`Article '${slug}' not found or unapproved`);
}
return post;
}
}
2. Frontend: Tối ưu SEO bằng Server-Side Rendering (SSR) trong Next.js 13
Nhằm đảm bảo 100% nội dung bài viết và thẻ meta OpenGraph được nạp trước khi trả về trình duyệt:
// pages/posts/[slug].tsx - Next.js Server-Side Rendering Page
import { GetServerSideProps } from 'next';
import Head from 'next/head';
import axios from 'axios';
interface ArticleProps {
post: {
id: number;
title: string;
summary: string;
content: string;
thumbnail: string;
category: { name: string };
};
}
export default function ArticleDetail({ post }: ArticleProps) {
return (
<>
<Head>
<title>{post.title} - Tin Tức 24h</title>
<meta name="description" content={post.summary} />
<meta property="og:title" content={post.title} />
<meta property="og:description" content={post.summary} />
<meta property="og:image" content={post.thumbnail} />
<meta property="og:type" content="article" />
</Head>
<article className="container mt-4">
<span className="badge badge-primary">{post.category.name}</span>
<h1 className="display-5 font-weight-bold mt-2">{post.title}</h1>
<p className="lead text-muted">{post.summary}</p>
<hr />
<div
className="article-body"
dangerouslySetInnerHTML={{ __html: post.content }}
/>
</article>
</>
);
}
export const getServerSideProps: GetServerSideProps = async (context) => {
const { slug } = context.params!;
try {
const response = await axios.get(
`${process.env.API_BASE_URL}/api/v1/posts/${slug}`
);
return { props: { post: response.data } };
} catch (error) {
return { notFound: true };
}
};
3. Frontend: Tổ chức cấu trúc thư mục theo mô hình Ducks Pattern
Để loại bỏ sự cồng kềnh khi dự án mở rộng, toàn bộ state management của Redux được đóng gói theo từng module độc lập (Duck Module):
src/redux/
Testing và validation
Hệ thống được kiểm thử tự động hóa bằng Cypress nhằm mô phỏng chính xác các tương tác của người dùng trên môi trường thực tế:
// cypress/e2e/article_creation.cy.js - Kịch bản kiểm thử tự động
describe('News Publishing & Validation Workflow', () => {
beforeEach(() => {
cy.visit('/login');
cy.get('input[name="username"]').type('editor_test');
cy.get('input[name="password"]').type('SecurePassword@123');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/');
cy.get('.user-profile').should('contain', 'editor_test');
});
it('Cho phép người dùng tạo bài viết mới và lưu vào danh sách chờ duyệt', () => {
cy.visit('/posts/create');
cy.get('input[name="title"]').type('Nghiên cứu ứng dụng NextJS trong SEO 2023');
cy.get('select[name="category"]').select('Công nghệ');
cy.get('textarea[name="summary"]').type('Tóm tắt phân tích hiệu năng SSR');
cy.get('.ck-editor__editable').type('Nội dung chi tiết bài viết đánh giá...');
cy.get('button[id="btn-submit-post"]').click();
cy.get('.toast-success')
.should('be.visible')
.and('contain', 'Bài viết đã được gửi và đang chờ phê duyệt');
});
});
Kết quả đo lường hiệu năng & Kiểm thử
| Chỉ số kiểm thử |
Giá trị mục tiêu |
Kết quả thực tế (Cypress / Lighthouse) |
Trạng thái |
| First Contentful Paint (FCP) |
< 1.8 giây |
0.95 giây |
Đạt xuất sắc |
| Time to First Byte (TTFB) |
< 250 ms |
110 ms |
Đạt |
| Lighthouse SEO Score |
> 90/100 |
98/100 |
Đạt |
| Cypress E2E Test Cases |
100% Pass |
18/18 kịch bản Pass (100%) |
Hoàn thành |
| Thời gian thực thi Test tự động |
< 3 phút |
1 phút 42 giây |
Đạt |
| Tỷ lệ xử lý đồng thời API |
500 req/s |
780 req/s (Không lỗi 5xx) |
Vượt chỉ tiêu |
Đổi mới và đóng góp
- Ứng dụng kiến trúc Ducks Boilerplate trên Next.js: Khắc phục triệt để tình trạng phân mảnh mã nguồn của Redux cổ điển, giúp gom nhóm toàn bộ actions, action types và reducers vào một module duy nhất, giảm 35% lượng boilerplate code dư thừa.
- Cơ chế đếm lượt xem bất đồng bộ đa chiều (Multi-dimension Analytics): Thay vì chỉ tăng view ở cấp bài viết, hệ thống tự động tổng hợp và ghi nhận vào bảng
statistical theo ngày và danh mục tag tương ứng mà không làm nghẽn luồng đọc bài chính của độc giả.
- Tiếp cận Mobile-First & PWA: Thiết kế giao diện phản hồi linh hoạt, đảm bảo tốc độ tải trang trên các thiết bị di động kết nối mạng 3G/4G nhanh hơn 45% so với các trang tin tức truyền thống chứa nhiều script nặng.
- Quy trình kiểm chuẩn chất lượng với Cypress Automation: Đưa automation test vào quy trình phát triển khóa luận tốt nghiệp, giúp giảm 60% thời gian kiểm thử hồi quy thủ công so với phương pháp thông thường.
Ứng dụng thực tế và triển khai
Kịch bản triển khai trong thực tế (Real-World Use Cases)
- Trang tin tức chuyên ngành cho doanh nghiệp: Cung cấp giải pháp CMS nội bộ giúp các doanh nghiệp xây dựng blog, cổng thông tin truyền thông thương hiệu với chi phí vận hành thấp.
- Cổng xuất bản nội dung cộng đồng: Cho phép độc giả đăng ký làm cộng tác viên, tự do sáng tạo nội dung và gửi bài viết vào luồng kiểm duyệt tự động trước khi xuất bản.
Mô hình triển khai sản phẩm (Production Deployment Strategy)
Hệ thống được thiết kế để dễ dàng đóng gói và triển khai qua Docker hoặc môi trường máy chủ Linux (Ubuntu Server 20.04/22.04 LTS):
Phân tích Chi phí - Lợi ích (Cost-Benefit Analysis & TCO)
- Chi phí hạ tầng tối thiểu: Chỉ cần 01 Cloud VPS (2 vCPU, 4GB RAM, 50GB NVMe SSD) trị giá khoảng $12 - $15/tháng là có thể phục vụ ổn định 50.000 - 100.000 lượt xem trang (page views) mỗi ngày nhờ cơ chế SSR tối ưu và Backend NodeJS phi phong tỏa (Non-blocking I/O).
- Thời gian hoàn vốn (ROI): Nhờ tối ưu hóa SEO đạt điểm số cao (98/100), website nhanh chóng đạt thứ hạng cao trên Google Search, giúp tiết kiệm 70% chi phí chạy quảng cáo từ khóa Google Ads để thu hút độc giả ban đầu.
Hạn chế và hướng phát triển
Hạn chế kỹ thuật hiện tại
- Thiếu lớp bộ đệm phân tán: Chưa tích hợp Redis In-memory Cache, do đó khi có hàng chục nghìn lượt truy cập đồng thời vào cùng một bài viết, cơ sở dữ liệu MySQL vẫn phải chịu tải truy vấn trực tiếp.
- Đột phá về UI/UX chưa cao: Giao diện người dùng mặc dù chuẩn responsive nhưng phong cách bố cục vẫn theo khuôn mẫu quen thuộc của các trang báo mạng truyền thống, chưa có các tính năng tương tác độc đáo như Dark Mode tự động, lưu bài đọc offline hay bình luận theo ngữ cảnh (Contextual Comments).
Hướng phát triển trong tương lai
- Tích hợp Redis & Elasticsearch: Bổ sung Redis Cache cho tầng API để giảm tải cho MySQL xuống dưới 5ms và tích hợp Elasticsearch để hỗ trợ tìm kiếm toàn văn bản (Full-text Search) mượt mà với hàng triệu bài viết.
- Chuyển đổi sang kiến trúc Microservices: Tách riêng dịch vụ xác thực (Auth Service), dịch vụ nội dung (Content Service) và dịch vụ thống kê (Analytics Service) kết nối qua RabbitMQ hoặc Kafka.
- Ứng dụng Trí tuệ nhân tạo (AI Engine): Tích hợp mô hình AI để tự động tóm tắt nội dung bài viết, đề xuất bài viết liên quan dựa trên thói quen đọc của từng người dùng và tự động phát hiện tin giả (Fake News Detection).
Đối tượng hưởng lợi
ĐỐI TƯỢNG HƯỞNG LỢI
Câu hỏi thường gặp
1. Yêu cầu phần cứng và phần mềm tối thiểu để triển khai hệ thống là gì?
Hệ thống yêu cầu máy chủ Linux (Ubuntu 20.04+ khuyến nghị) với tối thiểu 1 vCPU, 2GB RAM, 20GB dung lượng đĩa cứng trống. Về môi trường phần mềm: Cần cài đặt Node.js phiên bản 18 LTS trở lên, MySQL Server 8.0+, Nginx làm Reverse Proxy và trình quản lý tiến trình PM2 để duy trì dịch vụ hoạt động liên tục.
2. Khi lượng truy cập tăng vọt (Traffic Spike), hệ thống sẽ mở rộng thế nào?
Nhờ kiến trúc tách rời (Decoupled Architecture), Frontend Next.js và Backend NestJS có thể scale độc lập. Đối với Backend, có thể triển khai chạy đa luồng thông qua PM2 Cluster Mode để tận dụng toàn bộ số nhân CPU. Đối với Cơ sở dữ liệu, việc bổ sung lớp đệm Redis Cache cho các bài viết "hot" sẽ giảm tải đến 85% truy vấn trực tiếp vào MySQL.
3. Việc tích hợp hệ sinh thái JavaScript (TypeScript) đồng nhất mang lại lợi ích gì?
Việc sử dụng TypeScript từ Frontend (Next.js) đến Backend (NestJS) giúp tái sử dụng các kiểu dữ liệu (Interfaces/DTOs), giảm thiểu hoàn toàn các lỗi sai lệch cấu trúc dữ liệu trong quá trình trao đổi qua API (Data Contract Mismatch) và nâng cao hiệu suất làm việc của đội ngũ phát triển.
4. Quy trình kiểm duyệt bài viết hoạt động như thế nào?
Khi người dùng thông thường gửi bài viết mới, bài viết sẽ ở trạng thái PENDING và không xuất hiện trên giao diện công khai. Chỉ các tài khoản có vai trò STAFF hoặc ADMIN sau khi đăng nhập vào trang quản trị mới có quyền đọc, phê duyệt chuyển trạng thái thành APPROVED để xuất bản, hoặc REJECTED nếu nội dung không đạt tiêu chuẩn.
5. Tại sao dự án chọn Ducks Pattern cho Frontend thay vì Redux chuẩn thông thường?
Ducks Pattern gom nhóm Action Types, Actions và Reducers của một tính năng cụ thể vào chung một tệp duy nhất thay vì chia rẽ thành nhiều thư mục riêng lẻ. Điều này giúp giảm độ phức tạp trong việc điều hướng tệp tin khi dự án mở rộng, giúp mã nguồn sáng sủa, dễ bảo trì và dễ dàng test độc lập.
Kết luận
Đồ án tốt nghiệp "Xây dựng trang web thương mại điện tử xem tin tức" của tác giả Trần Nguyên Quang là một minh chứng thực tế cho việc ứng dụng thành công các công nghệ web hiện đại nhất hiện nay gồm Next.js 13, NestJS 9.0 và MySQL. Bằng việc giải quyết triệt để bài toán hiển thị dữ liệu chuẩn SEO thông qua cơ chế Server-Side Rendering và đảm bảo tính mở rộng của hệ thống bằng kiến trúc hướng Module, đề tài đã mang lại một sản phẩm hoàn thiện, có tính ứng dụng thực tiễn cao trong ngành xuất bản tin tức số.
Mã nguồn và kiến trúc của đề tài không chỉ đóng vai trò là một sản phẩm phần mềm hoàn chỉnh phục vụ bảo vệ tốt nghiệp đại học, mà còn là tài liệu tham khảo giá trị cho các kỹ sư phần mềm đang tìm kiếm giải pháp kết hợp hài hòa giữa TypeScript toàn diện, thiết kế hướng module và tự động hóa kiểm thử trong môi trường doanh nghiệp.