Menulis kode yang rapi dan efisien bukan hanya soal sintaks yang benar, tetapi juga tentang bagaimana kita menyusun arsitektur aplikasi. Dalam proyek besar, semakin banyak fitur dan modul yang ditambahkan, semakin kompleks pula alur logika yang harus diatur. Di sinilah pentingnya penerapan Design Pattern di Proyek JavaScript Modern — pola desain yang membantu pengembang membangun aplikasi yang scalable, modular, dan mudah di-maintain.
Pola desain seperti Module, Proxy, dan Strategy bukan hanya konsep teori, tetapi juga sangat relevan untuk proyek nyata seperti dashboard admin, aplikasi e-commerce, hingga sistem booking online. Artikel ini akan membahas bagaimana kamu dapat menerapkan design pattern tersebut dalam konteks pengembangan aplikasi JavaScript modern berbasis class, ES Modules, dan asynchronous workflow.
Struktur Modular Menggunakan Module Pattern
Dalam proyek modern, pengembang biasanya memisahkan logika menjadi beberapa berkas JavaScript menggunakan Module Pattern dan fitur import serta export. Pendekatan ini membuat kode lebih mudah dirawat, terutama saat bekerja dalam tim besar.
Contoh penerapan pada aplikasi Task Manager:
struktur folder:
src/ ├── modules/ │ ├── taskManager.js │ └── uiHandler.js ├── main.js
taskManager.js
export default class TaskManager {
constructor() {
this.tasks = [];
}
addTask(task) {
this.tasks.push(task);
console.log(`Tugas "${task}" berhasil ditambahkan!`);
}
listTasks() {
return this.tasks;
}
}
uiHandler.js
export function renderTasks(tasks) {
const list = document.getElementById('taskList');
list.innerHTML = '';
tasks.forEach(task => {
const li = document.createElement('li');
li.textContent = task;
list.appendChild(li);
});
}
main.js
import TaskManager from './modules/taskManager.js';
import { renderTasks } from './modules/uiHandler.js';
const manager = new TaskManager();
document.getElementById('addBtn').addEventListener('click', () => {
const task = document.getElementById('taskInput').value;
manager.addTask(task);
renderTasks(manager.listTasks());
});
Dengan cara ini, setiap file memiliki tanggung jawab sendiri (single responsibility), dan perubahan di satu bagian tidak mempengaruhi keseluruhan sistem.
Validasi Data dengan Proxy Pattern
Dalam aplikasi modern, validasi data sangat penting, terutama pada input pengguna. Daripada menulis logika validasi di setiap event handler, kamu bisa menggunakan Proxy Pattern untuk menangani semua validasi di satu tempat.
validationProxy.js
export function createValidatedUser(user) {
return new Proxy(user, {
set(target, prop, value) {
if (prop === 'email' && !value.includes('@')) {
throw new Error('Email tidak valid!');
}
if (prop === 'age' && (isNaN(value) || value < 0)) {
throw new Error('Usia harus berupa angka positif!');
}
target[prop] = value;
console.log(`Properti ${prop} diset ke ${value}`);
return true;
}
});
}
main.js
import { createValidatedUser } from './modules/validationProxy.js';
const user = createValidatedUser({});
try {
user.email = 'arvian@example.com';
user.age = 25;
console.log(user);
} catch (err) {
console.error(err.message);
}
Dengan pola ini, semua perubahan data akan melalui layer validasi otomatis tanpa perlu menulis ulang logika di setiap komponen.
Fleksibilitas Logika Menggunakan Strategy Pattern
Kadang kita membutuhkan logika yang bisa berubah tergantung kondisi, seperti sistem pembayaran, sorting data, atau algoritma rekomendasi. Di sinilah Strategy Pattern sangat membantu.
Contoh: sistem pemilihan metode pengiriman barang di e-commerce.
shippingStrategies.js
export class RegularShipping {
calculate(weight) {
return weight * 10000;
}
}
export class ExpressShipping {
calculate(weight) {
return weight * 20000;
}
}
export class SameDayShipping {
calculate(weight) {
return 50000 + (weight * 25000);
}
}
shippingContext.js
export default class ShippingContext {
setStrategy(strategy) {
this.strategy = strategy;
}
calculateCost(weight) {
return this.strategy.calculate(weight);
}
}
main.js
import ShippingContext from './modules/shippingContext.js';
import { RegularShipping, ExpressShipping, SameDayShipping } from './modules/shippingStrategies.js';
const shipping = new ShippingContext();
shipping.setStrategy(new ExpressShipping());
console.log('Biaya kirim Express:', shipping.calculateCost(3));
shipping.setStrategy(new SameDayShipping());
console.log('Biaya kirim Same Day:', shipping.calculateCost(3));
Dengan Strategy Pattern, kamu bisa mengganti logika kapan saja tanpa menyentuh struktur utama.
Integrasi Asynchronous Operation
Dalam aplikasi modern, banyak operasi berjalan secara asynchronous seperti pengambilan data API atau pengolahan data besar. Design Pattern dapat dikombinasikan dengan async/await untuk membuat kode tetap rapi.
Contoh integrasi Proxy dan async fetch:
const apiProxy = new Proxy({}, {
get(target, prop) {
return async function(url) {
console.log(`Fetching data untuk ${prop}...`);
const res = await fetch(url);
const data = await res.json();
console.log(`Data ${prop} berhasil diambil!`);
return data;
};
}
});
(async () => {
const posts = await apiProxy.posts('https://jsonplaceholder.typicode.com/posts');
console.log(posts.slice(0, 3));
})();
Kesimpulan
Menggunakan Design Pattern di Proyek JavaScript Modern bukan hanya mempercantik struktur kode, tetapi juga meningkatkan keandalan dan efisiensi aplikasi.
Dengan kombinasi Module, Proxy, dan Strategy Pattern, kamu bisa:
-
Membuat kode modular dan reusable.
-
Menyederhanakan validasi dan kontrol data.
-
Meningkatkan fleksibilitas logika bisnis.
-
Menangani proses asynchronous dengan lebih bersih.
Pola-pola ini menjadi pondasi penting bagi developer JavaScript profesional yang ingin menulis kode scalable dan siap produksi.