Alat Desain Berbasis Domain

Paus biru adalah contoh yang bagus tentang bagaimana desain proyek yang kompleks menjadi salah. Paus itu terlihat seperti seekor ikan, tetapi itu adalah mamalia: ia memberi makan anak-anaknya dengan susu, ia memiliki wol, dan tulang-tulang lengan bawah dan tangan dengan jari-jari masih dipertahankan di sirip, seperti di darat. Dia hidup di lautan, tetapi tidak bisa bernapas di bawah air, jadi dia secara teratur naik ke permukaan untuk menelan udara, bahkan ketika dia sedang tidur. Paus itu adalah hewan terbesar di dunia, dengan panjang rumah sembilan lantai, dan beratnya seperti 75 mobil Volkswagen Touareg, tetapi bukan pemangsa, tetapi memakan plankton.

Ketika pengembang mengerjakan paus, mereka tidak mulai menulis semuanya dari awal, tetapi menggunakan pengalaman dari proyek-proyek lama. Tampaknya dirakit bersama-sama dari bagian yang tidak kompatibel dari kode yang tidak diuji, dan semua desain datang untuk memilih kerangka kerja dan "bersepeda" mendesak sudah dalam produksi. Alhasil, proyek itu ternyata indah dalam penampilan, tetapi dengan potongan-potongan warisan yang lebat dan kruk di bawah kap.



Untuk membuat proyek yang membantu bisnis menghasilkan uang, daripada terlihat seperti binatang laut yang tidak bisa bernapas di bawah air, ada DDD. Ini adalah pendekatan yang tidak berfokus pada alat atau kode, tetapi pada studi bidang subjek, proses bisnis individu dan bagaimana kode atau alat bekerja untuk logika bisnis.

Apa itu DDD dan alat apa yang ada di dalamnya, kami akan kirim dalam sebuah artikel berdasarkan laporanArtyom Malyshev . Pendekatan DDD dengan Python, alat, perangkap, pemrograman kontrak, dan desain produk di sekitar masalah yang diselesaikan, daripada kerangka kerja yang digunakan, semuanya berada di bawah jalan pintas.

Presentasi lengkap dari laporan .

Artem Malyshev (proofit404) - pengembang independen, menulis dalam Python selama 5 tahun, aktif membantu dengan Django Channels 1.0. Kemudian, ia fokus pada pendekatan arsitektur: ia mempelajari apa yang kurang dimiliki oleh arsitek Python dan memulai proyek dry-python . Salah satu pendiri Drylabs.

Kompleksitas


Apa itu pemrograman?
Pemrograman adalah perjuangan konstan dengan kompleksitas yang dibuat oleh pengembang sendiri ketika mereka mencoba untuk memecahkan masalah.
Kompleksitas dibagi menjadi dua jenis: diperkenalkan dan alami. Yang diperkenalkan meluas bersama dengan bahasa pemrograman, kerangka kerja, OS, model asinkron. Ini adalah tantangan teknis yang tidak berlaku untuk bisnis. Kompleksitas alami disembunyikan dalam produk dan menyederhanakan kehidupan pengguna - untuk ini orang membayar uang.
Insinyur yang baik harus mengurangi kompleksitas yang ditambahkan dan meningkatkan sifat alami untuk meningkatkan kegunaan produk.
Tapi kami programmer adalah orang-orang yang kompleks dan kami senang menambahkan kompleksitas teknis untuk proyek-proyek. Sebagai contoh, kami tidak repot-repot dengan standar pengkodean, tidak menggunakan linter, praktik desain modular dan menerima banyak kode gaya dalam proyek if c==1.

Bagaimana cara kerjanya dengan kode seperti itu? Baca banyak file, pahami variabel, kondisi, dan kapan dan bagaimana semuanya akan bekerja. Kode ini sulit untuk diingat - benar-benar menambah kompleksitas teknis.

Contoh lain dari kompleksitas yang ditambahkan adalah "panggilan balik neraka" favorit saya.



