Arsitektur pragmatis murni. Brainstorming

Apakah ide itu masuk ke kepala Anda untuk menulis ulang aplikasi perusahaan Anda yang berani dari awal? Jika dari awal, maka itu wow. Setidaknya dua kali lebih sedikit kode, kan? Tetapi beberapa tahun akan berlalu, dan itu juga akan tumbuh, menjadi warisan ... tidak ada banyak waktu dan uang untuk menulis ulang untuk melakukannya dengan sempurna.

Tenang, pihak berwenang masih tidak akan mengizinkan untuk menulis ulang apa pun. Masih untuk refactor. Apa cara terbaik untuk menghabiskan sumber daya kecil Anda? Bagaimana cara refactor tempat membersihkan?

Judul artikel ini termasuk referensi ke buku Paman Bob "Arsitektur Bersih" , dan itu dibuat berdasarkan laporan yang luar biasa dari Victor Rentea ( twitter , situs web ) di JPoint (di bawah kucing ia akan mulai berbicara sebagai orang pertama, tetapi untuk sekarang baca yang pengantar). Membaca buku pintar, artikel ini tidak menggantikan, tetapi untuk deskripsi singkat diatur dengan sangat baik.

Idenya adalah bahwa hal-hal populer seperti "Arsitektur Bersih" sangat berguna. Kejutan Jika Anda perlu memecahkan masalah yang sangat spesifik, kode elegan sederhana tidak memerlukan usaha ekstra dan rekayasa berlebihan. Arsitektur murni mengatakan bahwa Anda perlu melindungi model domain Anda dari efek eksternal, dan memberi tahu Anda dengan tepat bagaimana hal ini dapat dilakukan. Pendekatan evolusi untuk meningkatkan volume layanan mikro. Tes yang membuat refactoring kurang menakutkan. Anda sudah tahu semua ini? Atau Anda tahu, tetapi Anda bahkan takut untuk memikirkannya, karena itu mengerikan apa yang harus dilakukan?

Siapa yang ingin mendapatkan pil anti-prokrastinasi ajaib yang akan membantu berhenti gemetar dan mulai refactoring - selamat datang di rekaman video dari laporan atau di bawah kucing.





Nama saya Victor, saya dari Romania. Secara formal, saya seorang konsultan, pakar teknis dan arsitek utama di IBM Romania. Tetapi jika saya diminta untuk memberikan definisi kegiatan saya sendiri, maka saya adalah seorang penginjil kode murni. Saya suka membuat kode yang cantik, bersih, dan didukung - sebagai aturan, saya berbicara tentang ini di laporan. Terlebih lagi, saya terinspirasi oleh pengajaran: melatih pengembang di bidang Java EE, Spring, Dojo, Test Driven Development, Performance Java, serta di bidang penginjilan yang disebutkan - prinsip-prinsip pola kode bersih dan pengembangan mereka.

Pengalaman yang menjadi dasar teori saya terutama adalah pengembangan aplikasi perusahaan untuk klien IBM terbesar di Rumania - sektor perbankan.

Rencana untuk artikel ini adalah sebagai berikut:

  • Pemodelan data: struktur data seharusnya tidak menjadi musuh kita;
  • Organisasi logika: prinsip "dekomposisi kode, yang terlalu banyak";
  • "Bawang" adalah arsitektur filosofi Naskah Transaksi yang paling murni;
  • Menguji sebagai cara untuk menghadapi ketakutan pengembang.


Tapi pertama-tama, mari kita ingat prinsip-prinsip utama yang kita, sebagai pengembang, harus selalu ingat.

Prinsip tanggung jawab tunggal





Dengan kata lain, kuantitas vs kualitas. Sebagai aturan, semakin banyak fungsionalitas kelas Anda, semakin buruk hasilnya dalam arti kualitatif. Mengembangkan kelas besar, programmer mulai menjadi bingung, membuat kesalahan dalam membangun dependensi, dan kode besar, antara lain, lebih sulit untuk di-debug. Lebih baik memecah kelas tersebut menjadi beberapa yang lebih kecil, yang masing-masing akan bertanggung jawab untuk beberapa subtugas. Lebih baik memiliki beberapa modul yang digabungkan secara ketat daripada yang satu - besar dan lambat. Modularitas juga memungkinkan penggunaan kembali logika.

Penjilidan modul yang lemah





Tingkat pengikatan adalah ukuran seberapa dekat modul Anda berinteraksi satu sama lain. Ini menunjukkan seberapa luas efek perubahan yang Anda buat pada satu titik di sistem dapat menyebar. Semakin tinggi pengikatannya, semakin sulit untuk membuat modifikasi: Anda mengubah sesuatu dalam satu modul, dan efeknya meluas jauh dan tidak selalu dengan cara yang diharapkan. Oleh karena itu, indikator pengikatan harus serendah mungkin - ini akan memberikan kontrol lebih besar atas sistem yang mengalami modifikasi.

Jangan diulang





Implementasi Anda sendiri mungkin bagus hari ini, tapi besok tidak begitu baik. Jangan biarkan diri Anda menyalin praktik terbaik Anda sendiri dan karenanya mendistribusikannya dalam basis kode. Anda dapat menyalin dari StackOverflow, dari buku - dari sumber otoritatif apa pun yang (seperti yang Anda tahu pasti) menawarkan implementasi yang ideal (atau dekat dengan itu). Meningkatkan implementasi Anda sendiri, yang terjadi lebih dari sekali, tetapi dikalikan di seluruh basis kode, bisa sangat melelahkan.

Kesederhanaan dan keringkasan





Menurut pendapat saya, ini adalah prinsip utama yang harus diperhatikan dalam rekayasa dan pengembangan perangkat lunak. "Enkapsulasi dini adalah akar kejahatan," kata Adam Bien. Dengan kata lain, akar kejahatan terletak pada "rekayasa ulang". Penulis kutipan, Adam Bien, pada suatu waktu terlibat dalam mengambil aplikasi warisan dan, sepenuhnya menulis ulang kode mereka, menerima basis kode 2-3 kali lebih kecil dari yang asli. Dari mana asal begitu banyak kode tambahan? Bagaimanapun, itu muncul karena suatu alasan. Ketakutannya memunculkan kita. Tampaknya bagi kami bahwa dengan menumpuk sejumlah besar pola, menghasilkan tidak langsung dan abstraksi, kami memberikan kode kami dengan perlindungan - perlindungan dari yang tidak diketahui besok dan persyaratan besok. Bagaimanapun, pada kenyataannya, hari ini kita tidak memerlukan semua ini, kita menciptakan semua ini hanya demi beberapa "kebutuhan masa depan". Dan ada kemungkinan bahwa struktur data ini selanjutnya akan mengganggu. Sejujurnya, ketika beberapa pengembang saya mendatangi saya dan mengatakan bahwa dia datang dengan sesuatu yang menarik yang dapat ditambahkan ke kode produksi, saya selalu menjawab dengan cara yang sama: "Wah, ini tidak akan berguna bagi Anda."

