DDD: Membuat modul yang dapat digunakan kembali dan perbedaan jenis layanan (Domain, Infrastruktur, Aplikasi)

8

Jadi setelah membaca "Menerapkan Desain Berbasis Domain oleh Vaughn Vernon" Saya telah memutuskan untuk memperbaiki kode saya untuk kegunaan yang lebih baik dengan mengisolasi apa yang saya yakini sebagai konsep domain inti ke dalam modul terpisah.

Setiap modul berisi set sendiri lapisan arsitektur yang berbeda yang mencakup Domain, Infrastruktur, dan lapisan Aplikasi / Presentasi (sesuai rekomendasi Vaughn, saya telah memutuskan untuk lebih jauh memisahkan tanggung jawab lapisan Aplikasi dari rute, pengontrol MVC + templat yang ada di Lapisan Presentasi).

Saya telah memutuskan untuk menempatkan masing-masing lapisan ini dalam paket mereka sendiri; dan setiap paket mereferensikan layer di bawahnya sebagai dependensi. yaitu: Lapisan Presentasi tergantung pada Lapisan Aplikasi, Aplikasi tergantung pada Infrastruktur, dll. Karena Repositori adalah bagian dari Domain, setiap Antarmuka Repositori ada di dalam lapisan / paket Domain dengan implementasi menjadi tanggung jawab lapisan / paket Infrastruktur (Doktrin , dll).

Saya berharap bahwa dengan merestrukturisasi kode saya dengan cara ini saya akan dapat menukar lapisan Aplikasi dan menggunakan kembali Domain saya di beberapa Aplikasi Web.

Kode akhirnya terlihat seperti mulai terbentuk lagi namun yang masih membingungkan saya adalah perbedaan antara Aplikasi, Infrastruktur, dan Layanan Domain ini.

Salah satu contoh umum Layanan Domain adalah sesuatu yang akan Anda gunakan untuk kata sandi hash. Ini masuk akal bagi saya dari perspektif SRP karena entitas Pengguna seharusnya tidak mementingkan dirinya sendiri dengan banyak algoritma hashing yang berbeda yang dapat digunakan untuk menyimpan kredensial pengguna.

Maka dengan itu dalam pikiran saya memperlakukan layanan Domain baru ini dengan cara yang sama seperti Repositori saya; dengan mendefinisikan antarmuka di Domain dan membiarkan implementasinya hingga ke lapisan Infrastruktur. Namun, sekarang saya bertanya-tanya tentang apa yang harus dilakukan dengan Layanan Aplikasi.

Seperti sekarang, setiap Entitas memiliki Layanan Aplikasi sendiri, yaitu Entitas Pengguna memiliki Layanan Pengguna dalam Lapisan Aplikasi. UserService dalam hal ini bertanggung jawab untuk mem-parsing tipe data primitif dan menangani kasus penggunaan umum "UserService :: CreateUser (nama string, email string, dll): Pengguna.

Yang membuat saya khawatir adalah kenyataan bahwa saya perlu mengimplementasikan kembali logika ini di beberapa aplikasi jika saya memutuskan untuk menukar layer Aplikasi. Jadi saya kira ini mengarahkan saya ke beberapa pertanyaan saya berikutnya:

  1. Apakah Layanan Domain hanyalah Antarmuka yang ada untuk memberikan lapisan abstraksi antara Lapisan Infrastruktur dan Model Anda? yaitu: Repositori + Layanan Hashing, dll.

  2. Saya sebutkan memiliki Layanan Aplikasi yang terlihat seperti ini:

    • Akses / Aplikasi / Layanan / UserService :: CreateUser (nama string, email string, dll): Pengguna

    • Tanda tangan metode menerima argumen tipe data primitif dan mengembalikan Entitas Pengguna baru (bukan DTO!).

    Apakah ini termasuk dalam lapisan Infrastruktur sebagai implementasi dari beberapa antarmuka yang didefinisikan dalam lapisan Domain atau apakah Lapisan Aplikasi sebenarnya lebih sesuai karena argumen tipe data primitif, dll ?

    contoh:

    Access/Domain/Services/UserServiceInterface 

    dan

    Access/Infrastructure/Services/UserService implements UserServiceInterface
  3. Bagaimana seharusnya modul terpisah menangani hubungan searah. Haruskah lapisan aplikasi modul A modul referensi B (seperti yang dilakukannya sekarang) atau implementasi infrastruktur (melalui antarmuka terpisah)?

  4. Apakah Layanan Lapisan Aplikasi memerlukan Antarmuka Terpisah? Jika jawabannya ya maka di mana mereka seharusnya berada?

