Menulis kode untuk backend API bukan hanya soal membuat endpoint dan menyambungkan ke database. Dalam jangka panjang, kualitas arsitektur kode akan menentukan seberapa mudah tim mengelola, mengembangkan, dan memelihara sistem. Oleh karena itu, pendekatan arsitektur yang solid sangat diperlukan. Salah satu metode yang sangat dianjurkan adalah Clean Architecture.
Apa Itu Clean Architecture?
Clean Architecture adalah prinsip desain perangkat lunak yang menekankan pemisahan tanggung jawab, independensi teknologi, dan kemudahan pengujian. Konsep ini diperkenalkan oleh Robert C. Martin (Uncle Bob). Tujuannya adalah menjaga sistem tetap modular dan mudah dirawat.
Model arsitektur ini membagi aplikasi ke dalam lapisan-lapisan seperti berikut:
- Entities (Domain Layer): Berisi logika bisnis inti.
- Use Cases (Application Layer): Mengatur alur proses dan aturan aplikasi.
- Interface Adapters (Delivery Layer): Menangani input/output seperti REST API atau CLI.
- Framework & Drivers (Infrastructure Layer): Meliputi database, HTTP framework, dan sistem eksternal.
Mengapa Clean Architecture Penting?
- Mudah Dirawat: Kode yang terpisah berdasarkan tanggung jawab lebih mudah dipahami.
- Fleksibel terhadap perubahan: Anda dapat mengganti framework atau database tanpa mengubah domain bisnis.
- Mendukung TDD: Dengan dependensi yang terbalik, Anda bisa menguji logika tanpa terikat dengan teknologi tertentu.
- Skalabilitas tim: Developer baru dapat lebih cepat memahami sistem karena strukturnya konsisten.
Penerapan Clean Architecture pada Backend API
1. Struktur Folder yang Jelas
Struktur direktori harus mencerminkan lapisan Clean Architecture. Berikut contoh strukturnya:
src/
├── domain/
│ ├── entities/
│ └── repositories/
├── usecases/
├── interfaces/
│ ├── controllers/
│ └── routes/
├── infrastructure/
│ └── database/
└── main.ts
2. Gunakan Interface untuk Abstraksi
Interface memainkan peran penting untuk membalik dependensi. Misalnya, UserRepository
didefinisikan di layer domain, sementara implementasinya berada di infrastruktur.
// domain/repositories/UserRepository.ts
export interface UserRepository {
findById(id: string): Promise<User | null>;
}
3. Implementasikan Use Case Secara Terpisah
Pisahkan setiap alur bisnis ke dalam use case
// usecases/GetUserById.ts
export class GetUserById {
constructor(private userRepo: UserRepository) {}
async execute(id: string): Promise<User | null> {
return this.userRepo.findById(id);
}
}
4. Controller Bertanggung Jawab pada Routing
Controller hanya bertugas menerima input dan mengirim output, tanpa logika bisnis.
// interfaces/controllers/UserController.ts
export const getUser = (getUserById: GetUserById) => async (req, res) => {
const user = await getUserById.execute(req.params.id);
if (user) res.json(user);
else res.status(404).send('User not found');
};
5. Hindari Ketergantungan Langsung ke Library
Lapisan domain dan use case tidak boleh bergantung pada pustaka seperti Express, Sequelize, atau Axios. Hal ini menjaga fleksibilitas sistem untuk diuji dan dipindah ke teknologi lain bila diperlukan.
Studi Kasus Singkat: Modul Autentikasi
- Entity:
User
,Token
- Use Case:
LoginUser
,RegisterUser
- Interface:
AuthController
,AuthRoutes
- Infra:
UserRepositoryImpl
,JWTService
Dengan struktur ini, logika login tetap bisa digunakan meskipun nanti Anda memindahkan dari Express ke Fastify, atau dari MongoDB ke PostgreSQL.
Tips Menjaga Konsistensi
- Selalu buat interface dan implementasi terpisah.
- Gunakan dependency injection.
- Uji logika bisnis tanpa tergantung infrastruktur.
- Dokumentasikan setiap lapisan agar mudah dipahami.
Kesimpulan
Clean Architecture bukan hanya teori desain, tetapi strategi nyata untuk menulis backend API yang rapi, terstruktur, dan mudah dikembangkan. Dengan memisahkan tanggung jawab dan menghindari ketergantungan langsung terhadap teknologi, Anda bisa membangun sistem yang fleksibel dan berumur panjang.