Ringkasan
Haruskah otorisasi dalam CQRS / DDD diimplementasikan per-perintah / permintaan atau tidak?
Saya mengembangkan untuk pertama kalinya aplikasi online menggunakan pola DDD CQRS secara ketat. Saya bertemu dengan beberapa masalah, yang tidak bisa saya pikirkan.
Aplikasi yang saya bangun adalah aplikasi buku besar yang memungkinkan orang untuk membuat buku besar, serta memungkinkan orang lain untuk melihat / mengedit / menghapusnya, seperti karyawan. Pembuat buku besar harus dapat mengedit hak akses buku besar yang dibuatnya. Bahkan bisa mengubah kepemilikan. Domain ini memiliki dua agregat, TLedger dan TUser .
Saya membaca banyak posting dengan kata kunci DDD / CQRS mengenai keamanan, otorisasi, dll. Sebagian besar dari mereka menyatakan bahwa otorisasi adalah Subdomain Generik , kecuali ada yang membangun aplikasi keamanan.
Dalam hal ini, domain inti tentu merupakan domain akuntansi yang tertarik dengan transaksi, penyeimbangan, dan akun. Tetapi fungsionalitas untuk dapat mengelola akses berbutir halus ke buku besar juga diperlukan. Saya bertanya-tanya bagaimana merancang ini dalam istilah DDD / CQRS.
Itu dinyatakan dalam tutorial DDD di seluruh tempat bahwa perintah adalah bagian dari bahasa di mana-mana. Mereka bermakna. Itu adalah tindakan nyata yang mewakili "hal yang nyata".
Karena semua perintah dan kueri tersebut adalah tindakan aktual yang akan dijalankan pengguna dalam "kehidupan nyata", haruskah implementasi otorisasi digabungkan dengan semua "perintah" dan "kueri" ini? Seorang pengguna akan memiliki otorisasi untuk menjalankan TLedger.addTransaction () tetapi tidak TLedger.removeTransaction () misalnya. Atau, pengguna akan diizinkan untuk menjalankan kueri "getSummaries ()" tetapi tidak "getTransactions ()".
Pemetaan tiga dimensi akan ada dalam bentuk user-ledger-command atau user-ledger-query untuk menentukan hak akses.
Atau, dengan cara yang dipisahkan, bernama "izin" akan didaftarkan untuk pengguna. Izin yang kemudian akan dipetakan untuk perintah tertentu. Misalnya, izin "ManageTransactions" akan memungkinkan pengguna untuk mengeksekusi "AddTransaction ()", "RemoveTransaction ()", dll.
Izin memetakan pengguna -> buku besar -> perintah / permintaan
Pengguna pemetaan izin -> buku besar -> izin -> perintah / permintaan
Itu bagian pertama dari pertanyaan. Atau secara singkat, haruskah otorisasi dalam CQRS / DDD diimplementasikan per-perintah atau per-permintaan? Atau, haruskah otorisasi dipisahkan dari perintah?
Kedua, tentang otorisasi berdasarkan izin. Seorang Pengguna harus dapat mengelola izin pada Buku Besar atau pada Buku Besar yang diizinkan untuk dikelola.
- Perintah manajemen otorisasi terjadi di Buku Besar
Saya berpikir untuk menambahkan peristiwa / perintah / penangan ke dalam agregat Ledger , seperti grantPermission (), revokePermission (), dll. Dalam hal ini, menegakkan aturan-aturan itu akan terjadi pada penangan perintah. Tetapi ini membutuhkan semua perintah untuk menyertakan id dari pengguna yang mengeluarkan perintah itu. Kemudian saya akan memeriksa ke TLedger jika ada izin bagi pengguna untuk menjalankan perintah itu.
Sebagai contoh :
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- Perintah manajemen otorisasi di Pengguna
Cara sebaliknya adalah dengan memasukkan izin ke dalam Pengguna. TUser akan memiliki seperangkat izin. Kemudian, dalam penangan perintah TLedger, saya akan mengambil pengguna dan memeriksa apakah dia memiliki izin untuk menjalankan perintah. Tapi ini akan mengharuskan saya untuk mengambil agregat TUser untuk setiap perintah TLedger.
class TAddTransactionCmdHandler(cmd) {
this.userRepository.find(cmd.userId)
.then(function(user){
if (!user.can(cmd)){
throw new Error('Not authorized');
}
return this.ledgerRepository.find(cmd.ledgerId);
})
.then(function(ledger){
ledger.addTransaction(cmd);
})
}
- Domain lain dengan layanan
Kemungkinan lain adalah memodelkan domain otorisasi lain sepenuhnya. Domain ini akan tertarik pada hak akses, otorisasi dll. Subdomain akuntansi kemudian akan menggunakan layanan untuk mengakses domain otorisasi ini dalam bentuk AuthorizationService.isAuthorized(user, command)
.
class TAddTransactionCmdHandler(cmd) {
authService.isAuthorized(cmd)
.then(function(authorized){
if (!authorized) throw new Error('Not authorized');
return this.ledgerRepository.find(cmd.ledgerId)
})
.then(function(){
ledger.addTransaction(cmd);
})
}
Keputusan apa yang akan menjadi cara paling "DDD / CQRS"?