pengguna2308097
sumber
2
Ini adalah masalah yang sangat beragam. Saya sarankan Anda membaginya menjadi pertanyaan terpisah.
guillaume31

Jawaban:

7

Apakah Layanan Domain hanyalah Antarmuka yang ada untuk memberikan lapisan abstraksi antara Lapisan Infrastruktur dan Model Anda? yaitu: Repositori + Layanan Hashing, dll.

Tanggung jawab layanan domain mencakup beberapa hal. Yang paling jelas adalah logika perumahan yang tidak cocok dengan satu entitas. Misalnya, Anda mungkin perlu mengesahkan pengembalian dana untuk pembelian tertentu, tetapi untuk menyelesaikan operasi Anda memerlukan data dari Purchaseentitas, Customerentitas, CustomerMembershipentitas.

Layanan domain juga menyediakan operasi yang diperlukan oleh domain untuk menyelesaikan fungsinya seperti PasswordEncryptionService, tetapi implementasi layanan ini akan berada di lapisan infrastruktur karena sebagian besar akan menjadi solusi teknis.

Layanan infrastruktur adalah layanan yang jadi operasi infrastruktur seperti membuka koneksi jaringan, menyalin file dari sistem file, berbicara dengan layanan web eksternal atau berbicara dengan basis data.

Layanan aplikasi adalah implementasi use case dalam aplikasi yang Anda bangun. Jika Anda membatalkan reservasi penerbangan, Anda akan:

  1. Permintaan database untuk objek Reservasi.
  2. aktifkan Reservasi-> batalkan.
  3. Simpan objek ke DB.

Lapisan aplikasi adalah klien dari domain. Domain tidak tahu kasus penggunaan Anda. Itu hanya memperlihatkan fungsi melalui agregat dan layanan domainnya. Namun lapisan aplikasi mencerminkan apa yang ingin Anda capai dengan mengatur lapisan domain dan infrastruktur.

Saya sebutkan memiliki Layanan Aplikasi yang terlihat seperti ini: Akses / Aplikasi / Layanan / UserService :: CreateUser (nama string, email string, dll): Pengguna Tanda tangan metode menerima argumen tipe data primitif dan mengembalikan Entitas Pengguna baru (bukan DTO !).

PHP mungkin bukan tempat terbaik untuk mulai belajar tentang DDD karena banyak kerangka kerja PHP di luar sana (Laravel, Symfony, Zend, dll) cenderung mempromosikan RAD. Mereka lebih fokus pada CRUD dan menerjemahkan formulir ke entitas. CRUD! = DDD

Lapisan presentasi Anda harus bertanggung jawab untuk membaca input formulir dari objek permintaan dan menjalankan layanan aplikasi yang benar. Layanan aplikasi akan membuat pengguna dan memanggil repositori Pengguna untuk menyimpan pengguna baru. Anda dapat secara opsional mengembalikan DTO pengguna kembali ke lapisan presentasi.

Bagaimana seharusnya modul terpisah menangani hubungan searah. Haruskah lapisan aplikasi modul A modul referensi B (seperti yang dilakukannya sekarang) atau implementasi infrastruktur (melalui antarmuka terpisah)?

Modul kata dalam istilah DDD memiliki arti yang berbeda dari yang Anda gambarkan. Modul harus memuat konsep terkait. Misalnya, modul pesanan di lapisan domain dapat menampung agregat pesanan, entitas OrderItem, OrderRepositoryInterface, dan MaxOrderValidationService.

Modul Order di lapisan aplikasi dapat menampung OrderApplicationServie, CreateOrderCommand dan OrderDto.

Jika Anda berbicara tentang lapisan maka setiap lapisan sebaiknya tergantung pada antarmuka lapisan lain bila memungkinkan. Lapisan presentasi harus bergantung pada antarmuka lapisan aplikasi. Lapisan aplikasi harus merujuk antarmuka dari repositori atau layanan domain.

