概要
CQRS / DDDの承認は、コマンド/クエリごとに実装する必要がありますか?
多かれ少なかれ厳密にDDD CQRSパターンを使用するオンラインアプリケーションを初めて開発しています。私はいくつかの問題にぶつかりましたが、それを本当に回避することはできません。
私が作成しているアプリケーションは、元帳アプリケーションで、元帳を作成できるほか、従業員など他の人が元帳を表示/編集/削除できます。元帳の作成者は、作成した元帳のアクセス権を編集できる必要があります。所有権を変更することさえできます。ドメインには、TLedgerとTUserの 2つの集約があります。
セキュリティ、認可などに関するDDD / CQRSキーワードを含む多くの投稿を読みました。それらのほとんどは、セキュリティアプリケーションを構築していない限り、認可はGeneric Subdomainであると述べました。
この場合、コアドメインは確かに、トランザクション、バランシング、アカウントに関心のあるアカウンティングドメインです。ただし、元帳へのきめ細かいアクセスを管理できる機能も必要です。これをDDD / CQRSの用語でどのように設計するのか疑問に思っています。
コマンドがユビキタス言語の一部であるということは、DDDチュートリアルの至る所で述べられています。それらは意味があります。それらは「本物」を表す具体的なアクションです。
これらのコマンドとクエリはすべて、ユーザーが「実際に」実行する実際のアクションであるため、承認の実装をこれらすべての「コマンド」および「クエリ」と組み合わせる必要がありますか?ユーザーには、たとえばTLedger.addTransaction()を実行する権限がありますが、TLedger.removeTransaction()は実行できません。または、ユーザーはクエリ「getSummaries()」を実行できますが、「getTransactions()」は実行できません。
アクセス権を決定するために、user-ledger-commandまたはuser-ledger-queryの形式で3次元マッピングが存在します。
または、切り離された方法で、「permissions」という名前がユーザーに登録されます。特定のコマンド用にマップされる許可。たとえば、権限「ManageTransactions」では、ユーザーは「AddTransaction()」、「RemoveTransaction()」などを実行できます。
権限マッピングユーザー->元帳->コマンド/クエリ
パーミッションマッピングユーザー->元帳->パーミッション->コマンド/クエリ
それが質問の最初の部分です。または、簡単に言えば、CQRS / DDDの承認はコマンドごとまたはクエリごとに実装する必要がありますか?または、許可をコマンドから分離する必要がありますか?
第二に、許可に基づいた認可について。ユーザーは、自分の元帳または管理が許可されている元帳の権限を管理できる必要があります。
- 元帳で許可管理コマンドが発生する
grantPermission()、revokePermission()などのイベント/コマンド/ハンドラーをLedger集計に追加することを考えました。この場合、これらのルールの強制はコマンドハンドラーで発生します。ただし、これにはすべてのコマンドに、そのコマンドを発行したユーザーのIDを含める必要があります。次に、そのコマンドを実行する権限がそのユーザーに存在する場合、TLedgerをチェックインします。
例えば :
class TLedger{
function addTransactionCmdHandler(cmd){
if (!this.permissions.exist(user, 'addTransaction')
throw new Error('Not Authorized');
}
}
- ユーザーの許可管理コマンド
もう1つの方法は、TUserにアクセス許可を含めることです。TUserには一連の権限があります。次に、TLedgerコマンドハンドラーでユーザーを取得し、コマンドを実行する権限があるかどうかを確認します。しかし、これには、すべてのTLedgerコマンドのTUser集計を取得する必要があります。
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);
})
}
- サービスを持つ別のドメイン
別の可能性は、別の許可ドメインを完全にモデル化することです。このドメインは、アクセス権、承認などに関心があります。会計サブドメインは、サービスを使用して、この承認ドメインにの形式でアクセスします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);
})
}
どの決定が最も「DDD / CQRS」の方法でしょうか?