Temukan Root Agregat DDD

10

Ayo mainkan game favorit semua orang, temukan Aggregrate Root. Mari kita gunakan domain masalah Pelanggan / Pesanan / OrderLines / Produk kanonik. Secara tradisional, Pelanggan, pesanan, dan produk adalah AR dengan OrderLines menjadi entitas di bawah Pesanan. Logika di balik ini adalah bahwa Anda perlu mengidentifikasi pelanggan, pesanan, dan produk, tetapi OrderLine tidak akan ada tanpa Pesanan. Jadi, dalam domain masalah kami, kami memiliki aturan bisnis yang mengatakan bahwa Pelanggan hanya dapat memiliki satu pesanan yang tidak terkirim pada satu waktu.

Apakah itu memindahkan pesanan di bawah akar agregat pelanggan? Saya kira begitu. Tetapi dengan melakukan itu, itu membuat AR Pelanggan agak besar dan tunduk pada masalah konkurensi nanti.

Atau, bagaimana jika kita memiliki aturan bisnis yang menyatakan bahwa pelanggan hanya dapat memesan produk tertentu sekali seumur hidup. Ini adalah lebih banyak bukti yang mengharuskan Pelanggan memiliki Pesanan.

Tetapi ketika menyangkut pengiriman, mereka melakukan semua tindakan mereka pada Order, bukan pada pelanggan. Agak bodoh harus memuat seluruh pelanggan untuk menandai pesanan individu yang dikirimkan.

Inilah yang saya usulkan:

class Customer
{
    public Guid Id {get;set;}
    public string Name { get; set; }
    public Address Address { get; set; }
    public IEnumerable<Order> Orders { get; set; }
    public void PlaceOrder(ThingsInTheOrder thingsInTheOrder)
    {
        // Make sure there aren't any pending orders already.
        // Make sure they aren't ordering a Widget if they've already ordered a Widget in the past.
        // Create an Order object and add it to the collection.  Raise a domain event to trigger emails and other stuff
    }
}

class Order
{
    public Guid Id { get; set; }
    public IEnumerable<OrderLine> OrderLines { get; set; }
    public ShippingData {get;set;}
    public void Ship(ShippedByPerson shippedByPerson, string trackingCode)
    {
         // Create a new ShippingData object and assign it from the data passed in.  
         // Publish a domain event
    }
}

Kekhawatiran terbesar saya adalah masalah konkurensi dan fakta bahwa Ordo itu sendiri memiliki karakteristik akar agregat.

Darthg8r
sumber

Jawaban:

12

Versi pendek

Alasan untuk DDD adalah bahwa Objek Domain adalah abstraksi yang harus memenuhi persyaratan domain fungsional Anda - jika Objek Domain tidak dapat dengan mudah memenuhi persyaratan tersebut, itu menunjukkan Anda mungkin menggunakan abstraksi yang salah.

Memberi Nama Objek Domain menggunakan Entity Nouns dapat menyebabkan objek-objek tersebut menjadi sangat erat satu sama lain dan menjadi objek "dewa" yang membengkak, dan mereka dapat memunculkan masalah seperti yang ada di pertanyaan ini seperti "Di mana tempat yang tepat untuk meletakkan Metode CreateOrder? "

Untuk membuatnya lebih mudah untuk mengidentifikasi Root Agregat 'kanan', pertimbangkan pendekatan berbeda di mana Objek Domain didasarkan pada persyaratan bisnis tingkat tinggi fungsional - yaitu memilih kata benda yang merujuk pada persyaratan fungsional dan / atau perilaku yang perlu dilakukan oleh pengguna sistem. melakukan.


Versi Panjang

DDD adalah pendekatan untuk Desain OO yang dimaksudkan untuk menghasilkan grafik Objek Domain di Lapisan Bisnis sistem Anda - Objek domain bertanggung jawab untuk memenuhi persyaratan Bisnis Tingkat Tinggi Anda, dan idealnya harus dapat mengandalkan Lapisan Data untuk hal-hal seperti kinerja dan integritas dari penyimpanan data persisten yang mendasarinya.