Seharusnya tidak ada banyak kode, dan yang harus sederhana - satu-satunya cara untuk bekerja secara normal dengannya. Ini adalah masalah bagi pengembang Anda. Anda harus ingat bahwa mereka adalah tokoh kunci untuk sistem Anda. Cobalah untuk mengurangi konsumsi energi mereka, untuk mengurangi risiko yang harus mereka jalani. Ini tidak berarti bahwa Anda harus membuat kerangka kerja Anda sendiri, terlebih lagi, saya tidak akan menyarankan Anda untuk melakukan ini: akan selalu ada bug dalam kerangka kerja Anda, semua orang perlu mempelajarinya, dll. Lebih baik menggunakan aset yang sudah ada, yang jumlahnya banyak hari ini. Ini harus menjadi solusi sederhana. Tuliskan penangan kesalahan global, terapkan teknologi aspek, pembuat kode, ekstensi Pegas atau CDI, konfigurasikan lingkup Permintaan / Utas, gunakan manipulasi bytecode dan hasilkan dengan cepat, dll. Semua ini akan menjadi kontribusi Anda pada hal yang benar-benar paling penting - kenyamanan pengembang Anda.

Secara khusus, saya ingin menunjukkan kepada Anda penerapan area Permintaan / Utas. Saya telah berulang kali menyaksikan bagaimana hal ini menyederhanakan aplikasi perusahaan. Intinya adalah memberi Anda kesempatan, sebagai pengguna yang masuk, untuk menyimpan data RequestContext. Dengan demikian, RequestContext akan menyimpan data pengguna dalam bentuk yang ringkas.



Seperti yang Anda lihat, implementasinya hanya membutuhkan beberapa baris kode. Setelah menulis permintaan dalam anotasi yang diperlukan (tidak sulit untuk melakukannya jika Anda menggunakan Spring atau CDI), Anda akan membebaskan diri dari kebutuhan untuk meneruskan login pengguna ke metode dan apa pun: metadata permintaan yang disimpan dalam konteks akan secara transparan menavigasi aplikasi. Proxy cakupan akan memungkinkan Anda untuk mengakses metadata dari permintaan saat ini kapan saja.

Tes regresi





Pengembang takut persyaratan yang diperbarui karena mereka takut dengan prosedur refactoring (modifikasi kode). Dan cara termudah untuk membantu mereka adalah membuat suite pengujian yang andal untuk pengujian regresi. Dengan itu, pengembang akan memiliki kesempatan kapan saja untuk menguji waktu operasi mereka - untuk memastikan bahwa itu tidak merusak sistem.

Pengembang tidak perlu takut untuk merusak apa pun. Anda harus melakukan segalanya agar refactoring dianggap sebagai sesuatu yang baik.
Refactoring adalah aspek penting dari pengembangan. Ingat, tepat pada saat pengembang Anda takut refactoring, aplikasi dapat dianggap telah menjadi Warisan.

Di mana menerapkan logika bisnis?





Memulai implementasi sistem apa pun (atau komponen sistem), kami bertanya pada diri sendiri pertanyaan: di mana lebih baik menerapkan logika domain, yaitu, aspek fungsional aplikasi kita? Ada dua pendekatan yang saling bertentangan.
Yang pertama didasarkan pada filosofi Naskah Transaksi . Di sini, logika diimplementasikan dalam prosedur yang bekerja dengan entitas anemia (yaitu, dengan struktur data). Pendekatan semacam itu baik karena dalam pelaksanaannya dimungkinkan untuk mengandalkan tugas-tugas bisnis yang dirumuskan. Saat bekerja pada aplikasi untuk sektor perbankan, saya telah berulang kali mengamati transfer prosedur bisnis ke perangkat lunak. Saya dapat mengatakan bahwa sangat alami untuk mengkorelasikan skenario dengan perangkat lunak.

Pendekatan alternatif adalah dengan menggunakan prinsip-prinsip Desain Berbasis Domain . Di sini Anda perlu menghubungkan spesifikasi dan persyaratan dengan metodologi berorientasi objek. Penting untuk secara hati-hati mempertimbangkan objek dan memastikan keterlibatan bisnis yang baik. Kelebihan dari sistem yang dirancang dengan cara ini adalah bahwa di masa depan mereka mudah dipelihara. Namun, dalam pengalaman saya, menguasai metodologi ini cukup sulit: Anda akan merasa lebih atau kurang berani tidak lebih awal dari setelah enam bulan mempelajarinya.

Untuk perkembangan saya, saya selalu memilih pendekatan pertama. Saya dapat meyakinkan Anda bahwa dalam kasus saya ini berfungsi dengan baik.

Pemodelan data



Entitas



Bagaimana kita memodelkan data? Segera setelah aplikasi mengambil ukuran yang lebih atau kurang layak, data persisten akan muncul. Ini adalah jenis data yang perlu Anda simpan lebih lama dari yang lainnya - ini adalah entitas domain dari sistem Anda. Di mana menyimpannya - baik dalam database, dalam file atau mengelola memori secara langsung - tidak masalah. Yang penting adalah bagaimana Anda akan menyimpannya - di mana struktur data.



Pilihan ini diberikan kepada Anda sebagai pengembang, dan itu hanya tergantung pada Anda apakah struktur data ini akan bekerja untuk Anda atau melawan Anda saat menerapkan persyaratan fungsional di masa depan. Agar semuanya menjadi baik, Anda harus mengimplementasikan entitas dengan meletakkannya di dalamnya logika domain yang digunakan kembali . Bagaimana tepatnya? Saya akan menunjukkan beberapa metode menggunakan contoh.