Ketika kita menulis dalam kerangka arsitektur berorientasi peristiwa (EDA) dan memilih kerangka kerja modern yang tidak begitu baik, kita mendapatkan kode di mana tidak jelas apa yang terjadi dan kapan. Sulit untuk membaca kode seperti itu - ini lagi kompleksitas yang ditambahkan.

Pemrogram tidak hanya menyukai kesulitan teknis, tetapi juga berdebat mana yang lebih baik:

  • AsyncIO atau Gevent;
  • PostgreSQL atau MongoDB;
  • Python atau Go;
  • Emacs atau Vim;
  • tab atau spasi;

Jawaban yang benar dari seorang programmer yang baik untuk semua pertanyaan ini: "Tidak ada bedanya!" Pengembang yang baik tidak berdebat tentang kuda bundar dalam ruang hampa, tetapi menyelesaikan masalah bisnis dan bekerja pada kegunaan produk. Beberapa dari mereka telah lama menetapkan serangkaian praktik yang mengurangi kompleksitas yang diperkenalkan dan membantu Anda lebih memikirkan bisnis.

Salah satunya adalah Eric Evans . Pada 2004, ia menulis buku Domain Driven Design. Dia β€œmenembak” dan memberikan dorongan untuk berpikir lebih banyak tentang bisnis, dan mendorong detail teknis ke latar belakang.



Apa itu DDD?


Pertama solusi untuk masalah, dan kemudian alat . Pertama-tama, Evans berinvestasi dalam konsep DDD, bahwa ini bukan teknologi, tetapi sebuah filosofi. Dalam filsafat, pertama-tama Anda harus berpikir tentang cara menyelesaikan masalah, dan hanya kemudian, dengan bantuan alat yang mana.

Kerjakan model dengan ahli materi pelajaran dan pengembang perangkat lunak. Kita harus berkomunikasi dengan orang-orang dari bisnis: mencari bahasa yang sama, membangun model dunia di mana produk kami akan bekerja dan menyelesaikan masalah.

Tulis perangkat lunak yang secara eksplisit mengekspresikan model. Perbedaan paling penting antara DDD dan kolaborasi sederhana dalam sebuah tim adalah bahwa kita harus menulis perangkat lunak dengan gaya yang sama ketika kita berbicara dengan para ahli domain. Semua terminologi, pendekatan diskusi dan pengambilan keputusan harus disimpan dalam kode sumber sehingga orang yang tidak teknis pun dapat memahami apa yang terjadi di sana.

Berbahasa yang sama dengan bisnis . DDD adalah filosofi tentang bagaimana berbicara bahasa yang sama dengan pakar bisnis di bidang tertentu dan menerapkan terminologi untuk bidang ini. Kami memiliki bahasa atau dialek yang sama dalam konteks terikat, yang kami anggap benar. Kami membuat batasan di sekitar solusi arsitektur.

DDD bukan tentang teknologi.

Pertama, bagian teknis, lalu - DDD. Pematung yang mengukir patung itu dari batu tidak membaca manual tentang cara memegang palu dan pahat - dia sudah tahu cara bekerja dengan mereka. Untuk membawa DDD ke proyek Anda, kuasai bagian teknisnya: pelajari Django sampai akhir, baca tutorialnya dan berhentilah berdebat apakah akan menggunakan PostgreSQL atau MongoDB.

Kebanyakan pola dan pola desain adalah noise teknis. Sebagian besar pola yang kita ketahui dan gunakan adalah teknis. Mereka mengatakan bagaimana menggunakan kembali kode, bagaimana menyusunnya, tetapi mereka tidak mengatakan bagaimana menggunakannya untuk pengguna, bisnis, dan model dunia luar. Oleh karena itu, pabrik atau kelas abstrak terikat dengan DDD.

Buku "biru" pertama keluar hampir 20 tahun yang lalu. Orang-orang mencoba menulis dengan gaya ini, berjalan menyapu, dan menyadari bahwa filosofi itu bagus, tetapi dalam praktiknya tidak bisa dipahami. Oleh karena itu, buku kedua muncul - "merah", tentang bagaimana programmer berpikir dan menulis dalam DDD.