Saya pribadi tidak membuat antarmuka untuk entitas dan objek nilai karena saya percaya antarmuka harus terkait dengan perilaku, tapi YMMV :)

Apakah Layanan Lapisan Aplikasi memerlukan Antarmuka Terpisah? Jika jawabannya ya maka di mana mereka seharusnya berada?

Itu tergantung :) untuk aplikasi kompleks saya membangun antarmuka karena kita menerapkan unit yang ketat, pengujian integrasi dan penerimaan. Kopling longgar adalah kunci di sini dan antarmuka berada di lapisan yang sama (lapisan aplikasi).

Untuk aplikasi sederhana saya membangun terhadap layanan aplikasi secara langsung.

Songo
sumber
Saya setuju untuk sebagian besar dari apa yang telah Anda tulis, hanya saya ingin mengatakan bahwa bagian RAD dari kerangka kerja sangat membantu prototipe aplikasi, untuk decoupling kita dapat mengandalkan Pemodelan Domain bahkan jika PHP tidak dikenal dengan ini, jika kita memiliki implementasi rekaman aktif sebagai sumber data lapisan kita dapat menganggapnya sebagai DTO sebagai paman bob mengatakan adalah jenis lain dari DTO dan adaptor dapat memediasi antara lapisan domain dan perintah sumber data juga merupakan jenis DTO lain praktik terbaik adalah dengan memasangkan bentuk ke Command (DTO) bukan entitas, hampir setiap kerangka kerja memiliki wadah DI jadi gunakan antarmuka.
Cherif BOUCHELAGHEM
1

Hmm jawaban singkat untuk pertanyaan panjang, tapi saya melihat polanya sebagai berikut

Aturan 1: Objek Domain harus memiliki Root Agregat tunggal

Aturan 2: Agregat Root seharusnya tidak terlalu besar, Pisahkan semuanya menjadi Konteks Terbatas

Masalah: Agregat Root segera menjadi terlalu besar dan tidak ada cara yang jelas untuk menggambar garis antara berbagai Model Domain di dalamnya

Solusi: Layanan Domain. Buat antarmuka yang Anda dapat menyuntikkan ke Model Domain yang memungkinkan mereka untuk melakukan hal-hal di luar Konteks Domain atau Agregat Root mereka.

Jadi saya pikir saya akan mengatakan bahwa contoh Anda hanya Layanan / Repositori dll, IDatabaseRepositoryForStoringUsers atau IGenericHashingCode

Layanan Domain memungkinkan komunikasi antara Konteks Terbatas. yaitu

User.UpgradeAccount(IAccountService accountService)
{
    accountService.UpgradeUsersAccount(this.Id);
}

Di mana Pengguna dan Akun berada di Root Agregat terpisah / Konteks Terbatas.

Jika Pengguna dan Akun berada di Root Agregat yang sama, Anda tentu harus dapat melakukan:

User.UpgradeAccount()
{
    this.MyAccount.Upgrade();
}

Saya tidak sepenuhnya jelas dari pertanyaan Anda bagaimana Anda mengintegrasikan hal-hal Aplikasi / Infrastruktur nTier dan hal-hal Modul. Obvs Anda tidak benar-benar ingin referensi silang antara Konteks Terikat, sehingga Anda akan menempatkan Antarmuka Layanan Domain Anda di Modul mereka sendiri yang tidak merujuk pada Konteks Terikat lainnya. membatasi Anda untuk mengekspos tipe nilai dasar, atau perhapse hanya DTO

Ewan
sumber
Integrasi merupakan perhatian dari paket / lapisan Aplikasi setiap modul. yaitu: Service Container Dynamic Bindings + Routes. Adapun hubungan lintas-modul - jika modul lain yang terpisah (ProjectManagement misalnya) perlu melakukan beberapa jenis Otentikasi di dalam API masing-masing itu sendiri daripada saya menyertakan lapisan Aplikasi modul "Akses" sebagai ketergantungan yang terdaftar. Manajer paket kemudian mengambil semua dependensi bersarang yang tersisa. Akses Aplikasi -> Akses Infrastruktur -> Akses Domain sehingga saya dapat melakukan pekerjaan yang diperlukan.
user2308097
1
Ya saya pikir semacam itu menghubungkan domain mungkin kesalahan. Anda cenderung mendapatkan dependensi melingkar
Ewan