Mari kita lihat apa yang saya berikan kepada entitas Pelanggan. Pertama, saya menerapkan getFullName() getter sintetis yang akan mengembalikan saya gabungan dari firstName dan lastName. Saya juga menerapkan metode activate() - untuk memantau keadaan entitas saya, sehingga merangkumnya. Dalam metode ini, saya menempatkan, pertama, operasi validasi , dan, kedua, menetapkan nilai ke status dan diaktifkan oleh bidang , sehingga tidak perlu setter untuk mereka. Saya juga menambahkan ke entitas Pelanggan metode isActive() dan canPlaceOrders() , yang menerapkan validasi lambda di dalam diri saya. Ini disebut enkapsulasi predikat. Predikat seperti itu sangat berguna jika Anda menggunakan filter Java 8: Anda dapat meneruskannya sebagai argumen untuk filter. Saya menyarankan Anda untuk menggunakan pembantu ini.

Mungkin Anda menggunakan semacam ORM seperti Hibernate. Misalkan Anda memiliki dua entitas dengan komunikasi dua arah. Inisialisasi harus dilakukan di kedua sisi, jika tidak, seperti yang Anda pahami, Anda akan mengalami masalah saat mengakses data ini di masa mendatang. Tetapi pengembang sering lupa untuk menginisialisasi objek dari salah satu pihak. Saat mengembangkan entitas ini, Anda dapat memberikan metode khusus yang akan menjamin inisialisasi dua arah. Lihatlah addAddress() .



Seperti yang Anda lihat, ini adalah entitas yang sangat biasa. Namun di dalamnya terletak logika domain. Entitas semacam itu tidak boleh sedikit dan dangkal, tetapi tidak boleh dibebani dengan logika. Overflow dengan logika lebih sering terjadi: jika Anda memutuskan untuk mengimplementasikan semua logika di domain, maka untuk setiap kasus penggunaan akan tergoda untuk menerapkan beberapa metode tertentu. Sebagai aturan, ada banyak kasus penggunaan. Anda tidak akan menerima entitas, tetapi satu tumpukan besar dari semua jenis logika. Coba perhatikan ukurannya di sini: hanya logika yang digunakan kembali yang ditempatkan di domain dan hanya dalam jumlah kecil.

Nilai Objek



Selain entitas, Anda kemungkinan besar juga akan membutuhkan nilai objek. Ini tidak lain adalah cara untuk mengelompokkan data domain sehingga Anda dapat memindahkannya di sekitar sistem bersama-sama.

Objek nilai harus:

  • Kecil Tidak ada float untuk variabel moneter! Hati-hati saat memilih tipe data. Semakin kompak objek Anda, semakin mudah bagi pengembang baru untuk mengetahuinya. Ini adalah dasar untuk kehidupan yang nyaman.
  • Tidak bisa diubah . Jika objek benar-benar tidak berubah, maka pengembang mungkin tenang bahwa objek Anda tidak akan mengubah nilainya dan tidak akan rusak setelah dibuat. Ini meletakkan dasar untuk pekerjaan yang tenang dan percaya diri.




Dan jika Anda menambahkan panggilan metode validate() ke konstruktor, pengembang akan dapat tenang untuk validitas entitas yang dibuat (ketika melewati, katakanlah, mata uang tidak ada atau jumlah uang negatif, konstruktor tidak akan berfungsi).

Perbedaan antara entitas dan objek nilai



Objek nilai berbeda dari entitas karena mereka tidak memiliki ID tetap. Entitas akan selalu memiliki bidang yang terkait dengan kunci asing pada beberapa tabel (atau penyimpanan lainnya). Objek nilai tidak memiliki bidang tersebut. Muncul pertanyaan: apakah prosedur untuk memeriksa kesetaraan dua objek nilai dan dua entitas berbeda? Karena objek nilai tidak memiliki bidang ID, untuk menyimpulkan bahwa dua objek tersebut sama, Anda harus membandingkan nilai semua bidang mereka berpasangan (yaitu, periksa semua konten). Saat membandingkan entitas, cukup membuat satu perbandingan tunggal - berdasarkan ID lapangan. Dalam prosedur perbandingan inilah perbedaan utama antara entitas dan objek nilai terletak.

Objek Transfer Data (DTO)





Apa interaksi dengan antarmuka pengguna (UI)? Anda harus memberikan data untuk ditampilkan kepadanya . Apakah Anda benar-benar membutuhkan struktur lain? Begitulah. Dan semua karena antarmuka pengguna sama sekali bukan teman Anda. Dia memiliki permintaannya sendiri: dia membutuhkan data untuk disimpan sesuai dengan bagaimana mereka harus ditampilkan. Ini sangat luar biasa - kadang-kadang antarmuka pengguna dan pengembangnya yang mengharuskan kami. Maka mereka perlu mendapatkan data untuk lima baris; kemudian muncul di pikiran mereka untuk membuat bidang Boolean isDeletable untuk objek (dapatkah objek memiliki bidang seperti itu pada prinsipnya?) untuk mengetahui apakah tombol Hapus aktif atau tidak. Tapi tidak ada yang marah. Antarmuka pengguna hanya memiliki persyaratan yang berbeda.

Pertanyaannya adalah, bisakah entitas kita dipercayakan kepada mereka untuk digunakan? Kemungkinan besar, mereka akan mengubahnya, dan dengan cara yang paling tidak diinginkan bagi kita. Oleh karena itu, kami akan memberi mereka sesuatu yang lain - Data Transfer Objects (DTO). Mereka akan secara khusus disesuaikan dengan persyaratan eksternal dan untuk logika yang berbeda dari kita. Beberapa contoh struktur DTO adalah: Formulir / Permintaan (berasal dari UI), Lihat / Respons (dikirim ke UI), SearchCriteria / SearchResult, dll. Anda dapat, dalam arti tertentu, menyebutnya model API.

Prinsip penting pertama: DTO harus mengandung logika minimum.
Berikut adalah contoh implementasi dari CustomerDto .



Konten: bidang pribadi , getter publik , dan setter untuknya. Segalanya tampak super. OOP dalam segala kejayaannya. Tetapi satu hal buruk: dalam bentuk getter dan setter saya telah menerapkan terlalu banyak metode. Di DTO, harus ada logika sesedikit mungkin. Lalu apa jalan keluar saya? Saya membuat ladang menjadi publik! Anda akan mengatakan bahwa ini bekerja buruk dengan referensi metode dari Java 8, bahwa akan ada batasan, dll. Tapi percaya atau tidak, saya melakukan semua proyek saya (10-11 buah) dengan DTO tersebut. Saudara masih hidup. Sekarang, karena bidang saya bersifat publik, saya dapat dengan mudah mengatur nilainya menjadi dto.fullName hanya dengan memberi tanda sama dengan. Apa yang bisa lebih cantik dan lebih sederhana?

Organisasi Logika



Pemetaan



Jadi, kita punya tugas: kita perlu mengubah entitas kita menjadi DTO. Kami menerapkan transformasi sebagai berikut:



Seperti yang Anda lihat, dengan mendeklarasikan DTO, kami beralih ke operasi pemetaan (pemberian nilai). Apakah saya perlu menjadi pengembang senior untuk menulis tugas reguler dalam angka seperti itu? Bagi sebagian orang, ini sangat tidak biasa sehingga mereka mulai mengganti sepatu mereka saat bepergian: misalnya, menyalin data menggunakan semacam kerangka pemetaan menggunakan refleksi. Tetapi mereka kehilangan hal utama - bahwa cepat atau lambat, UI akan berinteraksi dengan DTO, akibatnya entitas dan DTO berbeda dalam maknanya.

Orang bisa, katakanlah, meletakkan operasi pemetaan di konstruktor. Tetapi ini tidak memungkinkan untuk pemetaan apa pun; khususnya, perancang tidak dapat mengakses database.

Dengan demikian, kami terpaksa meninggalkan operasi pemetaan dalam logika bisnis. Dan jika mereka memiliki penampilan yang kompak, maka tidak ada yang perlu dikhawatirkan. Jika pemetaan tidak mengambil beberapa baris, tetapi lebih, maka lebih baik untuk menempatkannya di mapper yang disebut. Seorang mapper adalah kelas yang dirancang khusus untuk menyalin data. Ini, secara umum, adalah hal kuno dan boilerplate. Namun di belakang mereka, Anda dapat menyembunyikan banyak tugas kami - untuk membuat kode lebih bersih dan lebih ramping.

Ingat: kode yang tumbuh terlalu besar harus dipindahkan ke struktur terpisah . Dalam kasus kami, operasi pemetaan benar-benar sedikit banyak, jadi kami memindahkannya ke kelas yang terpisah - mapper.

Haruskah pembuat peta mengizinkan akses ke database? Anda dapat mengaktifkannya secara default - ini sering dilakukan karena alasan kesederhanaan dan pragmatisme. Tapi itu menghadapkan Anda pada risiko tertentu.

Saya akan ilustrasikan dengan sebuah contoh. Berdasarkan DTO yang ada, kami menciptakan entitas Customer .



Untuk pemetaan, kita perlu mendapatkan tautan ke grup pelanggan dari basis data. Jadi saya menjalankan metode getReference() , dan mengembalikan saya beberapa entitas. Permintaan kemungkinan besar akan masuk ke database (dalam beberapa kasus ini tidak terjadi, dan fungsi rintisan berhasil).

Tetapi masalah tidak menunggu kita di sini, tetapi dalam metode yang melakukan operasi terbalik - mengubah entitas menjadi DTO.



Dengan menggunakan loop, kami menelusuri semua alamat yang terkait dengan Pelanggan yang ada dan menerjemahkannya ke alamat DTO. Jika Anda menggunakan ORM, maka, mungkin, ketika Anda memanggil metode getAddresses() , pemuatan malas akan dilakukan. Jika Anda tidak menggunakan ORM, maka ini akan menjadi permintaan terbuka untuk semua anak dari orang tua ini. Dan di sini Anda berisiko masuk ke "masalah N +1". Mengapa



Anda memiliki satu set orang tua, yang masing-masing memiliki anak. Untuk semua ini, Anda perlu membuat analog Anda sendiri di dalam DTO. Anda perlu melakukan satu kueri SELECT untuk melintasi N entitas induk dan kemudian N SELECT kueri untuk berkeliling anak-anak dari masing-masing. Total permintaan N + 1. Untuk 1000 entitas Customer induk, operasi semacam itu akan memakan waktu 5-10 detik, yang tentu saja membutuhkan waktu lama.

Misalkan, bagaimanapun, metode CustomerDto() disebut di dalam loop, mengonversi daftar objek Pelanggan ke daftar CustomerDto.



Masalah dengan N + 1 kueri memiliki solusi standar sederhana: di JPQL Anda dapat menggunakan FETCH oleh customer.addresses untuk mengambil anak-anak dan kemudian menghubungkan mereka menggunakan JOIN , dan dalam SQL Anda dapat menggunakan bypass dan WHERE .

Tetapi saya akan melakukannya secara berbeda. Anda dapat mengetahui berapa panjang maksimum daftar anak-anak (ini dapat dilakukan, misalnya, berdasarkan pencarian dengan pagination). Jika daftar hanya berisi 15 entitas, maka kita hanya perlu 16 kueri. Alih-alih 5ms kita akan menghabiskan segalanya, katakanlah, 15ms - pengguna tidak akan melihat perbedaannya.

Tentang pengoptimalan



Saya tidak akan menyarankan Anda untuk melihat kembali kinerja sistem pada tahap awal pengembangan. Seperti yang dikatakan Donald Knud: "Optimalisasi prematur adalah akar kejahatan." Anda tidak dapat mengoptimalkan dari awal. Inilah yang perlu dibiarkan nanti. Dan apa yang sangat penting: tidak ada asumsi - hanya pengukuran dan evaluasi pengukuran!

Apakah Anda yakin bahwa Anda kompeten bahwa Anda adalah seorang ahli yang nyata? Bersikaplah rendah hati dalam mengevaluasi diri sendiri. Jangan berpikir bahwa Anda memahami JVM sampai Anda membaca setidaknya beberapa buku tentang kompilasi JIT. Kebetulan programmer terbaik dari tim kami mendatangi saya dan mengatakan bahwa mereka pikir mereka telah menemukan implementasi yang lebih efisien. Ternyata mereka kembali menemukan sesuatu yang hanya memperumit kode. Jadi saya jawab berulang-ulang: YAGNI. Kami tidak membutuhkannya.

Seringkali, untuk aplikasi perusahaan, tidak ada optimasi algoritma yang diperlukan sama sekali. Hambatan bagi mereka, sebagai suatu peraturan, bukanlah kompilasi dan tidak sejauh menyangkut prosesor, tetapi semua jenis operasi input-output. Misalnya, membaca sejuta baris dari basis data, banyak menulis ke file, berinteraksi dengan soket.

Seiring waktu, Anda mulai memahami apa yang menjadi hambatan dalam sistem, dan, dengan memperkuat segala sesuatu dengan pengukuran, Anda akan mulai secara bertahap mengoptimalkan. Untuk saat ini, jaga kode sebersih mungkin. Anda akan menemukan bahwa kode semacam itu jauh lebih mudah untuk dioptimalkan lebih lanjut.

Lebih suka komposisi daripada warisan



