
Foto oleh Dmitry Romanoff via Pexels
Setelah memahami konsep dasar Clean Architecture, langkah selanjutnya adalah memahami secara mendalam tanggung jawab spesifik masing-masing layer: Domain, Application, dan Infrastructure, serta bagaimana ketiganya berinteraksi tanpa melanggar Dependency Rule yang sudah dibahas sebelumnya.
Domain berisi entity dan aturan bisnis paling fundamental yang jarang berubah, Application berisi use case yang mengorkestrasi entity Domain untuk mencapai tujuan bisnis tertentu, dan Infrastructure berisi implementasi teknis konkret seperti akses database dan pemanggilan API eksternal.
Artikel ini membahas secara detail apa yang seharusnya ditempatkan di masing-masing layer, dengan contoh konkret struktur project yang memisahkan ketiganya dengan jelas dalam konteks aplikasi ASP.NET Core.
Domain: Entity dan Business Rules Murni
Entity Domain membungkus data sekaligus aturan bisnisnya sendiri
Application: Use Case yang Mengorkestrasi Domain
Use case mengoordinasikan alur, logika bisnis tetap di Domain
Infrastructure: Implementasi Teknis Konkret
Infrastructure mengimplementasikan interface dengan teknologi konkret
Struktur Project yang Mencerminkan Pemisahan Layer
Struktur project fisik mencerminkan Dependency Rule
Contoh Konkret Memisahkan Entity Domain dari Model Infrastructure
Entity domain dengan behavior vs model infrastruktur yang hanya menyimpan kolom
Pemisahan layer domain dari infrastructure sering kali paling terasa pada bagaimana entity dimodelkan. Entity domain seperti Produk di atas membungkus datanya dengan properti private set dan hanya mengizinkan perubahan lewat method seperti UbahHarga, yang bisa menegakkan aturan bisnis seperti harga tidak boleh negatif.
Model infrastructure seperti ProdukEntity sebaliknya dibuat sesederhana mungkin, murni merepresentasikan struktur tabel agar mudah dipetakan oleh Entity Framework atau ORM lain. Pemetaan antara kedua model ini biasanya dilakukan di layer Infrastructure lewat repository, sehingga layer Domain dan Application tidak pernah bersentuhan langsung dengan atribut spesifik ORM seperti navigation property atau foreign key.
Pendekatan dua model ini menambah sedikit kode mapping dibanding memakai satu entity untuk semua keperluan, namun hasilnya domain tetap bersih dari ketergantungan pada framework data access. Ketika suatu saat tabel database perlu dinormalisasi ulang atau ORM diganti, perubahan cukup dilakukan di layer Infrastructure tanpa menyentuh aturan bisnis yang sudah teruji di layer Domain.
Mapping antara Domain Entity dan DTO di Lapisan Application
DTO menyembunyikan detail entity domain dan menambahkan format tampilan
Mengembalikan entity domain langsung sebagai response API tampak praktis di awal, namun lama-kelamaan membocorkan detail internal seperti method bisnis atau field yang seharusnya tidak pernah dilihat client, serta membuat perubahan pada entity domain langsung berdampak pada kontrak API. DTO (Data Transfer Object) berfungsi sebagai lapisan pemisah yang hanya memuat data yang memang perlu dikirim ke luar, dengan bentuk yang bisa berbeda dari struktur entity domain aslinya. Perubahan skema database pun tidak langsung memaksa perubahan pada kontrak API, selama DTO yang diekspos ke client tetap dijaga stabil.
Mapping antara entity dan DTO idealnya dilakukan di lapisan Application, bukan di Controller atau di dalam entity domain itu sendiri, karena Application adalah lapisan yang memang bertanggung jawab mengoordinasikan data untuk kebutuhan presentasi. Mapping manual seperti pada contoh memberi kontrol penuh atas transformasi data, seperti memformat harga menjadi string mata uang, yang tidak perlu dan tidak boleh menjadi tanggung jawab entity domain. Struktur DTO juga bisa disesuaikan khusus untuk tiap use case, misalnya versi ringkas untuk daftar dan versi lengkap untuk halaman detail produk.
Untuk project dengan banyak entity dan DTO, menulis mapping manual untuk setiap kombinasi bisa menjadi repetitif, sehingga library seperti AutoMapper sering dipakai untuk menghasilkan mapping otomatis berdasarkan kecocokan nama properti. Trade-off-nya, mapping otomatis membuat transformasi data menjadi kurang eksplisit dan lebih sulit ditelusuri saat debugging dibanding mapping manual yang terlihat jelas langkah demi langkah di dalam kode. Validasi konfigurasi mapping otomatis tetap diperlukan lewat unit test, agar perubahan nama properti pada entity tidak diam-diam membuat field DTO menjadi kosong.
Domain Event untuk Memberi Tahu Perubahan Penting di Layer Domain
Entity domain mencatat event tanpa mengetahui siapa yang akan menanganinya
Domain event adalah catatan bahwa sesuatu yang penting telah terjadi di dalam entity domain, seperti pesanan yang baru saja dikonfirmasi. Entity cukup menghasilkan event tersebut tanpa perlu tahu siapa saja yang akan bereaksi terhadapnya, sehingga logika domain tetap bersih dari urusan seperti pengiriman notifikasi atau pembaruan laporan.
Setelah entity disimpan, lapisan Application membaca daftar event yang dihasilkan lalu memicu handler terpisah untuk masing-masing event, misalnya satu handler mengirim email dan handler lain memperbarui data statistik. Pola ini memudahkan penambahan reaksi baru terhadap kejadian bisnis yang sama tanpa perlu mengubah kode di dalam entity Pesanan itu sendiri.
Kesalahan Umum yang Sering Terjadi
Menaruh Logika Validasi Bisnis di Use Case, Bukan di Entity
Menulis validasi seperti 'pesanan selesai tidak bisa dibatalkan' di dalam use case Application alih-alih di method entity Domain membuat aturan bisnis tersebar dan berpotensi dilanggar jika ada jalur kode lain yang mengubah status pesanan tanpa melalui use case tersebut.
Membuat Use Case yang Terlalu Generic dan Menangani Banyak Hal
Use case yang menangani terlalu banyak skenario sekaligus dalam satu class kehilangan kejelasan single responsibility yang menjadi salah satu manfaat utama memisahkan use case per aksi bisnis yang spesifik.
Tidak Memisahkan Project Secara Fisik Sesuai Layer
Hanya memisahkan layer dengan folder dalam satu project csproj yang sama tanpa reference project terpisah tidak memberikan jaminan fisik terhadap Dependency Rule, hanya mengandalkan disiplin developer yang bisa saja terlewat tanpa disadari.
Kapan Pendekatan Ini Tidak Cocok Dipakai
Memisahkan project secara fisik per layer memberikan jaminan arsitektural yang kuat dan terdeteksi sejak kompilasi, namun menambah kompleksitas setup solution dengan banyak project kecil yang saling mereferensikan.
Untuk project yang lebih kecil, memisahkan layer hanya dengan folder dalam satu project bisa menjadi kompromi yang lebih ringan, meski kehilangan jaminan fisik terhadap Dependency Rule dan sepenuhnya bergantung pada disiplin tim dalam menjaga pemisahan tersebut.
Ringkasan
Memisahkan Domain, Application, dan Infrastructure dengan jelas memastikan logika bisnis inti tetap murni dan tidak tercampur dengan detail teknis, dengan use case di Application yang hanya mengorkestrasi tanpa menyimpan aturan bisnis itu sendiri. Struktur project terpisah secara fisik memberikan jaminan arsitektural yang lebih kuat dibanding sekadar pemisahan folder.
Seri Belajar REST API & Clean Architecture:
← Sebelumnya: Pengenalan Clean Architecture pada Aplikasi Backend
→ Selanjutnya: Dependency Rule dalam Clean Architecture