Buku-buku "merah" dan "biru" adalah pilar di mana semua DDD berdiri.

Catatan. Buku-buku merah dan biru adalah sumber informasi unik tentang DDD, tetapi mereka berat. Buku tidak mudah dibaca: dalam bahasa aslinya karena bahasa dan istilah yang rumit, dan dalam bahasa Rusia karena terjemahannya yang buruk. Karena itu, mulailah belajar DDD dengan buku hijau . Ini adalah versi sederhana dari dua yang pertama dengan contoh dan deskripsi umum yang lebih sederhana. Tapi itu lebih baik daripada jika buku merah dan biru mengalahkan keinginan Anda untuk belajar dan menerapkan DDD. Lebih baik membaca dalam aslinya.

Buku merah melompati gagasan tentang cara terbaik untuk membawa DDD ke dalam proyek, bagaimana menyusun pekerjaan di sekitar pendekatan ini. Sebuah terminologi baru muncul - "Desain Model-Driven", di mana model kita dari dunia luar diletakkan di tempat pertama.



Satu-satunya tempat teknologi yang dipilih adalah Smart UI. Ini adalah lapisan antara dunia luar, pengguna dan kita (referensi ke Robert Martin dan arsitektur bersihnya dengan lapisan). Seperti yang Anda lihat, semuanya sesuai dengan model.

Apa itu model? Ini adalah rasa sakit hantu arsitek mana pun. Semua orang berpikir ini adalah UML, tetapi sebenarnya tidak.
Model adalah sekumpulan kelas, metode, dan tautan di antara mereka yang mencerminkan skenario bisnis dalam program.
Model mencerminkan objek nyata dengan semua properti dan fungsi yang diperlukan. Ini adalah perangkat tingkat tinggi untuk membuat keputusan dari sudut pandang kasus bisnis. Metode dan kelas, bagaimanapun, adalah toolkit tingkat rendah untuk solusi arsitektur.

Python kering


Untuk mengisi ceruk model, saya memulai proyek python kering yang telah berkembang menjadi koleksi perpustakaan arsitektur tingkat tinggi untuk membangun Model Driven Design. Masing-masing perpustakaan mencoba untuk menutup satu lingkaran dalam arsitektur dan tidak mengganggu yang lain. Perpustakaan dapat digunakan secara terpisah, atau bersama-sama jika Anda merasakannya.



Urutan narasi sesuai dengan kronologi penambahan optimal DDD ke proyek - demi lapisan. Lapisan pertama adalah layanan , deskripsi skenario bisnis (proses) dalam sistem kami. Perpustakaan Cerita bertanggung jawab atas lapisan ini.

Cerita


Skenario bisnis dibagi menjadi tiga bagian:

  • spesifikasi - deskripsi proses bisnis;
  • Keadaan di mana skenario bisnis mungkin ada
  • implementasi setiap langkah skrip.

Bagian-bagian ini tidak boleh dicampur. Perpustakaan Cerita memisahkan bagian-bagian ini dan menarik garis yang jelas di antara mereka.

Pertimbangkan pengantar DDD dan Cerita dengan sebuah contoh. Sebagai contoh, kami memiliki proyek pada Django dengan mishmash dari sinyal Django dan model "tebal" yang tidak jelas. Tambahkan paket layanan kosong ke dalamnya. Menggunakan pustaka Stories di beberapa bagian, kami menulis ulang hash ini menjadi sekumpulan skrip yang jelas dan dapat dipahami dalam proyek kami.

Spesifikasi DSL. Perpustakaan memungkinkan Anda untuk menulis spesifikasi dan menyediakan DSL untuk ini. Ini adalah cara untuk menggambarkan tindakan pengguna langkah demi langkah. Misalnya, untuk membeli subscription, saya mengikuti beberapa langkah: Saya akan menemukan pesanan, memeriksa relevansi harga, memeriksa apakah pengguna mampu membelinya. Ini adalah deskripsi tingkat tinggi.