Kembali ke DTO kami. Misalkan kita mendefinisikan DTO seperti ini:



Kita mungkin membutuhkannya dalam banyak alur kerja. Tetapi aliran ini berbeda dan, kemungkinan besar, setiap kasus penggunaan akan mengasumsikan tingkat pengisian bidang yang berbeda. Misalnya, kita jelas perlu membuat DTO lebih awal daripada ketika kita memiliki informasi pengguna lengkap. Anda dapat mengosongkan bidang untuk sementara waktu. Tetapi semakin banyak bidang yang Anda abaikan, semakin Anda ingin membuat DTO yang lebih ketat untuk kasus penggunaan ini.

Atau, Anda dapat membuat salinan DTO yang terlalu besar (dalam jumlah kasus penggunaan yang tersedia) dan kemudian menghapus bidang tambahan dari itu untuk setiap salinan. Tetapi bagi banyak programmer, berdasarkan kecerdasan dan literasi mereka, sangat menyakitkan untuk menekan Ctrl + V. Aksioma mengatakan copy-paste buruk.

Anda dapat menggunakan prinsip pewarisan yang dikenal dalam teori OOP: cukup tentukan DTO dasar dan buat pewaris untuk setiap kasus penggunaan.



Prinsip yang terkenal adalah: "Lebih suka komposisi daripada warisan." Baca apa yang tertulis: "memanjang" . Tampaknya kita harus "memperluas" kelas sumber. Tetapi jika Anda memikirkannya, maka apa yang telah kami lakukan bukanlah "ekspansi" sama sekali. Ini adalah "pengulangan" yang sebenarnya - tampilan sisi salin-tempel yang sama. Karena itu, kami tidak akan menggunakan warisan.

Tapi bagaimana seharusnya kita? Bagaimana cara komposisi? Mari kita lakukan dengan cara ini: tulis bidang di CustomerView yang akan menunjuk ke objek DTO yang mendasarinya.



Dengan demikian, struktur dasar kami akan bersarang di dalamnya. Beginilah komposisi yang sebenarnya keluar.

Apakah kita menggunakan warisan atau memecahkan masalah dengan komposisi - ini semua adalah rincian, seluk-beluk yang muncul secara mendalam selama pelaksanaan kami. Mereka sangat rapuh . Apa artinya rapuh? Lihatlah kode ini dari dekat:



Sebagian besar pengembang yang saya tunjukkan ini segera mengatakan bahwa angka "2" diulang, sehingga harus diambil sebagai konstanta. Mereka tidak memperhatikan bahwa deuce dalam ketiga kasus memiliki arti yang sama sekali berbeda (atau "nilai bisnis") dan bahwa pengulangannya tidak lebih dari suatu kebetulan. Menarik dua ke dalam konstanta adalah keputusan yang sah, tetapi sangat rapuh. Cobalah untuk tidak mengizinkan logika rapuh ke domain. Jangan pernah bekerja darinya dengan struktur data eksternal, khususnya, dengan DTO.

Jadi, mengapa pekerjaan menghilangkan warisan dan memperkenalkan komposisi menjadi tidak berguna? Justru karena kita membuat DTO bukan untuk diri kita sendiri, tetapi untuk klien eksternal. Dan bagaimana aplikasi klien akan mem-parsing DTO yang diterima dari Anda - Anda hanya bisa menebak. Tapi jelas, ini tidak ada hubungannya dengan implementasi Anda. Pengembang di sisi lain mungkin tidak membuat perbedaan untuk DTO dasar dan non-dasar yang telah Anda pikirkan dengan seksama; mereka mungkin menggunakan warisan, dan mungkin dengan bodohnya salin-tempel itu saja.

Fasad





Mari kita kembali ke gambaran keseluruhan aplikasi. Saya akan menyarankan Anda untuk mengimplementasikan logika domain melalui pola Facade , memperluas fasad dengan layanan domain sesuai kebutuhan. Layanan domain dibuat ketika terlalu banyak logika terakumulasi dalam fasad, dan lebih mudah untuk menempatkannya di kelas yang terpisah.
Layanan domain Anda harus selalu berbicara bahasa model domain Anda (entitas dan objek nilainya). Dalam kasus apa pun mereka tidak bekerja dengan DTO, karena DTO, seperti yang Anda ingat, adalah struktur yang terus berubah di sisi klien, terlalu rapuh untuk suatu domain.



Apa tujuan dari fasad?

  1. Konversi data. Jika kita memiliki entitas dari satu ujung dan DTO dari yang lain, perlu untuk melakukan transformasi dari satu ke yang lain. Dan ini adalah hal pertama yang diperuntukkan bagi fasad. Jika prosedur konversi bertambah dalam volume - gunakan kelas mapper.
  2. Implementasi logika. Di fasad, Anda akan mulai menulis logika utama aplikasi. Segera setelah itu menjadi banyak - ambil bagian ke layanan domain.
  3. Validasi data. Ingat bahwa data yang diterima dari pengguna, menurut definisi, salah (mengandung kesalahan). Fasad memiliki kemampuan untuk memvalidasi data. Prosedur-prosedur ini, ketika volume terlampaui, biasanya dibawa ke validator .
  4. Aspek Anda dapat melangkah lebih jauh dan membuat setiap kasing menggunakan fasadnya. Kemudian akan muncul untuk menambahkan hal-hal seperti transaksi, penebangan, penangan pengecualian global ke metode fasad. Saya perhatikan bahwa sangat penting untuk memiliki penangan pengecualian global dalam aplikasi apa pun yang menangkap semua kesalahan yang tidak ditangkap oleh penangan lain. Mereka akan sangat membantu programmer Anda - mereka akan memberi mereka ketenangan pikiran dan kebebasan bertindak.


Dekomposisi banyak kode





Beberapa kata lagi tentang prinsip ini. Jika kelas telah mencapai ukuran yang tidak nyaman untuk saya (katakanlah, 200 baris), maka saya harus mencoba memecahnya menjadi beberapa bagian. Tetapi mengisolasi kelas baru dari yang sudah ada tidak selalu mudah. Kita perlu menemukan beberapa cara universal. Salah satu metode ini adalah mencari nama: Anda mencoba mencari nama untuk subset dari metode kelas Anda. Segera setelah Anda berhasil menemukan nama, jangan ragu untuk membuat kelas baru. Tapi ini tidak sesederhana itu. Dalam pemrograman, seperti yang Anda tahu, hanya ada dua hal kompleks: ini men-cache dan nama inv invating. Dalam hal ini, menciptakan nama melibatkan mengidentifikasi subtugas - bersembunyi dan karena itu tidak diidentifikasi sebelumnya oleh siapa pun.