Cara lain untuk melihatnya adalah poin-poin penting dalam daftar ini

  • Entity Nouns biasanya menyarankan atribut data.
  • Domain Nouns harus menyarankan perilaku
  • Pemodelan DDD dan OO berkaitan dengan abstraksi berdasarkan persyaratan fungsional dan domain inti / logika bisnis.
  • Lapisan Logika Bisnis bertanggung jawab untuk memenuhi persyaratan domain tingkat tinggi

Salah satu kesalahpahaman umum tentang DDD adalah bahwa Objek Domain harus didasarkan pada beberapa "benda" fisik dunia nyata (yaitu beberapa kata benda yang dapat Anda tunjukkan di dunia nyata, dikaitkan dengan semua jenis data / properti), namun datanya / atribut dari hal-hal dunia nyata itu tidak selalu menjadi titik awal yang baik ketika mencoba untuk memenuhi persyaratan fungsional.

Tentu saja, Logika Bisnis harus menggunakan data ini, tetapi Objek Domain itu sendiri pada akhirnya harus berupa abstraksi yang mewakili persyaratan dan perilaku Domain fungsional.

Sebagai contoh; kata benda seperti Orderatau Customertidak menyiratkan perilaku apa pun, dan oleh karena itu umumnya abstraksi yang tidak membantu untuk merepresentasikan logika bisnis dan Objek Domain.

Saat mencari jenis abstraksi yang mungkin berguna untuk mewakili Logika Bisnis, pertimbangkan persyaratan umum yang mungkin Anda harapkan dipenuhi oleh sistem:

  • Sebagai Tenaga Penjual, saya ingin Membuat Pesanan untuk Pelanggan Baru sehingga saya dapat menghasilkan faktur untuk Produk yang akan dijual dengan Harga dan Kuantitasnya.
  • Sebagai Penasihat Layanan Pelanggan, saya ingin Membatalkan Pesanan yang Tertunda sehingga Pesanan tidak dipenuhi oleh Operator Gudang.
  • Sebagai Penasihat Layanan Pelanggan, saya ingin Mengembalikan Baris Pesanan sehingga Produk dapat disesuaikan ke dalam Inventaris dan Pembayaran akan Dikembalikan melalui metode Pembayaran asli Pelanggan.
  • Sebagai Operator Gudang, saya ingin melihat semua Produk dengan Pending Order dan informasi Pengiriman sehingga saya dapat memilih produk dan mengirimkannya melalui Kurir.
  • dll.

Pemodelan Persyaratan Domain dengan Pendekatan DDD

Berdasarkan daftar di atas, pertimbangkan beberapa Objek Domain potensial untuk sistem Pesanan seperti itu:

SalesOrderCheckout
PendingOrdersStream
WarehouseOrderDespatcher
OrderRefundProcessor 

Sebagai objek domain, ini mewakili abstraksi yang mengambil kepemilikan berbagai persyaratan domain perilaku; memang kata benda mereka memberi petunjuk kuat pada persyaratan fungsional spesifik yang mereka penuhi.

(Mungkin ada infrastruktur tambahan di sana juga seperti EventMediatoruntuk menyampaikan pemberitahuan bagi pengamat yang ingin tahu kapan pesanan baru telah dibuat, atau kapan pesanan telah dikirim, dll).

Misalnya, SalesOrderCheckoutmungkin perlu menangani data tentang Pelanggan, Pengiriman dan Produk, namun tidak peduli dengan apa pun yang berkaitan dengan perilaku pesanan pengiriman, menyortir pesanan yang tertunda, atau mengeluarkan pengembalian uang.

