Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Petunjuk / Saran
Konten ini adalah kutipan dari eBook, Architect Modern Web Applications dengan ASP.NET Core dan Azure, tersedia di .NET Docs atau sebagai PDF gratis yang dapat diunduh yang dapat dibaca secara offline.
"Jika tukang bangunan membangun gedung dengan cara yang sama seperti programmer menulis program, maka burung pelatuk pertama yang datang akan menghancurkan peradaban."
- Gerald Weinberg
Anda harus mengarsitekturi dan merancang solusi perangkat lunak dengan mengingat kemudahan pemeliharaan. Prinsip-prinsip yang diuraikan dalam bagian ini dapat membantu memandu Anda menuju keputusan arsitektur yang akan menghasilkan aplikasi yang bersih dan dapat dipertahankan. Umumnya, prinsip-prinsip ini akan memandu Anda untuk membangun aplikasi dari komponen diskrit yang tidak digabungkan erat dengan bagian lain dari aplikasi Anda, melainkan berkomunikasi melalui antarmuka eksplisit atau sistem olahpesan.
Prinsip desain umum
Pemisahan kekhawatiran
Prinsip panduan saat mengembangkan adalah Pemisahan Kekhawatiran. Prinsip ini menegaskan bahwa perangkat lunak harus dipisahkan berdasarkan jenis pekerjaan yang dilakukannya. Misalnya, pertimbangkan aplikasi yang menyertakan logika untuk mengidentifikasi item penting untuk ditampilkan kepada pengguna, dan yang memformat item tersebut dengan cara tertentu untuk membuatnya lebih terlihat. Perilaku yang bertanggung jawab untuk memilih item mana yang akan diformat harus dipisahkan dari perilaku yang bertanggung jawab untuk memformat item, karena perilaku ini adalah masalah terpisah yang hanya terkait satu sama lain.
Secara arsitektur, aplikasi dapat dibangun secara logis untuk mengikuti prinsip ini dengan memisahkan perilaku bisnis inti dari infrastruktur dan logika antarmuka pengguna. Idealnya, aturan dan logika bisnis harus berada di proyek terpisah, yang seharusnya tidak bergantung pada proyek lain dalam aplikasi. Pemisahan ini membantu memastikan bahwa model bisnis mudah diuji dan dapat berkembang tanpa digabungkan erat ke detail implementasi tingkat rendah (juga membantu jika kekhawatiran infrastruktur tergantung pada abstraksi yang ditentukan dalam lapisan bisnis). Pemisahan kekhawatiran adalah pertimbangan utama di balik penggunaan lapisan dalam arsitektur aplikasi.
Enkapsulasi
Bagian aplikasi yang berbeda harus menggunakan enkapulasi untuk mengisolasinya dari bagian lain aplikasi. Komponen dan lapisan aplikasi harus dapat menyesuaikan implementasi internal mereka tanpa melanggar kolaborator mereka selama kontrak eksternal tidak dilanggar. Penggunaan enkapulasi yang tepat membantu mencapai konektor dan modularitas yang longgar dalam desain aplikasi, karena objek dan paket dapat diganti dengan implementasi alternatif selama antarmuka yang sama dipertahankan.
Di dalam kelas, enkapsulasi dicapai dengan membatasi akses luar ke keadaan internal kelas. Jika aktor luar ingin memanipulasi status objek, itu harus melakukannya melalui fungsi yang ditentukan dengan baik (atau setter properti), daripada memiliki akses langsung ke status privat objek. Demikian juga, komponen dan aplikasi aplikasi itu sendiri harus mengekspos antarmuka yang terdefinisi dengan baik untuk digunakan kolaborator mereka, daripada memungkinkan statusnya dimodifikasi secara langsung. Pendekatan ini membebaskan desain internal aplikasi untuk berevolusi dari waktu ke waktu tanpa khawatir hal itu akan memutus kolaborator, selama kontrak publik dipertahankan.
Kondisi global yang dapat diubah bersifat antitetis untuk enkapsulasi. Nilai yang diambil dari status global yang dapat diubah dalam satu fungsi tidak dapat diandalkan untuk memiliki nilai yang sama dalam fungsi lain (atau bahkan lebih jauh dalam fungsi yang sama). Memahami kekhawatiran dengan keadaan global yang dapat diubah adalah salah satu alasan bahasa pemrograman seperti C# memiliki dukungan untuk aturan cakupan yang berbeda, yang digunakan di mana-mana dari pernyataan hingga metode ke kelas. Perlu dicatat bahwa arsitektur berbasis data yang mengandalkan database pusat untuk integrasi di dalam dan di antara aplikasi adalah, sendiri, memilih untuk bergantung pada status global yang dapat diubah yang diwakili oleh database. Pertimbangan utama dalam desain berbasis domain dan arsitektur bersih adalah cara merangkum akses ke data, dan cara memastikan status aplikasi tidak dibuat tidak valid dengan akses langsung ke format persistensinya.
Inversi ketergantungan
Arah dependensi dalam aplikasi harus ke arah abstraksi, bukan detail implementasi. Sebagian besar aplikasi ditulis sedemikian rupa sehingga dependensi waktu kompilasi mengalir ke arah eksekusi runtime, menghasilkan grafik dependensi langsung. Artinya, jika kelas A memanggil metode dari kelas B dan kelas B memanggil metode dari kelas C, maka pada waktu kompilasi, kelas A akan bergantung pada kelas B, dan kelas B akan bergantung pada kelas C, seperti yang ditunjukkan pada Gambar 4-1.
Gambar 4-1. Diagram ketergantungan langsung.
Menerapkan prinsip inversi dependensi memungkinkan A untuk memanggil metode pada abstraksi yang diterapkan B, sehingga A dapat memanggil B pada waktu dijalankan, tetapi B bergantung pada antarmuka yang dikendalikan oleh A pada waktu kompilasi (dengan demikian, membalikkan dependensi waktu kompilasi yang khas). Pada waktu proses, alur eksekusi program tetap tidak berubah, tetapi pengenalan antarmuka berarti bahwa implementasi yang berbeda dari antarmuka ini dapat dengan mudah dicolokkan.
Gambar 4-2. Grafik dependensi terbalik.
Inversi dependensi adalah bagian penting dari membangun aplikasi yang digabungkan secara longgar, karena detail implementasi dapat ditulis untuk bergantung pada dan menerapkan abstraksi tingkat yang lebih tinggi, daripada sebaliknya. Aplikasi yang dihasilkan lebih dapat diuji, modular, dan dapat dipertahankan sebagai hasilnya. Praktik injeksi dependensi dimungkinkan dengan mengikuti prinsip inversi dependensi.
Dependensi eksplisit
Metode dan kelas harus secara eksplisit memerlukan objek kolaborasi yang mereka butuhkan untuk berfungsi dengan benar. Ini disebut Prinsip Dependensi Eksplisit. Konstruktor kelas memberikan kesempatan bagi kelas untuk mengidentifikasi hal-hal yang mereka butuhkan agar dalam keadaan valid dan berfungsi dengan baik. Jika Anda menentukan kelas yang dapat dibangun dan dipanggil, tetapi itu hanya akan berfungsi dengan baik jika komponen global atau infrastruktur tertentu diberlakukan, kelas-kelas ini tidak jujur dengan klien mereka. Kontrak konstruktor memberi tahu klien bahwa itu hanya membutuhkan hal-hal yang ditentukan (mungkin tidak ada jika kelas hanya menggunakan konstruktor tanpa parameter), tetapi kemudian pada runtime ternyata objek benar-benar membutuhkan sesuatu yang lain.
Dengan mengikuti prinsip dependensi eksplisit, kelas dan metode Anda jujur dengan klien mereka tentang apa yang mereka butuhkan untuk berfungsi. Mengikuti prinsip membuat kode Anda lebih mendokumentasikan diri dan kontrak pengodean Anda lebih ramah pengguna, karena pengguna akan percaya bahwa selama mereka memberikan apa yang diperlukan dalam bentuk parameter metode atau konstruktor, objek yang mereka kerjakan akan berperilaku dengan benar pada waktu proses.
Tanggung jawab tunggal
Prinsip tanggung jawab tunggal berlaku untuk desain berorientasi objek, tetapi juga dapat dianggap sebagai prinsip arsitektur yang mirip dengan pemisahan kekhawatiran. Ini menyatakan bahwa objek seharusnya hanya memiliki satu tanggung jawab dan bahwa mereka seharusnya hanya memiliki satu alasan untuk berubah. Secara khusus, satu-satunya situasi di mana objek harus berubah adalah jika cara melakukan satu tanggung jawabnya harus diperbarui. Mengikuti prinsip ini membantu menghasilkan sistem yang digabungkan dengan lebih longgar dan modular, karena banyak jenis perilaku baru dapat diimplementasikan sebagai kelas baru, daripada dengan menambahkan tanggung jawab tambahan ke kelas yang ada. Menambahkan kelas baru selalu lebih aman daripada mengubah kelas yang ada, karena belum ada kode yang bergantung pada kelas baru.
Dalam aplikasi monolitik, kita dapat menerapkan prinsip tanggung jawab tunggal pada tingkat tinggi ke lapisan dalam aplikasi. Tanggung jawab presentasi harus tetap berada dalam proyek UI, sementara tanggung jawab akses data harus disimpan dalam proyek infrastruktur. Logika bisnis harus disimpan dalam proyek inti aplikasi, di mana ia dapat dengan mudah diuji dan dapat berevolusi secara independen dari tanggung jawab lain.
Ketika prinsip ini diterapkan pada arsitektur aplikasi dan dibawa ke titik akhir logisnya, Anda mendapatkan layanan mikro. Layanan mikro tertentu harus memiliki satu tanggung jawab. Jika Anda perlu memperluas perilaku sistem, biasanya lebih baik melakukannya dengan menambahkan layanan mikro tambahan, daripada menambahkan tanggung jawab ke yang sudah ada.
Pelajari selengkapnya tentang arsitektur layanan mikro
Jangan ulangi kode yang sama (DRY)
Aplikasi harus menghindari menentukan perilaku yang terkait dengan konsep tertentu di beberapa tempat karena praktik ini adalah sumber kesalahan yang sering terjadi. Pada titik tertentu, perubahan persyaratan akan memerlukan perubahan perilaku ini. Kemungkinan setidaknya satu instance perilaku akan gagal diperbarui, dan sistem akan berperilaku tidak konsisten.
Daripada menduplikasi logika, merangkumnya dalam konstruksi pemrograman. Tetapkan otoritas tunggal atas perilaku ini, dan pastikan bagian lain dari aplikasi yang membutuhkan perilaku ini menggunakan konstruk baru.
Nota
Hindari mengaitkan perilaku yang hanya kebetulan berulang. Misalnya, hanya karena dua konstanta yang berbeda keduanya memiliki nilai yang sama, itu tidak berarti Anda hanya harus memiliki satu konstanta, jika secara konseptual mereka mengacu pada hal-hal yang berbeda. Duplikasi selalu lebih disukai daripada mengaitkannya dengan abstraksi yang salah.
Ketidaktahuan persistensi
Ketidaktahuan persistensi (PI) mengacu pada jenis yang perlu dipertahankan, tetapi kodenya tidak terpengaruh oleh pilihan teknologi persistensi. Jenis seperti itu di .NET terkadang disebut sebagai Objek CLR Lama Biasa (POCO), karena tidak perlu mewarisi dari kelas dasar tertentu atau menerapkan antarmuka tertentu. Ketidaktahuan persistensi sangat berharga karena memungkinkan model bisnis yang sama untuk dipertahankan dalam berbagai cara, menawarkan fleksibilitas tambahan ke aplikasi. Pilihan persistensi dapat berubah dari waktu ke waktu, dari satu teknologi database ke teknologi database lainnya, atau bentuk persistensi tambahan mungkin diperlukan selain apa pun yang dimulai aplikasi (misalnya, menggunakan cache Redis atau Azure Cosmos DB selain database relasional).
Beberapa contoh pelanggaran prinsip ini meliputi:
Kelas dasar yang diperlukan.
Implementasi antarmuka yang diperlukan.
Kelas yang bertanggung jawab atas penyimpanan data mereka sendiri (seperti pola Active Record).
Konstruktor tanpa parameter diperlukan.
Properti yang memerlukan kata kunci virtual.
Atribut yang dibutuhkan khusus untuk persistensi.
Persyaratan bahwa kelas memiliki salah satu fitur atau perilaku di atas menambahkan konektor antara jenis yang akan dipertahankan dan pilihan teknologi persistensi, sehingga lebih sulit untuk mengadopsi strategi akses data baru di masa depan.
Konteks terikat
Konteks terikat adalah pola pusat dalam Desain Domain-Driven. Mereka menyediakan cara mengatasi kompleksitas dalam aplikasi atau organisasi besar dengan memecahnya menjadi modul konseptual terpisah. Setiap modul konseptual kemudian mewakili konteks yang dipisahkan dari konteks lain (karenanya, terikat), dan dapat berkembang secara independen. Setiap konteks terikat idealnya harus bebas untuk memilih nama sendiri untuk konsep di dalamnya, dan harus memiliki akses eksklusif ke penyimpanan persistensinya sendiri.
Minimal, aplikasi web individu harus berusaha untuk menjadi konteks terikat mereka sendiri, dengan penyimpanan persistensi mereka sendiri untuk model bisnis mereka, daripada berbagi database dengan aplikasi lain. Komunikasi antara konteks terikat terjadi melalui antarmuka terprogram, bukan melalui database bersama, yang memungkinkan logika bisnis dan peristiwa terjadi sebagai respons terhadap perubahan yang terjadi. Konteks terbatas sangat sesuai dengan layanan mikro, yang idealnya juga diimplementasikan sebagai konteks terbatas individu mereka sendiri.