Contoh:



Dalam fasad asli CustomerFacade beberapa metode terkait langsung dengan pelanggan, dan beberapa terkait dengan preferensi pelanggan. Berdasarkan ini, saya akan dapat membagi kelas menjadi dua ketika mencapai ukuran kritis. Saya mendapatkan dua fasad: CustomerFacade dan CustomerPreferencesFacade . Satu-satunya hal yang buruk adalah bahwa kedua fasad ini memiliki tingkat abstraksi yang sama. Pemisahan dengan tingkat abstraksi menyiratkan agak berbeda.

Contoh lain:



Misalkan ada kelas OrderService di sistem kami di mana kami menerapkan mekanisme pemberitahuan email. Sekarang kami sedang membuat DeliveryService dan ingin menggunakan mekanisme pemberitahuan yang sama di sini. Salin-tempel dikecualikan. Mari kita lakukan dengan cara ini: ekstrak fungsionalitas pemberitahuan ke kelas AlertService baru dan tulis sebagai ketergantungan untuk kelas DeliveryService dan OrderService . Di sini, berbeda dengan contoh sebelumnya, pemisahan terjadi tepat pada tingkat abstraksi. DeliveryServicelebih abstrak daripada AlertService, karena menggunakannya sebagai bagian dari alur kerjanya.

Pemisahan dengan tingkat abstraksi selalu mengasumsikan bahwa kelas yang diekstraksi menjadi ketergantungan , dan ekstraksi dilakukan untuk digunakan kembali .

Tugas ekstraksi tidak selalu mudah. Hal ini juga dapat menyebabkan beberapa kesulitan dan memerlukan beberapa pengujian ulang unit test. Namun demikian, menurut pengamatan saya, bahkan lebih sulit bagi pengembang untuk mencari fungsionalitas dalam basis kode monolitik besar aplikasi.

Memasangkan pemrograman





Banyak konsultan akan berbicara tentang pemrograman pasangan, tentang fakta bahwa ini adalah solusi universal untuk setiap masalah pengembangan TI saat ini. Selama itu, programmer mengembangkan keterampilan teknis dan pengetahuan fungsional mereka. Selain itu, prosesnya sendiri menarik, menyatukan tim.

Berbicara bukan sebagai konsultan, tetapi secara manusiawi, yang paling penting adalah ini: pemrograman pasangan meningkatkan "faktor bus". Inti dari "faktor bus" adalah bahwa harus ada sebanyak mungkin orang dengan pengetahuan tentang struktur sistem sebanyak mungkin . Kehilangan orang-orang ini berarti kehilangan petunjuk terakhir untuk pengetahuan ini.

Refactoring pemrograman pasangan adalah seni yang membutuhkan pengalaman dan pelatihan. Hal ini berguna, misalnya, praktik refactoring agresif, melakukan hackathon, pemotongan, Coding Dojo, dll.

Pemrograman pasangan berfungsi dengan baik jika Anda perlu menyelesaikan masalah dengan kompleksitas tinggi. Proses bekerja bersama tidak selalu sederhana. Tapi itu menjamin Anda bahwa Anda akan menghindari "rekayasa ulang" - sebaliknya, Anda akan mendapatkan implementasi yang memenuhi persyaratan yang ditetapkan dengan kompleksitas minimal.



Mengatur format pekerjaan yang nyaman adalah salah satu tanggung jawab utama Anda kepada tim. Anda harus selalu menjaga kondisi kerja pengembang - berikan mereka kenyamanan dan kebebasan kreativitas yang lengkap, terutama jika mereka diminta untuk meningkatkan arsitektur desain dan kompleksitasnya.

“Saya seorang arsitek. Menurut definisi, saya selalu benar. "



Kebodohan ini secara berkala diungkapkan di depan umum atau di belakang layar. Dalam praktik hari ini, arsitek seperti itu semakin jarang ditemukan. Dengan munculnya Agile, peran ini secara bertahap diberikan kepada pengembang senior, karena biasanya semua pekerjaan, dengan satu atau lain cara, dibangun di sekitar mereka. Ukuran implementasi secara bertahap tumbuh, dan dengan ini ada kebutuhan untuk refactoring dan fungsionalitas baru sedang dikembangkan.

Arsitektur bawang



Bawang adalah filosofi Script Transaksi murni. Membangunnya, kami dipandu oleh tujuan melindungi kode yang kami anggap penting, dan untuk ini kami memindahkannya ke modul domain.



Dalam aplikasi kami, yang paling penting adalah layanan domain: mereka menerapkan aliran paling kritis. Pindahkan mereka ke modul domain. Tentu saja, ada baiknya juga memindahkan semua objek domain Anda di sini - entitas dan objek nilai. Segala hal lain yang telah kami susun hari ini - DTO, pembuat peta, validator, dll. - menjadi, garis pertahanan pertama dari pengguna. Karena pengguna, sayangnya, bukan teman kita, dan perlu untuk melindungi sistem darinya.

Perhatian pada ketergantungan ini:



Modul aplikasi akan tergantung pada modul domain - yaitu, bukan sebaliknya. Dengan mendaftarkan koneksi semacam itu, kami menjamin bahwa DTO tidak akan pernah masuk ke wilayah suci modul domain: mereka tidak terlihat dan tidak dapat diakses dari modul domain. Ternyata dalam arti kami memagari wilayah domain - kami membatasi akses ke sana oleh orang asing.

Namun, domain mungkin perlu berinteraksi dengan beberapa layanan eksternal. Dengan eksternal berarti tidak ramah, karena ia dilengkapi dengan DTO-nya. Apa saja pilihan kita?

Pertama: lewati musuh di dalam modul.



Jelas, ini adalah pilihan yang buruk: mungkin saja besok layanan eksternal tidak akan ditingkatkan ke versi 2.0, dan kami harus menggambar ulang domain kami. Jangan biarkan musuh di dalam domain!

Saya mengusulkan pendekatan yang berbeda: kami akan membuat adaptor khusus untuk interaksi .



Adaptor akan menerima data dari layanan eksternal, mengekstrak data yang diperlukan domain kami, dan mengubahnya menjadi jenis struktur yang diperlukan. Dalam hal ini, semua yang diperlukan dari kami selama pengembangan adalah untuk mengkorelasikan panggilan ke sistem eksternal dengan persyaratan domain. Anggap saja sebagai adaptor besar seperti ini . Saya menyebut lapisan ini "anti-korupsi."

