
Mengapa DDD biasanya didekati dari sisi yang salah? Dan sisi mana yang Anda inginkan? Apa hubungannya jerapah dan platipus dengan semua ini?
Khusus untuk Habr - transkrip teks laporan "Desain berbasis domain: resep untuk pragmatis." Laporan tersebut dibuat pada konferensi DotNext .NET, tetapi dapat bermanfaat tidak hanya bagi para donor, tetapi juga bagi semua orang yang tertarik pada DDD (kami yakin Anda akan menguasai beberapa contoh kode C #). Rekaman video dari laporan ini juga dilampirkan.
Halo semuanya, nama saya Alexey Merson. Saya akan memberi tahu Anda apa Desain Berbasis Domain dan apa esensinya, tapi pertama-tama, mari kita cari tahu mengapa itu diperlukan.

Martin Fowler berkata: "Ada beberapa hal yang kurang logis daripada logika bisnis." Jerapah jelas salah satu dari sedikit ini. Jarak antara otak dan laring jerapah hanya beberapa sentimeter. Namun, saraf yang menghubungkan mereka mencapai 4 meter. Pertama, dia turun melalui seluruh leher, di sana dia mengelilingi arteri dan kemudian dia kembali dengan cara yang hampir sama.
Sepintas memang tidak ada logika. Tapi ini hanya warisan padat yang tersisa dari ikan purba. Pada ikan, seperti yang Anda tahu, tidak ada leher, jadi saraf ini berjalan di sepanjang jalur optimal. Dan ketika mamalia muncul setelah beberapa juta tahun refactoring, saraf harus diperluas untuk menjaga kompatibilitas ke belakang. Nah, jangan merombak karena jerapah?
Tapi jerapah tidak apa-apa, karena ada platipus.

Pikirkan lagi. Mamalia Dengan paruh. Hidup terutama di air. Bertelur. Dan selain itu, beracun. Kelihatannya satu-satunya penjelasan logis untuk keberadaannya adalah bahwa ia berasal dari Australia.
Tapi saya pikir semuanya lebih dangkal. Kontraktor hanya lupa tentang desain dan menabung dengan StackOverflow, well, atau apa yang ada di sana pada masa itu.

Saya tahu apa yang Anda pikirkan sekarang: "Alexey, yah, Anda menjanjikan Domain-Driven Design kepada kami, dan inilah semacam" Di dunia binatang "!
Kolega, apa itu pembangunan? Pengembangan adalah ketika kita mengambil beberapa bagian dari dunia nyata, proses bisnis, dan mengubahnya menjadi kode, yaitu membangun model perangkat lunak. Masalah apa yang menanti kita di sepanjang jalan?
Yang pertama adalah kompleksitas proses bisnis itu sendiri, yaitu, kesulitan memahami bagaimana bisnis bekerja, proses apa yang terjadi di sana, oleh apa logika mereka dibangun.
Masalah kedua adalah implementasi proses bisnis ini dalam bentuk kode, penggunaan pola yang tepat, pendekatan yang tepat, dan sebagainya. Ini juga topik yang agak rumit.

Lihat, proses bisnis seperti jerapah: mereka mulai dengan yang uniseluler yang paling sederhana, dan kemudian "itu" melihat Anda, dan tidak ada yang mengerti "dari mana asalnya" atau "cara kerjanya".
Untuk membangun model yang sukses dari proses semacam itu, Anda harus terlebih dahulu menjawab pertanyaan "mengapa?". Mengapa kita ingin membangun model ini? Tujuan apa yang ingin kita capai? Lagi pula, jika pelanggan menginginkan jerapah yang diisi, tetapi mendapat nyali, maka ia akan kesal, bahkan jika pencernaan dalam model ini diimplementasikan untuk pesta untuk mata. Dan pelanggan tidak hanya akan kehilangan uang dan waktu, dia akan kehilangan kepercayaan pada kita sebagai pengembang, dan kita akan kehilangan reputasi dan pelanggan kita.
Tetapi bahkan jika kita mengetahui tujuannya, ini masih tidak menjamin bahwa kita tidak akan mendapatkan platipus sebagai hasilnya. Faktanya adalah bahwa tujuannya sedikit untuk dipahami. Tujuannya harus tercapai. Dan ini membantu kami Desain Berbasis Domain.

Tujuan utama Desain Berbasis Domain adalah untuk memerangi kompleksitas proses bisnis dan otomasi serta implementasinya dalam kode. "Domain" diterjemahkan sebagai "domain", dan pengembangan dan desain dalam kerangka pendekatan ini didorong menjauh dari domain.
Desain Berbasis Domain mencakup banyak hal. Desain strategis ini, dan interaksi antara orang-orang, dan pendekatan arsitektur, dan pola taktis - ini adalah gudang senjata lengkap yang benar-benar berfungsi dan sangat membantu untuk membuat proyek. Hanya ada satu "tetapi". Sebelum Anda mulai berurusan dengan kompleksitas dengan Desain Berbasis Domain, Anda perlu belajar bagaimana menangani kompleksitas Desain Berbasis Domain itu sendiri.

Ketika seseorang mulai terjun ke topik ini, sejumlah besar informasi jatuh padanya: buku tebal, banyak artikel, pola, contoh. Semua ini membingungkan, dan mudah, seperti kata mereka, tidak memperhatikan di balik pepohonan di hutan. Saya pernah merasakan ini pada diri saya sendiri, tetapi hari ini saya ingin berbagi pengalaman saya dengan Anda dan membantu Anda melewati hutan ini, akhirnya mulai menggunakan Desain Berbasis Domain.

Istilah Domain-Driven Design sendiri diusulkan oleh Eric Evans pada tahun 2003 dalam bukunya yang tidak dapat dilanggar, yang oleh komunitas disebut Blue Book. Masalahnya adalah bahwa paruh pertama buku Evans berbicara tentang pola taktis (Anda semua tahu mereka: ini adalah pabrik, entitas, repositori, layanan), dan orang-orang biasanya tidak sampai ke babak kedua. Pria itu terlihat: semuanya akrab, saya akan pergi dan mendapatkan aplikasi DDD.

Di sebelah kanan adalah apa yang terjadi jika Anda melempar pola taktis pada kompiler. Kiri - jika Anda menggunakan pola strategis.

Sejak dirilisnya Buku Biru, komunitas DDD yang agak kuat telah terbentuk, banyak hal telah dipikirkan kembali. Ya, dan Evans sendiri mengakui bahwa dia tidak lagi mengerti bagaimana dia bisa mengakhiri hal penting seperti desain strategis.
Dan 10 tahun kemudian, pada 2013, Buku Merah diterbitkan oleh Vaughn Vernon. Dan dalam buku ini, presentasi sudah dibangun dalam urutan yang benar: dimulai dengan desain strategis, dengan dasar-dasarnya. Dan ketika pembaca telah menerima basis yang diperlukan, maka mereka sudah mulai berbicara tentang pola taktis dan detail implementasi.
Biasanya dalam laporan DDD mereka merekomendasikan membaca Evans, di Internet bahkan ada seluruh manual di mana Anda perlu membaca bab untuk perendaman yang tepat. Saya sarankan melakukannya dengan lebih mudah: mulai dengan Buku Merah, bacalah, dan baru kemudian beralih ke Biru.
Dan karena desain strategis adalah hal yang sangat penting, mari kita bicara tentang ide-ide kuncinya.
"Ide-ide kunci dari desain strategis"
Dalam setiap proyek otomasi bisnis, selalu ada pakar domain. Inilah orang-orang yang paling mengerti bagaimana proses bisnis yang akan dimodelkan bekerja. Ini dapat berupa pengembang, eksekutif, manajer puncak. Secara umum, itu bisa siapa saja, kalau saja dia memahami proses-proses bisnis yang perlu kita tangani.

Di sisi lain, ada pakar teknis: pengembang, arsitek yang terlibat langsung dalam otomatisasi dan implementasi aplikasi. Dalam contoh yang digambarkan, pelanggan mungkin menginginkan kereta api anak-anak, tetapi ternyata itu semacam monster.
Mengapa ini terjadi? Karena interaksi antara pakar teknis dan pakar domain dalam situasi tipikal terlihat seperti ini: ada tembok besar-besar di antara mereka, dan seorang manajer berjalan di sepanjang bagian atas tembok ini dan pertama-tama mencoba mendengar apa yang mereka teriakkan di satu sisi dinding, kemudian ia mencoba meneriakkannya sebaik mungkin pada bungkusan itu. di sisi lain dinding, dan seterusnya dalam lingkaran.
Terkadang seorang manajer tuli, maka seluruh rantai manajer tersebut dapat dibangun, yang, tentu saja, tidak berkontribusi pada keberhasilan proyek. Dan bagaimana seharusnya?

Harus ada interaksi yang konstan. Pakar teknis, pakar domain - semua peserta proyek harus terus memelihara komunikasi, menyinkronkan, mendiskusikan tujuan, cara untuk mencapainya, dan mengapa kita melakukan semua ini.
Dan di sini kita sampai pada yang pertama dan, mungkin, titik kunci paling penting dari desain strategis dan Desain Berbasis Domain secara umum.

Komunikasi antara peserta proyek membentuk apa yang oleh Desain Domain-Driven disebut bahasa di mana-mana. Dia bukan satu dalam arti bahwa dia adalah satu untuk semua kesempatan. Justru sebaliknya. Itu tunggal dalam arti bahwa semua peserta berkomunikasi di dalamnya, semua diskusi berlangsung dalam satu bahasa dan semua artefak harus secara maksimal dalam satu bahasa, yaitu, mulai dari TK dan diakhiri dengan kode.
Skenario bisnis
Untuk diskusi lebih lanjut, kita perlu semacam skenario bisnis. Mari kita bayangkan situasi ini:

Direktur Grup JUG.ru datang kepada kami dan berkata: "Teman-teman, arus laporan semakin meningkat, orang-orang, secara umum, disiksa untuk melakukan semuanya secara manual ... Mari mengotomatiskan proses mempersiapkan konferensi." Kami membalas: "Oke!" - dan mulai bekerja.
Skenario pertama yang akan kami otomatisasi adalah: "Pembicara mengajukan aplikasi untuk laporan di acara tertentu dan menambahkan informasi tentang laporannya." Apa yang kita lihat dalam skenario ini? Apa itu pembicara, ada acara dan ada laporan, yang berarti sudah mungkin untuk membangun model domain pertama.

Di sini kami memiliki model domain: Pembicara - pembicara, Bicara - laporan, Acara - acara. Tetapi model domain tidak bisa tanpa batas, tidak bisa mencakup semuanya, jika tidak maka akan menjadi buram dan kehilangan fokus, sehingga model domain harus dibatasi oleh sesuatu. Ini adalah poin kunci selanjutnya.

Baik model domain dan bahasa di mana-mana dibatasi oleh konteks yang oleh Desain Domain-Driven disebut konteks terbatas. Dia membatasi model domain sedemikian rupa sehingga semua konsep di dalamnya tidak ambigu, dan semua orang mengerti apa yang dipertaruhkan.
Jika mereka mengatakan "Pengguna", maka semuanya harus jelas sekaligus, itu harus memiliki peran yang dapat dimengerti, makna yang dapat dimengerti, seharusnya tidak menjadi semacam pengguna abstrak dari sudut pandang industri TI.

Dalam kasus kami, model domain ini berlaku untuk konteks persiapan konferensi, sehingga dalam konteks itulah kami akan menyebutnya "Konteks perencanaan acara". Tetapi bagi pembicara untuk menambahkan sesuatu, mengubah informasi, dia harus entah bagaimana masuk, dia perlu diberi hak. Dan ini sudah akan menjadi konteks lain, "Konteks identitas", di mana akan ada semacam entitas mereka sendiri: Pengguna, Peran, Profil.
Dan lihat apa masalahnya di sini. Ketika seseorang masuk ke sistem dan bermaksud untuk memasukkan beberapa jenis informasi, secara fisik ini adalah orang yang sama, tetapi dalam konteks yang berbeda ia diwakili oleh entitas yang berbeda, dan entitas ini tidak terkait langsung.
Jika kami mengambil dan, misalnya, mewarisi Speaker dari Pengguna, maka kami akan mencampur hal-hal yang tidak dapat dicampur, dan beberapa atribut dapat dicampur dengan logika. Dan model tersebut akan kehilangan fokus pada makna spesifik yang dimilikinya, dibagi menjadi beberapa konteks.
Demo: Layanan penjualan
Mari kita sedikit menyimpang dari teori kering dan melihat kodenya.
Konferensi tidak hanya persiapan konten, tetapi juga penjualan. Mari kita bayangkan bahwa sebuah layanan untuk menjual tiket telah ditulis, dan seorang manajer penjualan mendatangi kami dan berkata, “Guys! Begitu seseorang menulis layanan ini, mari kita cari tahu, ada sesuatu yang tidak jelas bagi saya bagaimana diskon untuk pelanggan reguler dipertimbangkan. "
Setelah berbicara dengan manajer, kami mengetahui bahwa seluruh skenario layanan ini adalah ini: dengan mengklik pada Checkout harga tiket akhir dianggap dengan mempertimbangkan diskon pelanggan reguler, dan pesanan masuk ke status "Menunggu pembayaran".
Kode yang sekarang akan kita analisis dapat dilihat secara terpisah di
repositori .
Solusi Terbuka, lihat struktur:

Tampaknya semuanya terlihat baik: ada Aplikasi dan Inti (rupanya, orang tahu tentang lapisan), Repositori ... Rupanya, orang itu menguasai paruh pertama Evans.
Buka OrderCheckoutService. Apa yang kita lihat di sana? Ini
kodenya :
public void Checkout(long id) { var ord = _ordersRepository.GetOrder(id); var orders = _ordersRepository.GetOrders() .Count(o => o.CustomerId == ord.CustomerId && o.StateId == 3 && o.OrderDate >= DateTime.UtcNow.AddYears(-3)); ord.Price *= (100 - (orders >= 5 ? 30m : orders >= 3 ? 20m : orders >= 1 ? 10m : 0)) / 100; ord.StateId = 1; _ordersRepository.SaveOrder(ord); }
Kami melihat garis dengan Harga: di sini perubahan harga. Kami memanggil manajer penjualan kami dan berkata: "Di sini, singkatnya, diskon dipertimbangkan di sini, semuanya jelas":
ord.Price *= (100 - (orders >= 5 ? 30m : orders >= 3 ? 20m : orders >= 1 ? 10m : 0)) / 100;
Dia melihat dari balik bahunya, “Oh! Jadi seperti inilah bentuk Brainfuck! Dan mereka mengatakan kepada saya bahwa mereka menulis dalam bahasa C # ”.
Jelas, pengembang kode ini menanggapi wawancara dengan baik tentang algoritma dan struktur data. Saya menulis di olimpiade sekolah dengan gaya yang sama. Setelah beberapa waktu, menggunakan format dan refactoring
cabul , kami mencari tahu apa apa, dan menjelaskan kepada manajer penjualan lama kami bahwa logikanya adalah ini: jika jumlah pesanan dalam 3 tahun terakhir tidak kurang dari satu, maka ia menerima diskon 10% , tidak kurang dari tiga - 20%, dan tidak kurang dari lima - 30%. Dia gembira pergi - sekarang jelas bagaimana semuanya bekerja.
Saya pikir banyak yang telah membaca Bob Clean Code. Di sana ia berkata tentang aturan Pramuka: "Tempat parkir setelah kami pergi harus lebih bersih daripada sebelum kami tiba di sana." Oleh karena itu, mari kita refactor kode ini sehingga terlihat manusia dan sesuai dengan apa yang kita bicarakan sebelumnya tentang bahasa di mana-mana dan penggunaannya dalam kode.
Ini adalah kode yang sudah di refactored.
public class DiscountCalculator { private readonly IOrdersRepository _ordersRepository; public DiscountCalculator(IOrdersRepository ordersRepository) { _ordersRepository = ordersRepository; } public decimal CalculateDiscountBy(long customerId) { var completedOrdersCount = _ordersRepository.GetLast3YearsCompletedOrdersCountFor(customerId); return DiscountBy(completedOrdersCount); } private decimal DiscountBy(int completedOrdersCount) { if (completedOrdersCount >= 5) return 30; if (completedOrdersCount >= 3) return 20; if (completedOrdersCount >= 1) return 10; return 0; } }
Hal pertama yang kami lakukan adalah mentransfer perhitungan diskon ke DiscountCalculator yang terpisah, di mana metode CustomerId CalculateDiscountBy muncul. Semuanya dibaca secara manusiawi, semuanya jelas: apa, mengapa dan bagaimana. Di dalam metode ini, kita melihat bahwa kita secara global memiliki dua langkah untuk menghitung diskon. Pertama: kami mendapatkan sesuatu dari repositori pesanan, semuanya sesuai dengan kasus pengguna, Anda bahkan tidak harus masuk ke dalam jika ini bukan bagian yang menarik minat Anda sekarang. Faktanya adalah bahwa kita mendapatkan jumlah beberapa pesanan selesai, setelah itu kami segera mempertimbangkan diskon kedua untuk kuantitas ini sebagai langkah kedua.
Jika kita ingin melihat bagaimana itu dipertimbangkan, kita pergi ke DiscountBy, dan di sini hal yang hampir sama ditulis dalam bahasa Inggris yang hampir manusiawi bahwa "jenis brainfair" kita sebelumnya, semuanya jelas dan tepat.
Satu-satunya pertanyaan yang bisa muncul adalah dalam unit apa diskon diukur. Dimungkinkan untuk menambahkan kata "persen" dalam nama metode untuk memperjelasnya, tetapi dari konteks dan angka-angka yang terlibat, kemungkinan besar menebak bahwa ini adalah persentase, dan untuk singkatnya dapat dihilangkan. Jika kita ingin melihat jumlah pesanan yang ada di sana, maka kita akan pergi ke kode Repositori dan lihat. Sekarang kita tidak akan melakukan ini. Dalam Layanan kami, kami perlu menambahkan ketergantungan DiscountCalculator baru. Dan mari kita lihat apa yang kita dapatkan di versi kedua dari metode Checkout.
public void CheckoutV2(long orderId) { var order = _ordersRepository.GetOrder(orderId); var discount = _discountCalculator.CalculateDiscountBy(order.CustomerId); order.ApplyDiscount(discount); order.State = OrderState.AwaitingPayment; _ordersRepository.SaveOrder(order); }
Lihat, metode Checkout menerima orderId, lalu menerima orderId oleh orderId, menurut CustomerId dari pesanan ini, ia menganggap diskon menggunakan kalkulator diskon, menerapkan diskon pada pesanan, mengatur status ke Menunggu Pembayaran dan menyimpan pesanan. Kami memiliki skrip dalam bahasa Rusia di slide, tetapi di sini kami praktis membaca terjemahan skrip ini ke dalam bahasa Inggris dan semuanya jelas, semuanya jelas.
Apakah Anda melihat apa jimat itu? Kode ini dapat ditampilkan kepada siapa saja: bukan hanya programmer, tetapi QA, analis, pelanggan. Mereka semua akan mengerti apa yang terjadi, karena semuanya ditulis dalam bahasa manusia. Saya menggunakan ini di proyek kami, benar-benar QA dapat melihat beberapa bagian, periksa dengan Wiki dan mengerti bahwa ada beberapa jenis bug. Karena Wiki mengatakan demikian, dan kodenya sedikit berbeda, tetapi dia mengerti apa yang terjadi di sana, meskipun dia bukan pengembang. Dan dengan cara yang sama, kita dapat mendiskusikan kode dengan analis dan mendiskusikannya secara rinci. Saya berkata, "Lihat, ini cara kerjanya dalam kode." Pilihan terakhir kami bukanlah Wiki, melainkan kodenya. Semuanya berfungsi seperti yang tertulis dalam kode. Sangat penting untuk menggunakan bahasa di mana-mana saat menulis kode.

Ini adalah titik kunci ketiga.
Ada begitu banyak kebingungan tentang Desain Berbasis Domain dalam hal-hal seperti Domain, Subdomain, konteks Bounded, bagaimana mereka berhubungan dengan apa yang mereka maksud. Tampaknya semua orang membatasi sesuatu, semuanya entah bagaimana rapi. Tetapi tidak jelas lalu apa bedanya, mengapa mereka begitu berbeda ditemukan.

Domain adalah hal yang global, ini adalah bidang subjek global di mana bisnis khusus ini menghasilkan uang. Misalnya, untuk DotNext ini adalah konferensi, untuk Pyaterochka itu adalah penjualan barang secara eceran.
Perusahaan besar mungkin memiliki beberapa domain. Misalnya, Amazon terlibat dalam penjualan barang melalui Internet dan penyediaan layanan cloud, ini adalah area subjek yang berbeda.
Namun demikian, ini adalah sesuatu yang global dan tidak dapat diotomatisasi secara langsung, bahkan untuk menyelidiki hal itu sulit. Untuk analisis, Domain pasti dibagi menjadi Subdomain, yaitu, menjadi subdomain.

Subdomain adalah bagian dari bisnis yang, dalam bahasa kami, sangat terhubung, yaitu, mereka adalah semacam proses logis terisolasi yang berinteraksi satu sama lain pada tingkat utama.
Misalnya, jika kita mengambil toko online, itu akan menjadi formasi dan pemrosesan pesanan, itu akan menjadi pengiriman, ini bekerja dengan pemasok, ini adalah pemasaran, ini adalah akuntansi. Berikut adalah beberapa bagian ini - inilah yang dibagi menjadi bisnis.

Dari sudut pandang DDD, Subdomain dibagi menjadi tiga jenis. Dan di sini saya ingin mengatakan satu hal lagi: sering dalam buku dan artikel Subdomain hanya direduksi menjadi Domain, tetapi biasanya dalam kasus ketika dikombinasikan dengan jenis Subdomain. Artinya, ketika mereka mengatakan "Core domain", maksud mereka Core Subdomain, tolong jangan bingung dalam hal ini. Awalnya itu membuat saya terlena.
Subdomain dibagi menjadi tiga jenis.

Yang pertama dan paling penting adalah Core. Core adalah Subdomain utama, ini adalah keunggulan kompetitif perusahaan, apa yang membuat uang perusahaan ini, bagaimana ia berbeda dari para pesaingnya, pengetahuannya, apa pun sebutannya. Jika kita mengikuti konferensi DotNext, maka ini isinya. Anda semua datang ke sini untuk mencari konten, jika tidak ada konten seperti itu di sini, Anda tidak akan pergi atau pergi ke konferensi lain. Tidak akan ada DotNext dalam bentuknya.

Tipe kedua adalah Subdomain Pendukung. Ini juga merupakan hal penting untuk menghasilkan uang, itu juga sesuatu yang tanpanya mustahil, tetapi ini bukan semacam pengetahuan, keunggulan kompetitif yang nyata. Inilah yang didukung Core Subdomain. Dari sudut pandang penerapan Desain Berbasis Domain, ini berarti bahwa lebih sedikit upaya yang dihabiskan untuk Mendukung Subdomain, semua kekuatan utama dilemparkan ke Core.
Contoh untuk DotNext yang sama adalah pemasaran. Tidak mungkin tanpa pemasaran, jika tidak, tidak ada yang akan tahu tentang konferensi, tetapi tanpa pemasaran konten tidak diperlukan.

Dan akhirnya, Subdomain Generik. Generic adalah beberapa tugas bisnis yang khas, yang, sebagai suatu peraturan, dapat diotomatisasi dengan produk jadi atau outsourcing. Ini adalah apa yang juga dibutuhkan, tetapi tidak perlu memerlukan implementasi independen oleh kami, dan bahkan lebih dari itu, biasanya akan menjadi ide yang baik untuk menggunakan produk pihak ketiga.
Misalnya, menjual tiket. DotNext menjual tiket melalui TimePad. Subdomain ini sepenuhnya diotomatiskan oleh TimePad, dan Anda tidak perlu menulis TimePad kedua sendiri.

Dan akhirnya, dibatasi konteks. Konteks terikat dan Subdomain selalu berada di suatu tempat di dekatnya, tetapi ada perbedaan yang signifikan di antara mereka. Ini sangat penting.

Ada pertanyaan di StackExchange bagaimana konteks terikat berbeda dari Subdomain. Subdomain adalah bagian dari bisnis, bagian dari dunia nyata, itu adalah konsep ruang pernyataan masalah. Batas konteks membatasi model domain dan bahasa di mana-mana, yaitu, apa hasil pemodelan, dan karenanya, konteks terikat adalah konsep ruang solusi. Dalam proses implementasi proyek, semacam pemetaan Subdomain terjadi pada konteks terbatas.

Contoh klasik: pembukuan sebagai Subdomain, bagaimana proses dipetakan, otomatis, misalnya, Pembukuan 1C, Elba atau "Bisnisku" - entah bagaimana otomatis oleh beberapa produk. Ini adalah konteks terbatas dari akuntansi, di mana ada bahasa di mana-mana, terminologinya sendiri. Itulah perbedaan di antara mereka.

Jika kita kembali ke DotNext, maka, seperti yang saya katakan, tiket dipetakan ke TimePad, dan konten yang merupakan Subdomain Inti kami dipetakan ke aplikasi khusus yang kami kembangkan untuk manajemen konten.
Ukuran konteks yang dibatasi
Ada momen yang menimbulkan banyak pertanyaan. Bagaimana memilih ukuran yang tepat untuk konteks terbatas? Dalam buku-buku, orang dapat menemukan definisi seperti itu: "Konteks terikat harus persis sedemikian rupa sehingga bahasa di mana-mana lengkap, konsisten, tidak ambigu, tidak ambigu, konsisten." Definisi keren, dalam gaya ahli matematika dari lelucon terkenal: sangat akurat, tetapi tidak berguna.
Mari kita bahas bagaimana kita memahami semua yang sama: apakah itu harus Solusi, atau Proyek, atau namespace - skala mana yang harus dilampirkan ke konteks dibatasi?

Hal pertama yang dapat Anda baca hampir di mana-mana: idealnya,
satu Subdomain harus dipetakan ke satu konteks terbatas , yaitu diotomatisasi oleh satu konteks terbatas. Kedengarannya masuk akal, karena baik di sana maupun di sana ada batasan dari proses bisnis yang terpisah, dalam kedua kasus beberapa istilah bisnis, satu bahasa muncul. Tetapi di sini Anda perlu memahami bahwa ini adalah situasi yang ideal, Anda belum tentu memiliki ini, dan tidak perlu mencoba untuk mencapai ini.
Karena, di satu sisi, Subdomain bisa sangat besar, dan beberapa aplikasi atau layanan dapat diperoleh yang akan mengotomatiskannya, sehingga mungkin ternyata beberapa konteks yang dibatasi sesuai dengan satu Subdomain.
Tetapi ada situasi terbalik, sebagai suatu peraturan, ini tipikal untuk Legacy. Yaitu, ketika mereka membuat aplikasi besar, besar yang mengotomatiskan semua yang ada di dunia di perusahaan ini, maka yang terjadi adalah yang sebaliknya. Satu aplikasi adalah satu konteks terbatas, di sana model kemungkinan besar akan menjadi semacam ambigu, tetapi Subdomain belum hilang dari ini, masing-masing, satu konteks terikat akan sesuai dengan beberapa Subdomain.
Ketika arsitektur microservice menjadi modis, rekomendasi lain muncul (walaupun mereka tidak saling bertentangan):
satu konteks terbatas per microservice . Sekali lagi, ini terdengar masuk akal, orang benar-benar melakukannya. Karena microservice harus mengambil beberapa fungsi yang jelas, yang secara internal memiliki konektivitas tinggi, dan berkomunikasi dengan layanan lain melalui semacam interaksi. Jika Anda menggunakan arsitektur microservice, Anda dapat mengambil rekomendasi ini sendiri.
Tapi itu belum semuanya. Izinkan saya mengingatkan Anda sekali lagi bahwa Desain Berbasis Domain adalah tentang banyak hal: tentang bahasa, tentang orang-orang. Dan Anda tidak dapat mengabaikan orang dan hanya melakukan kriteria teknis dalam hal ini. Oleh karena itu, saya menulis ini:
satu konteks sama dengan X-man . Dulu saya berpikir bahwa x adalah sekitar 10, tetapi kami berbicara sedikit dengan Igor Labutin (
twitter.com/ilabutin ) dan pertanyaannya tetap terbuka.
Di sini penting untuk memahami ini: satu bahasa tetap bersatu sementara semua peserta berbicara, berdiskusi dan semua orang memahaminya dengan jelas. Jelas bahwa jumlah orang yang tidak terbatas tidak dapat berbicara bahasa yang sama. Sejarah umat manusia kita dengan jelas menunjukkan hal ini. Bagaimanapun, beberapa dialek muncul, beberapa artinya, sekarang Anda bahkan dapat menambahkan meme dan sebagainya. Dengan satu atau lain cara, bahasa akan kabur.
Oleh karena itu, harus dipahami bahwa jumlah orang yang menggunakan bahasa tunggal ini dan, karenanya, mengambil bagian dalam pengembangan, dalam otomatisasi, terbatas. Buku-buku juga berbicara tentang beberapa alasan politik: jika dua tim bekerja di bawah kepemimpinan manajer yang berbeda dan bekerja pada konteks yang sama, dan untuk beberapa alasan manajer ini tidak berteman satu sama lain, konflik akan dimulai dan fokus akan hilang. Oleh karena itu, akan jauh lebih sederhana dan lebih tepat untuk membuat dua konteks terikat untuk setiap perintah dan tidak mencoba untuk menggabungkan apa yang tidak digabungkan.
Arsitektur dan Manajemen Ketergantungan
Dari sudut pandang Desain Berbasis Domain, tidak terlalu masalah arsitektur apa yang Anda pilih. Desain Berbasis Domain bukan tentang itu; Desain Berbasis Domain adalah tentang bahasa dan komunikasi.

Tetapi ada satu poin penting, dari sudut pandang kriteria untuk memilih arsitektur yang menarik bagi kita dari perspektif Desain Domain-Driven:
tujuan kami adalah untuk secara maksimal menghilangkan logika bisnis dari ketergantungan pihak ketiga . Karena, segera setelah dependensi pihak ketiga muncul, terminologi muncul, kata-kata muncul yang tidak masuk ke dalam satu bahasa dan mulai mengotori logika bisnis kami.

Mari kita lihat contoh arsitektur klasik: arsitektur tiga lapis yang terkenal. Segera setelah mereka tidak memanggil lapisan domain (di sini lapisan Bisnis): bisnis, inti, dan domain semuanya sama. Bagaimanapun, ini adalah lapisan di mana logika bisnis berada, dan jika itu tergantung pada lapisan data, itu berarti bahwa beberapa konsep dari lapisan data entah bagaimana akan mengalir ke lapisan domain dan akan membuangnya.

Arsitektur empat-lapisan pada dasarnya sama, lapisan domain masih tergantung, dan karena tergantung, pihak ketiga, dependensi yang tidak perlu akan menuju ke sana.

Dan dalam pengertian ini, ada arsitektur yang memungkinkan ini untuk dihindari - itu adalah bawang-arsitektur ("bawang"). Perbedaannya adalah bahwa ia terdiri dari lapisan konsentris, dependensi pergi dari luar ke pusat. Yaitu, lapisan luar dapat bergantung pada bagian dalam mana pun, lapisan bagian dalam tidak dapat bergantung pada bagian luar.
Lapisan terluar adalah antarmuka pengguna dalam arti global (yaitu, itu belum tentu UI manusia, itu bisa menjadi REST API atau apa pun). Dan infrastruktur, yang sering secara umum juga terlihat seperti I / O, adalah database yang sama, pada kenyataannya, sebuah lapisan data. Semua hal ini berada di lapisan luar. Yaitu, karena itu aplikasi entah bagaimana menerima beberapa data, perintah, dan sebagainya, itu dikeluarkan, dan lapisan domain menghilangkan ketergantungan pada hal-hal ini.
Selanjutnya datang lapisan Aplikasi - tema yang agak holistik, tetapi ini adalah lapisan tempat skrip, kasus pengguna berada. Lapisan ini menggunakan lapisan domain untuk mengimplementasikan konsep-konsepnya.
Di tengah adalah lapisan domain. Seperti yang kita lihat, dia tidak lagi bergantung pada apa pun, dia menjadi sesuatu dalam dirinya sendiri. Dan itulah sebabnya lapisan domain sering disebut "Inti", karena itu adalah inti, itu adalah yang ada di tengah, yang tidak bergantung pada hal-hal pihak ketiga.

Salah satu opsi untuk mengimplementasikan arsitektur bawang seperti itu adalah arsitektur heksagonal, atau "port dan adaptor". Saya membawa gambar ini untuk intimidasi, saya tidak akan membicarakannya. Di akhir posting ada tautan ke satu dari sejuta artikel tentang arsitektur ini, Anda bisa membaca.
Sedikit tentang pola taktis: Antarmuka Terpisah
Seperti yang saya katakan, pertama, sebagian besar pola taktis sudah umum bagi semua orang, dan kedua, inti dari laporan saya adalah bahwa itu bukan intisari. Tapi saya suka pola Separated Interface secara terpisah, dan saya ingin membicarakannya secara terpisah.
Mari kita kembali ke kode microservice kami dan melihat apa yang terjadi dengan repositori.

Lapisan domain memiliki antarmuka
repositori IOrdersRepository.cs dan
implementasinya, OrdersRepository.cs.
using System.Linq; namespace DotNext.Sales.Core { public interface IOrdersRepository { Order GetOrder(long id); void SaveOrder(Order order); IQueryable<Order> GetOrders(); #region V2 int GetLast3YearsCompletedOrdersCountFor(long customerId); #endregion } }
Di sini kami telah menambahkan di sini metode tertentu untuk menerima pesanan selama tiga tahun terakhir GetLast3YearsCompletedOrdersCountFor.
Dan mereka menerapkannya dalam beberapa bentuk (dalam hal ini, melalui Kerangka Entitas, tetapi bisa berupa apa saja):
public int GetLast3YearsCompletedOrdersCountFor(long customerId) { var threeYearsAgo = DateTime.UtcNow.AddYears(-3); return _dbContext.Orders .Count(o => o.CustomerId == customerId && o.State == OrderState.Completed && o.OrderDate >= threeYearsAgo); }
Lihat apa masalahnya. Repositori berakhir di lapisan domain, implementasinya di lapisan domain, tetapi kodenya, dimulai dengan DateTime.UtcNow.AddYears (-3), secara inheren bukan milik lapisan domain, dan bukan logika bisnis. Ya, LINQ membuatnya lebih atau kurang manusiawi, tetapi jika, misalnya, ada pertanyaan SQL di sini, semuanya akan benar-benar menyedihkan.
Arti dari pola Interface Terpisah adalah bahwa antarmuka layanan yang kami gunakan dalam logika domain dinyatakan dalam lapisan domain. Kita berbicara tentang repositori dan layanan serupa di mana rincian implementasi layanan ini bukan logika bisnis. Logika bisnis adalah fakta keberadaan layanan ini dan fakta panggilan dan penggunaannya di lapisan domain. Oleh karena itu, antarmuka repositori tetap berada di lapisan domain, dan implementasinya pindah ke lapisan infrastruktur.
Saya menyiapkan opsi lain. Antarmuka repositori tetap dalam perakitan Inti, tetapi implementasinya pindah ke Infrastruktur.EF.
Jadi, kami membawa konsep-konsep yang tidak khas ke lapisan domain ke infrastruktur. Sebagai efek samping, kita dapat mengganti infrastruktur ini dengan beberapa implementasi lainnya. Tapi ini bukan tujuan utama, tujuan utama adalah, seperti yang saya katakan, untuk menghilangkan logika domain dari dependensi pihak ketiga.
Sekali lagi tentang bahasa
Mari kita bicara lagi, dan lagi, dan lagi tentang bahasanya.
Pada awalnya, kami membangun model domain "speaker - talk - event". Saya pikir tidak ada yang mengajukan pertanyaan khusus.
Dan inilah skenario atas dasar yang kami bangun model domain ini:

Lihat, skrip dalam bahasa Rusia, dan model domain dalam bahasa Inggris.
Untuk pengembang yang tidak berbahasa Inggris, ini adalah sesuatu yang harus Anda jalani terus-menerus.

Anda masing-masing, kemungkinan besar, terus-menerus melakukan proses ini: menerjemahkan dari bahasa Rusia ke bahasa Inggris dan sebaliknya. Mereka yang bekerja dengan pelanggan dan proyek yang berbahasa Inggris sedikit lebih mudah, karena persyaratannya dalam bahasa Inggris, diskusi dengan pelanggan dalam bahasa Inggris, sebagai aturan, semua skenario dalam bahasa Inggris, kode dalam bahasa Inggris, dan hanya ada komunikasi dalam tim di Rusia, yang dengan cepat tumbuh dalam bahasa Inggris (klien - pelanggan, pesanan - pesanan). Dan beban kognitif itu, overhead itu, yang diciptakan oleh terjemahan yang konstan, sedikit surut.
, , , . , .
1, . , — , , .

1. PascalCase , , ,
, , , , - - .
, - ?

, use case, , .
, . C#, , , . , , , .
, , Domain-Driven Design. , , , , C# . .
, - Continuous Integration. , , , - - . , - , , . , 95% , , Continuous Integration, , TeamCity . .
, . . , 1-, , . «» , . , , .

, Domain-Driven Design.
— , . Domain-Driven Design — , . — , , ubiquitous language. , , . , , , .
. , . - , , , , , , , , , , — . .
. . . . , , . DSL-. .NET-, - , , , , . , .
, - . , , ubiquitous language -. .
- marshinov . , DDD: , « DDD, CQRS Event Sourcing » . , , .
- , AutoMapper MediatR. , Domain-Driven Design, .
- «F# for fun and profit» , , F# Domain-Driven Design. , , , .
- , , .
, ,
GitHub .
DotNext:
, « , ». DotNext ( 15-16 ) . , 1 , .