Untuk SalesOrderCheckoutmemenuhi persyaratan domainnya termasuk memberlakukan aturan-aturan bisnis tersebut seperti mencegah pelanggan memesan terlalu banyak barang, mungkin menjalankan beberapa validasi, dan mungkin meningkatkan pemberitahuan untuk bagian lain dari sistem - itu dapat melakukan semua hal itu tanpa harus bergantung pada apa pun dari benda-benda lainnya.

DDD menggunakan Entity Nouns untuk mewakili Objek Domain

Ada sejumlah bahaya potensial ketika merawat kata benda sederhana seperti Order, Customerdan Productsebagai Objek Domain; di antara masalah-masalah itu adalah yang Anda singgung dalam pertanyaan:

  • Jika suatu metode menangani suatu Order, a Customerdan a Product, objek Domain mana yang dimilikinya?
  • Di mana Root Agregat untuk 3 Obyek itu?

Jika Anda memilih Entity Nouns untuk mewakili Objek Domain, sejumlah hal mungkin terjadi:

  • Order, Customerdan Productberisiko tumbuh menjadi benda "dewa"
  • Risiko berakhir dengan satu objek Managerdewa untuk mengikat semuanya.
  • Objek-objek itu berisiko saling terkait erat satu sama lain - mungkin sulit untuk memenuhi persyaratan domain tanpa melewati this(atau self)
  • Risiko mengembangkan abstraksi "bocor" - yaitu objek domain yang diharapkan untuk mengekspos puluhan get/ setmetode yang melemahkan enkapsulasi (atau, jika Anda tidak melakukannya, maka beberapa programmer lain mungkin akan nanti ..).
  • Risiko Objek Domain menjadi membengkak dengan campuran data bisnis yang kompleks (misalnya input data pengguna melalui UI) dan status sementara (misalnya 'riwayat' tindakan pengguna saat pesanan telah dimodifikasi).

DDD, Desain OO dan Model Biasa

Kesalahpahaman umum tentang DDD dan OO Design adalah bahwa model "polos" entah bagaimana 'buruk' atau 'anti-pola'. Martin Fowler menulis sebuah artikel yang menggambarkan Model Domain Anemik - tetapi ketika ia menjelaskan dalam artikel itu, DDD sendiri tidak boleh 'bertentangan' dengan pendekatan pemisahan bersih antara lapisan

"Perlu juga ditekankan bahwa menempatkan perilaku ke objek domain tidak boleh bertentangan dengan pendekatan solid menggunakan layering untuk memisahkan logika domain dari hal-hal seperti ketekunan dan tanggung jawab presentasi. Logika yang harus ada dalam objek domain adalah logika domain - validasi domain, perhitungan , aturan bisnis - apa pun yang Anda suka menyebutnya. "

Dengan kata lain, menggunakan Model biasa untuk menyimpan data bisnis yang ditransfer antara lapisan lain (misalnya model Pesanan yang dilewatkan oleh aplikasi pengguna saat pengguna ingin membuat pesanan baru) bukan hal yang sama dengan "Model Domain Anemik". model data 'biasa' seringkali merupakan cara terbaik untuk melacak data dan mentransfer data antar lapisan (seperti layanan web REST, toko persistensi, Aplikasi atau UI, dll).

Logika bisnis dapat memproses data dalam model-model tersebut dan dapat melacaknya sebagai bagian dari kondisi bisnis - tetapi tidak serta merta mengambil kepemilikan dari model-model tersebut.

Root Agregat

Melihat kembali pada contoh Domain Objects - SalesOrderCheckout, PendingOrdersStream, WarehouseOrderDespatcher, OrderRefundProcessormasih ada ada jelas Agregat Akar; tapi itu sebenarnya tidak masalah karena Objek Domain ini memiliki tanggung jawab yang sangat terpisah yang tampaknya tidak tumpang tindih.