Misalnya, kita mungkin perlu menjalankan kueri LDAP dari domain. Untuk melakukan ini, kami menerapkan "modul anti-korupsi" LDAPUserServiceAdapter.



Dalam adaptor kita dapat:

  • Sembunyikan panggilan API yang jelek (dalam kasus kami, sembunyikan metode yang mengambil array Objek);
  • Paket pengecualian dalam implementasi kami sendiri;
  • Konversikan struktur data orang lain menjadi milik mereka (menjadi objek domain kami);
  • Periksa validitas data yang masuk.


Ini adalah tujuan dari adaptor. Bagus, di antarmuka dengan setiap sistem eksternal yang Anda perlukan untuk berinteraksi, adaptor Anda harus diinstal.


Dengan demikian, domain tidak akan mengarahkan panggilan ke layanan eksternal, tetapi ke adaptor. Untuk melakukan ini, ketergantungan yang sesuai harus didaftarkan di domain (dari adaptor atau dari modul infrastruktur di mana ia berada). Tetapi apakah kecanduan ini aman? Jika Anda menginstalnya seperti ini, DTO layanan eksternal dapat masuk ke domain kami. Kita seharusnya tidak mengizinkan ini. Oleh karena itu, saya menyarankan Anda cara lain untuk memodelkan dependensi.

Prinsip Pembalikan Ketergantungan





Mari kita buat antarmuka, tulis di dalamnya tanda tangan dari metode yang diperlukan dan letakkan di dalam domain kami. Tugas adaptor adalah untuk mengimplementasikan antarmuka ini. Ternyata antarmuka di dalam domain, dan adaptor di luar, dalam modul infrastruktur yang mengimpor antarmuka. Dengan demikian, kami memutar arah ketergantungan pada arah yang berlawanan. Pada saat dijalankan, sistem domain akan memanggil kelas apa saja melalui antarmuka.

Seperti yang Anda lihat, hanya dengan memperkenalkan antarmuka ke dalam arsitektur, kami dapat menggunakan dependensi dan dengan demikian mengamankan domain kami dari struktur asing dan API yang jatuh ke dalamnya. Pendekatan ini disebut inversi ketergantungan .



Secara umum, inversi dependensi mengasumsikan bahwa Anda menempatkan metode yang menarik bagi Anda dalam antarmuka di dalam modul tingkat tinggi Anda (dalam domain), dan mengimplementasikan antarmuka ini dari luar - dalam satu atau modul jelek jelek lain (infrastruktur).

Antarmuka yang diimplementasikan di dalam modul domain harus berbicara dalam bahasa domain, yaitu, ia akan beroperasi pada entitasnya, parameternya, dan jenis kembali. Pada saat run time, domain akan memanggil kelas apa saja melalui panggilan polimorfik ke antarmuka. Kerangka kerja injeksi ketergantungan (seperti Spring dan CDI) memberi kita contoh konkret dari kelas yang tepat di runtime.

Tetapi yang utama adalah bahwa selama kompilasi modul domain tidak akan melihat isi dari modul eksternal. Itu yang kita butuhkan. Tidak ada entitas eksternal yang termasuk dalam domain.

Menurut Paman Bob , prinsip inversi kontrol (atau, demikian ia menyebutnya, "arsitektur plug-in") mungkin adalah yang terbaik yang ditawarkan paradigma OOP secara umum.



Strategi ini dapat digunakan untuk integrasi dengan sistem apa pun, untuk panggilan dan pesan yang sinkron dan asinkron, untuk mengirim file, dll.

Ikhtisar Bulb





Jadi, kami memutuskan bahwa kami akan melindungi modul domain. Di dalamnya ada layanan domain, entitas, objek nilai, dan sekarang antarmuka untuk layanan eksternal, ditambah antarmuka untuk repositori (untuk berinteraksi dengan database).

Strukturnya terlihat seperti ini:



Modul aplikasi, modul infrastruktur (melalui inversi dependensi), modul repositori (kami juga mempertimbangkan database sebagai sistem eksternal), modul batch, dan mungkin beberapa modul lain dinyatakan dependensi untuk domain. Arsitektur ini disebut "bawang" ; itu juga disebut "bersih," "heksagonal," dan "port dan adaptor."

Modul repositori



Saya akan berbicara singkat tentang modul repositori. Apakah akan mengeluarkannya dari domain adalah pertanyaan. Tugas repositori adalah membuat logika lebih bersih, menyembunyikan dari kita kengerian bekerja dengan data persisten. Pilihan untuk orang-orang jompo adalah menggunakan JDBC untuk berinteraksi dengan basis data:



Anda juga dapat menggunakan Spring dan JdbcTemplate:



Atau MyBatis DataMapper:



Tapi itu sangat rumit dan jelek sehingga tidak menganjurkan keinginan untuk melakukan apa pun lebih jauh. Oleh karena itu, saya sarankan menggunakan JPA / Hibernate atau Spring Data JPA. Mereka akan memberi kita kesempatan untuk mengirim pertanyaan yang dibangun bukan pada skema basis data, tetapi langsung berdasarkan model entitas kita.

Implementasi untuk JPA / Hibernate:



Dalam kasus Spring Data JPA:



Spring Data JPA dapat secara otomatis menghasilkan metode saat runtime, seperti, misalnya, getById (), getByName (). Ini juga memungkinkan Anda untuk mengeksekusi permintaan JPQL jika perlu - dan bukan ke database, tetapi ke model entitas Anda sendiri.

Kode Hibernate JPA dan Spring Data JPA benar-benar terlihat cukup bagus. Apakah kita perlu mengekstraknya dari domain? Menurut pendapat saya, ini tidak begitu dan perlu. Kemungkinan besar, kode akan lebih bersih jika Anda meninggalkan fragmen ini di dalam domain. Jadi bertindaklah sesuai situasi.



Jika Anda tetap membuat modul repositori, maka untuk organisasi dependensi lebih baik menggunakan prinsip inversi kontrol dengan cara yang sama. Untuk melakukan ini, tempatkan antarmuka di domain dan implementasikan dalam modul repositori. Adapun logika repositori, lebih baik untuk mentransfernya ke domain. Ini membuat pengujian nyaman, karena Anda dapat menggunakan objek Mock di domain. Mereka akan memungkinkan Anda untuk menguji logika dengan cepat dan berulang-ulang.

Secara tradisional, hanya satu entitas yang dibuat untuk repositori dalam domain. Mereka memecahnya menjadi potongan-potongan hanya ketika menjadi terlalu tebal. Ingat bahwa kelas harus kompak.