Kontrak.Di bawah kelas ini kami akan menulis kontrak untuk keadaan skenario bisnis. Untuk melakukan ini, kami menunjukkan area variabel yang muncul dalam proses bisnis, dan untuk setiap variabel kami menetapkan satu set validator.

Segera setelah seseorang mencoba menetapkan variabel ke area ini sebagai bagian dari proses bisnis, satu set validator akan dikerjakan. Kami akan memastikan bahwa kondisi proses saat runtime selalu berfungsi. Tetapi jika tidak, itu jatuh dan menjerit keras tentang hal itu.

Tahap implementasi setiap langkah . Di kelas yang sama, subscriptionkami menulis serangkaian metode yang namanya sesuai dengan langkah-langkah bisnis. Setiap metode input menerima status yang dapat digunakannya, tetapi tidak memiliki hak untuk memodifikasinya. Metode ini dapat mengembalikan beberapa penanda dan melaporkan:

  • , () ;
  • - , .


Ada penanda yang lebih kompleks: mereka dapat mengkonfirmasi bahwa negara berfungsi, menyarankan menghapus atau mengubah beberapa bagian dari proses bisnis. Anda juga bisa menulis di kelas.

Luncurkan Story. Bagaimana cara menjalankan Story pada saat eksekusi? Ini adalah objek bisnis yang berfungsi sebagai metode: kami mentransfer data ke input, memvalidasinya, menginterpretasikan langkah-langkahnya. Kisah yang berjalan mengingat sejarah eksekusi, mencatat keadaan yang terjadi di dalamnya dalam proses bisnis, dan memberi tahu kami siapa yang memengaruhi keadaan ini .

Bilah alat debug. Jika kita menulis dalam Django dan menggunakan panel debug, kita dapat melihat skenario bisnis apa yang diproses dalam setiap permintaan dan statusnya.

Py.test. Jika kita menulis di py.test, maka untuk tes jatuh kita bisa melihat skrip bisnis apa yang dieksekusi pada setiap baris dan apa yang salah. Ini nyaman - alih-alih memilih kode, kita membaca spesifikasi dan memahami apa yang terjadi.



Penjaga. Bahkan lebih baik, ketika kita mendapatkan kesalahan 500. Dalam sistem reguler, kita tahan dengan itu dan mulai menyelidiki. Di Sentry, laporan terperinci akan muncul tentang apa yang dilakukan pengguna untuk membuat kesalahan. Sangat mudah dan menyenangkan ketika pada pukul 3 pagi informasi tersebut dikumpulkan untuk Anda.


RUSA . Sekarang kami secara aktif bekerja pada sebuah plugin yang menulis semua ini di Elasticsearch ke tumpukan Kibana dan membangun indeks yang kompeten.



Misalnya, kami memiliki kontrak untuk status proses bisnis. Kita tahu apa yang ada di sana, misalnya,relation IDmelaporkan. Alih-alih penelitian kuno tentang apa yang pernah terjadi di sana, kami menulis permintaan di Kibana. Ini akan menampilkan semua Cerita yang terkait dengan pengguna tertentu. Selanjutnya, kami memeriksa keadaan dalam proses bisnis dan skenario bisnis kami. Kami tidak menulis satu baris kode logging, tetapi proyek ini dicatat pada tingkat abstraksi yang sangat menarik untuk kami tonton.

Tapi saya ingin sesuatu yang lebih tinggi, misalnya, benda ringan. Objek tersebut mengandung struktur data dan metode melek yang terkait dengan adopsi keputusan bisnis, dan tidak bekerja dengan database, misalnya. Oleh karena itu, kita beralih ke bagian selanjutnya dari arsitektur Model-Driven - entitas, agregat, dan nilai objek.



Entitas, agregat dan nilai objek


Bagaimana semua ini saling berhubungan? Misalnya, pengguna melakukan pemesanan produk dan kami menagih. Apa akar agregasi, dan apa itu objek sederhana?



