web-dev-qa-db-ja.com

DDD CQRS-クエリごとおよびコマンドごとの認証

概要

CQRS/DDDでの認証をコマンド/クエリごとに実装する必要があるかどうか

ほぼ厳密にDDD CQRSパターンを使用するオンラインアプリケーションを初めて開発しています。私はいくつかの問題にぶつかりましたが、頭がよくなりません。

私が作成しているアプリケーションは、人々が元帳を作成できるようにするだけでなく、他の人々が従業員などのそれらを表示/編集/削除できるようにする元帳アプリケーションです。元帳の作成者は、自分が作成した元帳のアクセス権を編集できる必要があります。所有権を変更することもできます。ドメインには2つの集約TLedgerおよびTUserがあります。

私は、セキュリティ、承認などに関するDDD/CQRSキーワードを使用して多くの投稿を読みました。それらのほとんどは、セキュリティアプリケーションを構築していない限り、承認はGeneric Subdomainであると述べていました。

この場合、コアドメインは、トランザクション、バランシング、およびアカウントに関心のあるアカウンティングドメインです。ただし、元帳への細かいアクセスを管理できる機能も必要です。これをDDD/CQRS用語で設計する方法を知りたいです。

DDDチュートリアルでは、コマンドがユビキタス言語の一部であると述べています。彼らは意味があります。それらは「本物」を表す具体的な行動です。

これらのコマンドとクエリはすべて、ユーザーが「実生活」で実行する実際のアクションであるため、承認の実装をこれらすべての「コマンド」と「クエリ」と組み合わせる必要がありますか?ユーザーはTLedger.addTransaction()を実行する権限を持ちますが、たとえばTLedger.removeTransaction()は実行できません。または、ユーザーはクエリ「getSummaries()」を実行できますが、「getTransactions()」は実行できません。

3次元マッピングは、アクセス権を決定するために、user-ledger-commandまたはuser-ledger-queryの形式で存在します。

または、分離された方法で、名前付きの「許可」がユーザーに登録されます。その後、特定のコマンドにマップされる権限。たとえば、権限「ManageTransactions」では、ユーザーが「AddTransaction()」、「RemoveTransaction()」などを実行できます。

  1. 権限マッピングユーザー->元帳->コマンド/クエリ

  2. 権限マッピングユーザー->元帳->権限->コマンド/クエリ

それが質問の最初の部分です。または、簡単に言うと、CQRS/DDDの承認はコマンドごとまたはクエリごとに実装する必要がありますか?または、承認をコマンドから切り離すべきですか?

第二に、許可に基づく許可に関する。ユーザーは、自分の元帳または管理が許可されている元帳の権限を管理できる必要があります。

  1. 承認管理コマンドは元帳で発生します

イベント/コマンド/ハンドラーを、grantPermission()、revokePermission()などのLedger集約に追加することを考えました。この場合、これらのルールを強制すると、コマンドハンドラーで発生します。ただし、この場合、すべてのコマンドに、そのコマンドを発行したユーザーのIDを含める必要があります。次に、そのユーザーがそのコマンドを実行するための権限が存在するかどうかをTLedgerにチェックインします。

例えば ​​:

_class TLedger{ 
    function addTransactionCmdHandler(cmd){
        if (!this.permissions.exist(user, 'addTransaction')
            throw new Error('Not Authorized');
    }
}
_
  1. ユーザーの承認管理コマンド

もう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);
    })

}
_
  1. サービスのある別のドメイン

別の可能性は、別の承認ドメインを完全にモデル化することです。このドメインは、アクセス権、承認などに関心があります。アカウンティングサブドメインは、サービスを使用して、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」の方法はどのような決定でしょうか?

15
Ludovic C

最初の質問で、私は似たようなものと格闘してきました。次第に、3段階の承認スキームに傾倒しています。

1)コマンド/クエリレベルでの「このユーザーはeverこのコマンドを実行する権限を持っていますか?」という承認MVCアプリでは、これはおそらくコントローラーレベルで処理できますが、現在のユーザーと実行中のコマンドに基づいてアクセス許可ストアをクエリする汎用プリハンドラーを選択しています。

2)「このユーザーは「このエンティティにアクセスするためのアクセス許可を持っていますか?」というアプリケーションサービス内での承認。基本的に、OrganizationIdの粒度が少し高いTenantId。

3)エンティティの一時的なプロパティ(ステータスなど)に依存する承認は、ドメイン内で処理されます。 (例:「特定の人だけが閉じた元帳を変更できます。」)ドメインとビジネスロジックに大きく依存しているため、ドメイン内に配置することを選択しています。

私はこのアイデアに対する他の人の反応を聞きたいです-もし望むなら、それを千切りに引き裂いてください(そうするなら、いくつかの代替案を提供してください:))

5
emragins

承認BCの一部として承認を実装しますが、それをアクションフィルターとして元帳システムに展開します。このようにして、それらを論理的に分離することができます-元帳コードは認証コードを呼び出す必要はありません-しかし、着信リクエストごとに高性能のインプロセス認証を取得できます。

0
pnschofield