API





Anda bisa membuat modul terpisah, letakkan antarmuka yang diekstraksi dari fasad dan DTO yang bergantung padanya, lalu kemas dalam JAR, dan transfer ke klien Java Anda di formulir ini. Memiliki file ini, mereka akan dapat mengirim permintaan ke fasad.

Bola Pragmatis



Selain dari "musuh" kami kepada siapa kami memberikan fungsionalitas, yaitu, pelanggan, kami juga memiliki musuh dan, di sisi lain, modul-modul yang menjadi sandaran kami sendiri. Kita juga perlu melindungi diri kita dari modul-modul ini. Dan untuk ini saya menawarkan "bawang" yang sedikit dimodifikasi - di dalamnya seluruh infrastruktur digabungkan menjadi satu modul.


Saya menyebut arsitektur ini "bola pragmatis". Di sini, pemisahan komponen dilakukan sesuai dengan prinsip "milikku" dan "dapat diintegrasikan": secara terpisah, yang berkaitan dengan domain saya, dan secara terpisah, yang mengacu pada integrasi dengan kolaborator eksternal. Dengan demikian, hanya dua modul yang diperoleh: domain dan aplikasi. Arsitektur seperti itu sangat bagus, tetapi hanya ketika modul aplikasi kecil. Jika tidak, Anda sebaiknya kembali ke bawang tradisional.

Tes



Seperti yang saya katakan sebelumnya, jika semua orang takut dengan aplikasi Anda, pertimbangkan bahwa itu telah mengisi kembali jajaran Legacy.
Tapi tesnya bagus. Mereka memberi kami rasa percaya diri yang memungkinkan kami untuk melanjutkan refactoring. Namun sayangnya, kepercayaan ini dapat dengan mudah berubah menjadi tidak bisa dibenarkan. Saya akan menjelaskan alasannya. TDD (pengembangan melalui pengujian) mengasumsikan bahwa Anda adalah pembuat kode dan pembuat kasus pengujian: Anda membaca spesifikasi, mengimplementasikan fungsionalitas, dan segera menulis rangkaian uji untuknya. Tes, katakanlah, akan berhasil. Tetapi bagaimana jika Anda salah memahami persyaratan spesifikasi? Maka tes akan memeriksa tidak apa yang dibutuhkan. Jadi kepercayaan diri Anda tidak berharga. Dan semua karena Anda menulis kode dan tes sendiri.

Tapi coba tutup mata kita untuk ini. Tes masih diperlukan, dan bagaimanapun mereka memberi kita kepercayaan diri. Yang paling penting, tentu saja, kami menyukai tes fungsional: tes ini tidak menyiratkan efek samping, tanpa ketergantungan - hanya data input dan output. Untuk menguji domain, Anda perlu menggunakan objek tiruan: mereka akan memungkinkan Anda untuk menguji kelas secara terpisah.

Adapun permintaan basis data, mengujinya tidak menyenangkan. Tes-tes ini rapuh, mereka mengharuskan Anda pertama kali menambahkan data uji ke database - dan hanya setelah itu Anda dapat melanjutkan untuk menguji fungsionalitas. Tapi seperti yang Anda pahami, tes ini juga diperlukan, bahkan jika Anda menggunakan JPA.

Tes unit





Saya akan mengatakan bahwa kekuatan unit test bukan dalam kemungkinan menjalankannya, tetapi dalam apa yang meliputi proses penulisan mereka. Saat Anda menulis tes, Anda memikirkan kembali dan mengerjakan kode - mengurangi konektivitas, memecahnya menjadi kelas - dengan kata lain, melakukan refactoring berikutnya. Kode yang diuji adalah kode murni; itu lebih sederhana, keterhubungannya berkurang di dalamnya; secara umum, ini juga didokumentasikan (unit test yang ditulis dengan sempurna menggambarkan bagaimana kelas bekerja). Tidak mengherankan bahwa tes unit menulis sulit, terutama beberapa bagian pertama.



Pada tahap tes unit pertama, banyak orang benar-benar takut dengan prospek bahwa mereka benar-benar harus menguji sesuatu. Mengapa mereka diberikan begitu keras?

Karena tes ini adalah beban pertama di kelas Anda. Ini adalah pukulan pertama ke sistem, yang, mungkin, akan menunjukkan bahwa itu rapuh dan tipis. Tetapi Anda perlu memahami bahwa beberapa tes ini adalah yang paling penting untuk perkembangan Anda. Mereka, pada dasarnya, adalah teman baik Anda, karena mereka akan mengatakan segalanya karena kualitas kode Anda. Jika Anda takut pada tahap ini, maka Anda tidak akan jauh. Anda harus menjalankan pengujian untuk sistem Anda. Setelah itu, kompleksitas akan berkurang, tes akan ditulis lebih cepat. Menambahkannya satu per satu, Anda akan membuat basis pengujian regresi yang andal untuk sistem Anda. Dan ini sangat penting untuk pekerjaan pengembang Anda di masa depan. Akan lebih mudah bagi mereka untuk melakukan refactor; Mereka akan memahami bahwa sistem dapat diuji regresi kapan saja, oleh karena itu bekerja dengan basis kode aman. Dan, saya yakinkan Anda, mereka akan terlibat dalam refactoring dengan lebih rela.



Saran saya kepada Anda: jika Anda merasa memiliki banyak kekuatan dan energi hari ini, curahkan diri Anda untuk menulis unit test. Dan pastikan masing-masing bersih, cepat, memiliki berat sendiri dan tidak mengulangi yang lain.

Kiat



Merangkum semua yang telah dikatakan hari ini, saya ingin mengingatkan Anda dengan tips berikut:

  • Buat sesederhana mungkin selama mungkin (dan berapapun biayanya) : hindari "rekayasa ulang" dan optimasi yang terlambat, jangan membebani aplikasi;
  • , , ;
  • «» — ;
  • , — : ;
  • «», , — ;
  • Jangan takut pada tes : beri mereka kesempatan untuk menghancurkan sistem Anda, rasakan semua manfaatnya - pada akhirnya, mereka adalah teman Anda karena mereka dapat dengan jujur ​​menunjukkan masalah.


Dengan melakukan hal-hal ini, Anda akan membantu tim Anda dan diri Anda sendiri. Dan kemudian, ketika hari pengiriman produk tiba, Anda akan siap untuk itu.

Apa yang harus dibaca







. JPoint — , 19-20 - Joker 2018 — Java-. . .

Source: https://habr.com/ru/post/id419179/


All Articles