Semua yang digarisbawahi adalah akar agregasi. Inilah yang ingin saya kerjakan secara langsung: penting, berharga, holistik.

Di mana untuk memulai? Kami akan membuat paket kosong di proyek, di mana kami akan menempatkan unit kami. Agregat lebih baik ditulis dengan sesuatu yang bersifat deklaratif, seperti dataclassesatau attrs.

Dataclasses . Jika dataclasskami menunjukkan beberapa jenis agregat, kami akan menulis anotasi tentang itu menggunakan NewType . Dalam anotasi kami menunjukkan referensi eksplisit, yang dinyatakan dalam sistem tipe. Jika itu dataclasshanya struktur data (entitas), maka simpan di dalam agregat.

Dalam konteks Cerita, hanya kelompok unsur kehidupan yang bisa berbohong. Akses ke sesuatu yang tertanam di dalamnya hanya dapat diperoleh melalui metode publik dan aturan tingkat tinggi. Ini memungkinkan Anda untuk secara logis dan kompeten membangun model di mana kami bekerja sama dengan para ahli dari bidang subjek. Ini adalah bahasa tunggal yang sama .



Masalahnya segera muncul - repositori. Saya memiliki database yang saya gunakan melalui Django, sebuah microservice tetangga tempat saya mengirim permintaan, ada JSON dan sebuah instance dari model Django. Untuk menerima data dan mentransfernya dengan tangan, cukup menelepon atau menguji metode ini dengan indah? Tentu saja tidak. Dry-python memiliki perpustakaan pemetaan yang memungkinkan Anda untuk memetakan abstraksi tingkat tinggi dan agregat domain ke tempat-tempat di mana kami menyimpannya.

Pemeta


Kami menambahkan satu paket lagi ke proyek kami - repositori tempat kami akan menyimpannya mappers. Inilah cara kami akan mentransfer logika bisnis tingkat tinggi ke dunia nyata.

Sebagai contoh, kita dapat menggambarkan bagaimana kita memetakan satu dataclasske model Django.

Django ORM. Kami membandingkan model pesanan dengan deskripsi Django ORM - kami melihat bidangnya.

Misalnya, kita dapat menulis ulang beberapa bidang melalui konfigurasi opsional. Berikut ini akan terjadi: mapperselama deklarasi itu akan membandingkan bagaimana dataclassmodel ditulis . Sebagai contoh, anotasi int( Order dataclassada bidang costdengan anotasi int) dalam model Django sesuai integer fielddengan opsi nullable="true". Di sini akan dataclassmenawarkan untuk menambahkan optionalke dataclass, atau untuk menghapusnullabledari field.

Melalui Mappers, Anda dapat menambahkan fungsi yang membaca atau menulis sesuatu. Pembaca adalah fungsi yang menerima agregat pada input dan mengembalikan referensi padanya. Penulis melakukan yang sebaliknya - mengembalikan unit. Di bawah tenda, misalnya, mungkin ada permintaan ke database melalui Django.

Definisi Swagger Operasi yang sama dapat dilakukan dengan layanan microser. Anda dapat menulis bagian dari skema swagger pada mereka dan memeriksa seberapa besar skema swagger dari layanan tertentu sesuai dengan model domain Anda. Selanjutnya, permintaan yang dikembalikan dari perpustakaan Permintaan akan diterjemahkan secara transparan dataclass.

Permintaan GraphQL. GraphQL dan microservices: Skema tipe antarmuka GraphQL bekerja dengan baik terhadapdataclass. Anda bisa menerjemahkan permintaan GraphQL tertentu ke dalam struktur data internal.

Mengapa repot dengan model data tingkat tinggi internal di dalam aplikasi? Untuk mengilustrasikan "mengapa", saya akan menceritakan kisah "menghibur".

Di salah satu proyek kami, soket web berfungsi melalui layanan Pusher. Kami tidak repot, kami membungkusnya dalam antarmuka agar tidak menelepon langsung. Antarmuka ini diikat di semua Cerita dan puas.

