Domain-Driven-Design - dependensi eksternal dalam masalah Entity

23

Saya ingin memulai Domain-Driven-Design, tetapi ada beberapa masalah yang ingin saya selesaikan sebelum memulai :)

Mari kita bayangkan saya memiliki Grup dan Pengguna dan ketika pengguna ingin bergabung dengan grup, saya memanggil groupsService.AddUserToGroup(group, user)metode. Dalam DDD yang harus saya lakukan group.JoinUser(user), yang terlihat cukup bagus.

Masalahnya muncul jika saya ada beberapa aturan validasi untuk menambahkan pengguna, atau beberapa tugas eksternal harus dimulai ketika pengguna ditambahkan ke grup. Memiliki tugas-tugas ini akan menyebabkan entitas memiliki dependensi eksternal.

Contohnya bisa berupa - batasan bahwa pengguna hanya dapat berpartisipasi dalam 3 grup, maks. Ini akan membutuhkan panggilan-DB dari dalam group.JoinUser metode untuk memvalidasi ini.

Tetapi kenyataan bahwa Entitas bergantung pada beberapa layanan / kelas eksternal tampaknya tidak begitu baik dan "alami" bagi saya.

Apa cara yang tepat untuk menangani ini dalam DDD?

Shaddix
sumber

Jawaban:

15

Mari kita bayangkan saya memiliki Grup dan Pengguna dan ketika pengguna ingin bergabung dengan grup, saya memanggil metode groupsService.AddUserToGroup (grup, pengguna). Dalam DDD saya harus melakukan group.JoinUser (pengguna), yang terlihat cukup bagus.

Tetapi DDD juga mendorong Anda untuk menggunakan layanan (stateless) untuk melakukan tugas, jika tugas yang dihadapi terlalu rumit atau tidak akan cocok dengan model entitas. Tidak masalah memiliki layanan di lapisan domain. Tetapi layanan di lapisan domain hanya boleh menyertakan logika bisnis. Tugas eksternal dan logika aplikasi (seperti mengirim email) di sisi lain, harus menggunakan layanan domain di lapisan aplikasi, di mana Anda dapat memiliki layanan (aplikasi-) terpisah yang membungkusnya misalnya.

Masalahnya muncul jika saya ada beberapa aturan validasi untuk menambahkan pengguna ...

Aturan validasi memang milik model domain! Mereka harus dienkapsulasi di dalam objek domain (entitas dll).

... atau beberapa tugas eksternal harus dimulai ketika pengguna ditambahkan ke grup. Memiliki tugas-tugas ini akan menyebabkan entitas memiliki dependensi eksternal.

Meskipun saya tidak tahu apa jenis tugas eksternal yang Anda bicarakan, saya menganggap itu sesuatu seperti mengirim email dll. Tapi ini bukan bagian dari model domain Anda. Itu harus hidup di lapisan aplikasi dan di-hanlded di sana. Anda dapat memiliki layanan di lapisan aplikasi Anda yang beroperasi pada layanan domain dan entitas untuk melakukan tugas-tugas tersebut.

Tetapi kenyataan bahwa Entitas bergantung pada beberapa layanan / kelas eksternal tampaknya tidak begitu baik dan "alami" bagi saya.

Itu tidak wajar dan tidak seharusnya terjadi. Entitas tidak boleh tahu tentang hal-hal yang bukan tanggung jawabnya. Layanan harus digunakan untuk mengatur interaksi entitas.

Apa cara yang tepat untuk menangani ini dalam DDD?

Dalam kasus Anda, hubungan mungkin harus dua arah. Apakah pengguna bergabung dengan grup atau grup mengambil pengguna tergantung pada domain Anda. Apakah pengguna bergabung dengan grup? Atau apakah pengguna ditambahkan ke grup? Bagaimana cara kerjanya di domain Anda?

Bagaimanapun, Anda memiliki hubungan dua arah dan dengan demikian dapat menentukan jumlah grup yang sudah dimiliki pengguna di dalam agregat pengguna. Apakah Anda meneruskan pengguna ke grup atau grup ke pengguna secara teknis sepele setelah Anda menentukan kelas yang bertanggung jawab.