Secara fungsional, tidak perlu bagi SalesOrderCheckoutuntuk berbicara dengan PendingOrdersStreamkarena pekerjaan mantan selesai ketika telah menambahkan pesanan baru ke Database; di sisi lain, PendingOrdersStreamdapat mengambil pesanan baru dari Database. Objek-objek ini sebenarnya tidak perlu berinteraksi satu sama lain secara langsung (Mungkin Mediator Peristiwa mungkin memberikan notifikasi di antara keduanya, tapi saya berharap setiap sambungan antara objek-objek ini menjadi sangat longgar)

Mungkin Agregat Root akan menjadi Kontainer IoC yang menyuntikkan satu atau lebih dari Objek Domain ke Pengendali UI, juga menyediakan infrastruktur lain seperti EventMediatordan Repository. Atau mungkin itu akan menjadi semacam Layanan Orkestra ringan yang berada di atas Lapisan Bisnis.

Akar Agregat tidak selalu perlu menjadi Object Domain. Demi menjaga Pemisahan Kekhawatiran antara objek Domain, umumnya merupakan hal yang baik ketika akar agregat adalah objek terpisah tanpa logika bisnis.

Ben Cottrell
sumber
3
Saya menurunkan suara karena jawaban Anda mengonfigurasi konsep dari Entity Framework yang merupakan teknologi khusus Microsoft dengan Desain Domain Driven, yang berasal dari buku yang ditulis oleh Eric Evans dengan nama yang sama. Anda memiliki beberapa pernyataan dalam jawaban Anda yang bertentangan langsung dengan buku DDD dan pertanyaan ini tidak menyebutkan Kerangka Entitas tetapi secara spesifik ditandai dengan DDD. Ada juga tidak menyebutkan kegigihan sama sekali dalam pertanyaan jadi saya tidak melihat di mana tabel database relevan.
RibaldEddie
@RibaldEddie Terima kasih telah meluangkan waktu untuk meninjau jawaban dan komentar, saya setuju bahwa penyebutan data persisten tidak benar-benar perlu dalam jawaban jadi saya telah memesannya ulang untuk menghapusnya. Fokus utama dari jawaban tersebut dapat diringkas sebagai "Entity Nouns sering kali tidak terlalu baik nama kelas Domain Object karena kecenderungannya untuk menjadi objek dewa kembung yang digabungkan dengan erat", semoga pesan dan alasan WRT persyaratan / perilaku fungsional lebih jelas sekarang. ?
Ben Cottrell
Buku DDD tidak memiliki konsep IIRC itu. Ini memiliki Entitas, yang hanya kelas yang ketika instantiated memiliki identitas yang gigih dan unik sehingga dua contoh yang terpisah menyiratkan dua hal yang unik dan bertahan, yang kontras dengan Obyek Nilai yang tidak memiliki identitas dan tidak bertahan lama. . Dalam buku, objek Entitas dan Nilai adalah objek domain.
RibaldEddie
11

Apa kriteria untuk mendefinisikan agregat?

Mari kita kembali ke dasar-dasar buku biru besar:

Agregat: Sekelompok objek terkait yang diperlakukan sebagai unit untuk tujuan perubahan data . Referensi eksternal dibatasi untuk satu anggota AGREGAT, yang ditunjuk sebagai root. Serangkaian aturan konsistensi berlaku dalam batas-batas AGREGAT.

Tujuannya adalah untuk mempertahankan invarian. Tapi itu juga untuk mengelola identitas lokal yang benar, yaitu mengidentifikasi objek yang tidak memiliki makna sendiri.

Orderdan secara Order linedefinitif menjadi bagian dari gugus seperti itu. Sebagai contoh:

  • Hapus suatu Order, akan membutuhkan penghapusan semua lini.
  • Menghapus sebuah baris mungkin memerlukan penomoran ulang dari baris berikut
  • Menambahkan baris baru akan diperlukan untuk menentukan garis nulber berdasarkan semua baris lain dari urutan yang sama.
  • Mengubah beberapa informasi pesanan, seperti misalnya mata uang, dapat memengaruhi makna harga dalam item baris (atau perlu menghitung ulang harga).