Tetapi persyaratan bisnis telah berubah. Ternyata jaminan yang disediakan Pusher untuk soket web tidak cukup. Misalnya, Anda memerlukan pengiriman pesan dan riwayat pesan yang terjamin selama 2 menit terakhir. Karena itu, kami memutuskan untuk pindah ke layanan Ably Realtime. Ini juga memiliki antarmuka - kita akan menulis adaptor dan mengikatnya di mana-mana, semuanya akan menjadi besar. Tidak juga.

Abstraksi yang digunakan Pusher (argumen fungsi) terperangkap di setiap objek bisnis. Saya harus memperbaiki sekitar 100 Cerita, dan memperbaiki pembentukan saluran pengguna tempat kami mengirimkan sesuatu.

Kembali ke tes.

Tes & cemoohan


Bagaimana Anda biasanya menguji perilaku ini dengan layanan eksternal? Kami membasahi sesuatu, kami menyaksikan bagaimana perpustakaan pihak ketiga dipanggil, dan itu saja - kami yakin semuanya baik-baik saja. Tetapi ketika perpustakaan berubah, format argumen berubah juga.

Anda dapat menyimpan seminggu menulis ulang ribuan tes dan ratusan kasus bisnis jika Anda menguji perilaku model internal secara berbeda. Misalnya, sesuatu yang mirip dengan pengujian integrasi: kami menulis ke aliran pengguna, dan sudah ada di dalam adaptor, Pusher atau Ably kami menerjemahkan aliran ini ke nama saluran normal sehingga tidak menuliskan semuanya ke dalam logika bisnis.

Ketergantungan


Dalam arsitektur model seperti itu, banyak entitas yang berlebihan muncul. Sebelumnya, kami mengambil beberapa jenis fungsi Django dan menulisnya: permintaan, respons, gerakan tubuh minimum. Di sini Anda perlu menginisialisasi Mappers, memasukkan Stories dan menginisialisasi, memproses baris permintaan permintaan HTTP, lihat jawaban mana yang akan diberikan. Semua ini menghasilkan 30-50 baris Cerita kode panggilan boilerplate di dalam Django-view.

Di sisi lain, kita sudah menulis antarmuka dan pemetaan. Kami dapat memeriksa kompatibilitasnya dengan kasus bisnis tertentu, misalnya, menggunakan perpustakaan Dependensi. Bagaimana? Melalui pola injeksi Ketergantungan, semuanya secara deklaratif direkatkan ke pelat ketel minimum.

Di sini kami menunjukkan tugas untuk mengambil kelas dalam paket layanan, masukkan tigamappers, inisialisasi objek Stories dan berikan kepada kami. Dengan pendekatan ini, jumlah boilerplate dalam kode berkurang sangat besar.

Peta refactoring


Dengan menggunakan semua yang saya bicarakan, kami mengembangkan skema di mana kami menulis ulang proyek besar dari sinyal Django ("panggilan balik neraka") ke Django menggunakan DDD.

Langkah pertama tanpa DDD . Awalnya kami tidak memiliki DDD - kami menulis MVP. Ketika mereka menghasilkan uang pertama mereka, mereka mengundang investor, dan meyakinkan mereka untuk beralih ke DDD.

Cerita tanpa kontrak. Kami memecah proyek menjadi kasus bisnis logis tanpa kontrak data.

Kontrak dan agregat . Kemudian, satu per satu, kami menyeret kontrak data untuk setiap model, yang dapat ditelusuri dalam arsitektur kami.

Pemeta . Pemetaan menulis untuk menyingkirkan templat data warehouse.

Ketergantungan injeksi . Singkirkan pola pengeleman.

Jika proyek Anda telah melampaui MVP dan sangat perlu diubah dalam arsitektur agar tidak tergelincir ke dalam warisan - lihatlah ke arah DDD.

legacy Python-, , , Moscow Python Conf++ 27 . Python . unconference, , , , Drylabs.

DDD Python, TechLead Conf β€” IT-, DDD . 8 , Call for Papers 6 .

All Articles