Validasi kemudian harus dilakukan oleh entitas. Semuanya dipanggil dari layanan lapisan aplikasi yang juga dapat melakukan hal-hal teknis, seperti mengirim email dll.

Namun, jika logika validasi benar-benar rumit, layanan domain mungkin merupakan solusi yang lebih baik. Dalam hal itu, enkapsulasi aturan bisnis di sana dan kemudian panggil dari lapisan aplikasi Anda.

Elang
sumber
Tetapi jika kita memindahkan begitu banyak logika di luar entitas, apa yang harus disimpan di dalam?
SiberianGuy
Tanggung jawab langsung entitas! Jika Anda dapat mengatakan "pengguna dapat bergabung dengan grup" misalnya, maka itu adalah tanggung jawab entitas pengguna. Terkadang Anda harus membuat keputusan tradeoff karena alasan teknis. Saya juga bukan penggemar berat hubungan dua arah, tetapi kadang-kadang cocok dengan model. Jadi dengarkan baik-baik ketika berbicara tentang domain. "Entitas memang ..." "Entitas itu bisa ..." Ketika Anda mendengar kalimat seperti itu, maka operasi-operasi itu kemungkinan besar milik entitas itu.
Falcon
Selanjutnya, Anda tahu bahwa Anda memerlukan layanan ketika dua atau lebih objek yang tidak terkait harus berpartisipasi dalam tugas untuk menyelesaikan sesuatu.
Falcon
1
Terima kasih atas jawaban Anda, Falcon! Btw, saya selalu mencoba menggunakan layanan stateless, jadi saya selangkah lebih dekat ke DDD :) Katakanlah dalam domain, operasi UserJoinsToGroup ini milik Grup. Masalahnya adalah, untuk memvalidasi operasi itu, saya perlu tahu dalam berapa banyak grup yang telah berpartisipasi oleh Pengguna (untuk menolak operasi jika sudah> 3). Untuk mengetahui bahwa saya perlu menanyakan database. Bagaimana saya bisa melakukan itu dari entitas Grup? Saya punya beberapa contoh lagi, ketika saya perlu menyentuh DB dalam operasi yang seharusnya secara alami milik entitas (saya akan mempostingnya jika diperlukan :))
Shaddix
2
Nah, jika saya memikirkannya: Bagaimana dengan entitas GroupMembership? Itu dapat dibangun oleh pabrik dan pabrik ini dapat mengakses respositories. Itu akan menjadi DDD yang bagus dan merangkum pembuatan keanggotaan. Pabrik dapat mengakses Gudang, membuat Keanggotaan dan kemudian menambahkannya ke masing-masing pengguna dan grup. Entitas baru ini juga dapat merangkum hak istimewa. Mungkin itu ide yang bagus.
Falcon
3

Cara saya mendekati masalah validasi adalah dengan cara ini: Membuat Layanan Domain bernama MembershipService:

class MembershipService : IMembershipService
{
   public MembershipService(IGroupRepository groupRepository)
   { 
     _groupRepository = groupRepository;
   }
   public int NumberOfGroupsAssignedTo(UserId userId)
   {
        return _groupsRepository.NumberOfGroupsAssignedTo(userId);
   }
}

Entitas Grup perlu disuntik IMemberShipService. Ini dapat dilakukan di tingkat kelas atau tingkat metode. Mari kita asumsikan kita melakukannya di level metode.

class Group{

   public void JoinUser(User user, IMembershipService membershipService)
   {
       if(membershipService.NumberOfGroupsAssignedTo(user.UserId) >= 3)
         throw new BusinessException("User assigned to more than 3 groups. Cannot proceed");

       // do some more stuff
   }
}

Layanan Aplikasi: GroupServicedapat disuntikkan dengan IMemberShipServicemenggunakan injeksi Konstruktor, yang kemudian dapat diteruskan ke JoinUsermetode Groupkelas.

Eklavya Gupta
sumber
1
Anda mungkin ingin mempertimbangkan memformat kode sumber di pos Anda agar mudah dibaca
Benni