Jadi di sini agregat penuh diperlukan untuk memastikan aturan konsistensi dan invarian.

Kapan harus berhenti?

Sekarang, Anda menjabarkan beberapa aturan bisnis, dan berpendapat bahwa untuk memastikannya, Anda harus mempertimbangkan pelanggan sebagai bagian dari agregat:

Kami memiliki aturan bisnis yang mengatakan bahwa Pelanggan hanya dapat memiliki satu pesanan yang tidak terkirim pada satu waktu.

Tentu saja, mengapa tidak. Mari kita lihat implikasinya: pesanan akan selalu diakses melalui pelanggan. Apakah ini kehidupan nyata ? Ketika pekerja mengisi kotak untuk mengirimkan pesanan, apakah mereka perlu membaca kode batang pelanggan dan barcode pesanan untuk mengakses pesanan? Faktanya, secara umum, identitas suatu Order bersifat global, bukan lokal bagi pelanggan, dan independensi relatif ini menyarankan agar ia berada di luar agregat.

Selain itu, aturan bisnis ini lebih terlihat sebagai kebijakan: itu adalah keputusan sewenang-wenang perusahaan untuk menjalankan proses mereka dengan aturan ini. Jika aturan tidak dihormati, bos mungkin tidak senang, tetapi datanya tidak benar-benar tidak konsisten. Selain itu, dalam semalam "per pelanggan satu pesanan yang tidak terkirim pada satu waktu" dapat menjadi "sepuluh pesanan yang tidak terkirim per pelanggan" atau bahkan "secara terpisah dari pelanggan, ratusan pesanan yang tidak terkirim per gudang", sehingga agregat mungkin tidak lagi dibenarkan.

Christophe
sumber
1

dalam domain masalah kami, kami memiliki aturan bisnis yang mengatakan bahwa Pelanggan hanya dapat memiliki satu pesanan yang tidak terkirim pada satu waktu.

Sebelum Anda masuk terlalu jauh ke dalam lubang kelinci itu, Anda harus meninjau kembali diskusi Greg Young tentang konsistensi set , dan khususnya:

Apa dampak bisnis dari kegagalan?

Karena dalam banyak kasus, jawaban yang tepat bukanlah mencoba mencegah hal yang salah terjadi, tetapi menghasilkan laporan pengecualian ketika mungkin ada masalah.

Tetapi, dengan anggapan bahwa banyak pesanan tidak terkirim adalah tanggung jawab yang signifikan bagi bisnis Anda ....

Ya, jika Anda ingin memastikan bahwa hanya ada satu pesanan yang tidak terkirim, maka harus ada agregat yang dapat melihat semua pesanan untuk pelanggan.

Agregat itu belum tentu merupakan agregat pelanggan .

Itu mungkin sesuatu seperti antrian pesanan, atau riwayat pesanan, di mana semua pesanan untuk pelanggan tertentu masuk ke antrian yang sama. Dari apa yang Anda katakan, itu tidak memerlukan semua data profil pelanggan, sehingga tidak boleh menjadi bagian dari agregat ini.

Tetapi ketika menyangkut pengiriman, mereka melakukan semua tindakan mereka pada Order, bukan pada pelanggan.

Ya, saat Anda benar-benar bekerja dengan lembar pemenuhan dan tarik, tampilan riwayat tidak terlalu relevan.

Tampilan histori, untuk menegakkan invarian Anda, hanya memerlukan id pesanan dan status pemrosesan saat ini. Itu tidak harus menjadi bagian dari agregat yang sama dengan urutan - ingat, batas agregat adalah tentang mengelola perubahan, bukan menyusun pandangan.

