91 Studi Kasus: Sistem Manajemen Artikel (CMS)
CMS (Content Management System) adalah salah satu sistem paling fundamental dalam ekosistem software modern. Di era serba digital, kebutuhan untuk mengelola konten secara terstruktur, skalabel, dan aman sangat vital—baik untuk blog personal, portal berita, hingga knowledge base enterprise.
Artikel ini adalah bagian dari seri 91 Studi Kasus di mana kita akan mendisect arsitektur, implementasi, dan best practices membangun CMS dengan pendekatan engineering-first, sekaligus menghadirkan studi kasus dan contoh kode implementasi pragmatis.
1. Masalah & Kebutuhan
Studi Kasus: Portal Berita “MediaKita”
Skenario:
MediaKita adalah portal berita yang sedang berkembang. Awalnya, artikel diunggah manual oleh tim via Google Docs, kemudian di-copy paste ke website oleh developer. Proses yang tidak scalable dan sering mengundang masalah, seperti:
- Kesalahan format dan metadata.
- Artikel duplikat.
- Deadline sering terlewati.
- Tidak ada sistem approval/editorial workflow.
Key Requirements dari Stakeholder:
- Setiap pengguna (author) bisa menulis, menyimpan, dan mempublikasikan artikel.
- Artikel harus bisa dikategorikan, diberi tag, serta memiliki status (draft, review, published).
- Editor bisa mereview dan meng-approve artikel sebelum terpublish.
- Sistem harus trackable (audit trail).
- REST API untuk integrasi frontend & mobile app.
2. High Level Design
Mari kita breakdown menjadi beberapa komponen:
flowchart TD subgraph Frontend FE[React/Vue SPA] end subgraph Backend BE[REST API (Express/NestJS)] end subgraph Database DB[(PostgreSQL/MongoDB)] end FE -- HTTP/JSON --> BE BE -- ORM/ODM --> DB
Entity Model
Tabel berikut mengilustrasikan 3 entitas inti dalam sistem CMS sederhana.
Entity | Fields | Relasi |
---|---|---|
User | id, name, email, role (author/editor/admin), password_hash | 1 User : n Article |
Article | id, title, slug, content, category_id, status, created_at, updated_at, author_id, editor_id, audit_log | n Article : 1 Category |
Category | id, name, slug, parent_id | n Category : n Article |
User Stories
1. Penulis Menulis Artikel
- User buka halaman form, mengisi title, content, memilih kategori.
- Menyimpan sebagai draft.
2. Editorial Workflow
- Draft masuk ke review queue.
- Editor memberikan feedback atau approve.
- Setelah approved, status artikel berubah menjadi
published
.
3. Audit Trail
- Setiap perubahan status atau content dicatat dalam audit log.
3. Contoh Kode: Backend API (Node.js + Express + Sequelize)
Mari lihat bagaimana konteks di atas diterjemahkan menjadi endpoint RESTful:
// src/routes/articles.js
const express = require('express');
const router = express.Router();
const { Article, Category, User } = require('../models');
const auth = require('../middleware/auth');
// Endpoint: Buat artikel (Draft)
router.post('/', auth, async (req, res) => {
try {
const { title, content, category_id } = req.body;
const article = await Article.create({
title, content, category_id,
author_id: req.user.id,
status: 'draft'
});
res.status(201).json(article);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// Endpoint: Update status artikel ke 'review'
router.post('/:id/submit', auth, async (req, res) => {
try {
const article = await Article.findByPk(req.params.id);
if (article.author_id !== req.user.id) return res.status(403).json({ error: "Unauthorized" });
article.status = 'review';
await article.save();
res.json(article);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// Endpoint: Approve artikel (Editor)
router.post('/:id/approve', auth, async (req, res) => {
try {
if (req.user.role !== 'editor') return res.status(403).json({ error: "Editor only" });
const article = await Article.findByPk(req.params.id);
article.status = 'published';
article.editor_id = req.user.id;
await article.save();
res.json(article);
} catch (error) {
res.status(400).json({ error: error.message });
}
});
4. Editorial Workflow: State Machine
Untuk menjaga proses editorial tetap robust, workflow status biasanya diatur via state machine.
stateDiagram-v2 [*] --> Draft Draft --> Review: Submit for Review Review --> Draft: Request Change Review --> Published: Approve Published --> Archived: Archive Draft --> Deleted: Delete Review --> Deleted: Delete
5. Audit Trail: Simulasi & Implementasi
Audit trail penting untuk governance dan debugging. Simulasi sederhana dengan hook pada model ORM:
// src/models/Article.js (Sequelize Model)
Article.afterUpdate(async (article, options) => {
await AuditLog.create({
article_id: article.id,
changed_by: options.user.id,
status: article.status,
timestamp: new Date()
});
});
Setiap kali status artikel berubah, record dimasukkan ke tabel audit_logs
.
Contoh Query Audit Log
SELECT * FROM audit_logs WHERE article_id = 123 ORDER BY timestamp DESC;
6. Kategori dan Tagging
Pengelompokan konten sangat penting untuk navigasi dan discoverability. Misal, satu artikel bisa punya banyak tag.
Tabel Relationship Many-to-Many:
article_id | tag_id |
---|---|
10 | 3 |
10 | 8 |
12 | 3 |
7. Integrasi Frontend
Dengan memiliki REST API, frontend bisa dibuat dengan framework modern seperti React.
Contoh Simulasi Fetch Artikel:
useEffect(() => {
fetch("/api/articles?status=published")
.then(res => res.json())
.then(data => setArticles(data));
}, []);
8. Deployment & Scalability
- Database: PostgreSQL, opsi full-text search untuk pencarian artikel.
- Backend: Node.js/NestJS/Go, disarankan stateless service untuk kemudahan scaling.
- Frontend: SPA, cache data penting di CDN jika traffic tinggi.
- Integrasi OAuth: Untuk login aman.
9. Studi Kasus Live: Simulasi Workflow
Alur Editorial:
- Author buat artikel
draft
. - Submit for review.
- Editor memberikan feedback (
request change
) atau approve. - Jika approved, artikel terpublish otomatis.
Simulasi Request API:
# 1. Author create
curl -X POST /api/articles -d '{ "title": "CMS Rules", "content": "...", ... }'
# 2. Author submit
curl -X POST /api/articles/5/submit
# 3. Editor approve
curl -X POST /api/articles/5/approve
Tabel Sample Audit Trail:
ID | Article ID | Changed By | Status | Waktu |
---|---|---|---|---|
1 | 5 | 12 | draft | 2024-06-01 09:01:01 |
2 | 5 | 12 | review | 2024-06-01 09:02:00 |
3 | 5 | 13 | published | 2024-06-01 11:10:00 |
10. Lessons Learnt & Best Practices
- Separation of Concerns: Pisahkan strictly antara model, controller, dan layer service.
- Access Control: Implementasi RBAC (Role-Based Access Control) pada endpoint dan UI.
- State Machine: Jangan hardcode status sebagai string magic, gunakan state machine pattern.
- Audit Trail: Dengan log perubahan, kita bisa dengan mudah menelusuri siapa mengubah apa.
- API-First: Desain backend supaya bisa diintegrasikan ke frontend/web/mobile dengan mudah.
- Validasi & Error Handling: Jangan lupakan validation layer baik di backend maupun frontend.
11. Kesimpulan
Membangun CMS bukan hanya soal CRUD artikel, tapi mendesain sistem workflow, integritas data, dan kemudahan scaling yang enterprise-ready. Dari studi kasus MediaKita, kebutuhan editorial yang dulu mengandalkan proses manual, kini bisa di-automasi serta dipantau dengan baik. Value utamanya terletak pada flexibility, governance, dan extensibility.
Semoga breakdown kasus ini membantu Anda yang sedang merancang atau refactor sistem CMS untuk scale-up ke level berikutnya. Jangan ragu bereksperimen, iterasi, dan selalu lakukan audit atas tiap flow penting dalam sistem!
Bagaimana pengalaman Anda membangun CMS? Sampaikan di komentar!
90 Strategi Pengujian dan CI/CD graphql-go
Artikel Terhangat
99 Kontribusi ke Open Source graphql-go
10 Oct 2025
98 Membuat Plugin Sendiri untuk graphql-go
10 Oct 2025
96 Migrasi REST API ke GraphQL
10 Oct 2025
94 Studi Kasus: Dashboard Admin Real-time
10 Oct 2025

99 Kontribusi ke Open Source graphql-go

98 Membuat Plugin Sendiri untuk graphql-go

96 Migrasi REST API ke GraphQL