Jadi bisa jadi Anda menangani pesanan sebagai agregat, dan riwayat pesanan sebagai agregat terpisah, dan mengoordinasikan aktivitas di antara keduanya.

VoiceOfUnreason
sumber
1

Anda telah membuat contoh orang jerami. Ini terlalu sederhana dan saya ragu itu mencerminkan sistem dunia nyata. Saya tidak akan memodelkan Entitas tersebut dan perilaku terkaitnya dengan cara yang Anda tentukan karena itu.

Kelas Anda perlu memodelkan keadaan pesanan dengan cara yang tercermin dalam beberapa agregat. Misalnya ketika pelanggan menempatkan sistem ke dalam keadaan di mana permintaan pesanan pelanggan perlu diproses, saya mungkin membuat agregat objek entitas domain yang disebut CustomerOrderRequestatau PendingCustomerOrderatau bahkan adil CustomerOrder, atau bahasa apa pun yang digunakan bisnis, dan itu bisa menyimpan pointer ke keduanya Pelanggan dan OrderLines dan kemudian memiliki metode seperti canCustomerCompleteOrder()yang dipanggil dari lapisan layanan.

Objek domain ini akan berisi logika bisnis untuk menentukan apakah pesanan itu valid atau tidak.

Jika pesanan itu valid dan diproses maka saya akan memiliki beberapa cara untuk mengalihkan objek ini ke objek lain yang mewakili pesanan yang diproses.

Saya pikir masalah dengan pemahaman Anda adalah bahwa Anda menggunakan contoh agregat yang terlalu disederhanakan. A PendingOrderdapat berupa agregat sendiri yang terpisah dari UndeliveredOrderdan lagi-lagi terpisah dari DeliveredOrderatau CancelledOrderatau apa pun.

RibaldEddie
sumber
Meskipun usaha Anda dalam bahasa yang netral gender itu lucu, saya perhatikan bahwa perempuan tidak pernah berdiri di ladang untuk menakuti burung gagak.
Robert Harvey
@RobertHarvey itu hal yang aneh untuk fokus pada posting saya. Orang-orangan sawah dan patung keduanya secara teratur telah muncul dalam bentuk perempuan sepanjang sejarah.
RibaldEddie
Anda tidak akan membuat perbedaan dalam posting Anda jika Anda tidak menganggapnya penting. Sebagai masalah linguistik, istilahnya adalah "manusia jerami"; keraguan apa pun tentang seksisme hampir pasti dikalahkan oleh faktor "apa yang dia bicarakan" yang diciptakan dengan menciptakan istilah Anda sendiri.
Robert Harvey
5
@RobertHarvey jika ada yang tahu apa arti pria jerami, saya yakin mereka bisa mencari tahu apa arti orang jerami jika mereka belum mendengar istilah itu. Bisakah kita fokus pada substansi posting saya, tolong tulis perangkat lunak?
RibaldEddie
1

Vaughn Vernon menyebutkan ini dalam bukunya "Implementing Domain-Driven Design" di awal Bab 7 (Layanan):

"Seringkali indikasi terbaik bahwa Anda harus membuat Layanan dalam model domain adalah ketika operasi yang Anda perlu lakukan terasa tidak pada tempatnya sebagai metode pada Agregat atau Objek Nilai".

Jadi, dalam hal ini mungkin ada layanan domain yang disebut "CreateOrderService" yang mengambil contoh Pelanggan dan daftar item untuk pesanan.

class CreateOrderService
{
    public Order CreateOrder(Customer customer, ThingsInTheOrder thingsInTheOrder)
    {
        // Get all the orders for the customer
        // Check if any of the things to be ordered exist in previous orders   
        // If none have been previously ordered, create the order and return it
        // Otherwise return null 
    }
}
claudius
sumber
1
Bisakah Anda jelaskan lebih lanjut bagaimana layanan domain dapat membantu mengatasi masalah konkurensi dalam pertanyaan?
ivenxu