DDDを使用せず、ESを使用しない(または使用する)CQRS-書き込みモデルとは何か、読み取りモデルとは何ですか?


11

私の知る限り、CQRSの背後にある大きなアイデアは、コマンドとクエリを処理するための2つの異なるデータモデルを持つことです。これらは「書き込みモデル」および「読み取りモデル」と呼ばれます。

Twitterアプリケーションのクローンの例を考えてみましょう。コマンドは次のとおりです。

  • ユーザーは自分で登録できます。CreateUserCommand(string username)放出するUserCreatedEvent
  • ユーザーは他のユーザーをフォローできます。FollowUserCommand(int userAId, int userBId)放出するUserFollowedEvent
  • ユーザーは投稿を作成できます。CreatePostCommand(int userId, string text)放出するPostCreatedEvent

上記では「イベント」という用語を使用していますが、「イベントソース」イベントを意味するものではありません。読み取りモデルの更新をトリガーする信号を意味します。イベントストアはありませんが、これまではCQRS自体に集中したいと考えています。

クエリは次のとおりです。

  • ユーザーは投稿のリストを見る必要があります。 GetPostsQuery(int userId)
  • ユーザーは、フォロワーのリストを見る必要があります。 GetFollowersQuery(int userId)
  • ユーザーは、フォローしているユーザーのリストを見る必要があります。 GetFollowedUsersQuery(int userId)
  • ユーザーは、「フレンドフィード」-すべての友達のアクティビティのログ(「友達のジョンが新しい投稿を作成しました」)を表示する必要があります。 GetFriedFeedRecordsQuery(int userId)

処理CreateUserCommandするには、そのようなユーザーが既に存在するかどうかを知る必要があります。したがって、この時点で、自分の書き込みモデルにはすべてのユーザーのリストが必要であることを知っています。

処理FollowUserCommandするには、userAがすでにuserBをフォローしているかどうかを知る必要があります。この時点で、書き込みモデルにすべてのユーザーフォローユーザー接続のリストが必要です。

そして最後に、処理するCreatePostCommandために他に何も必要ないと思います。なぜなら、私にはのようなコマンドがないからUpdatePostCommandです。それらがある場合、投稿が存在することを確認する必要があるため、すべての投稿のリストが必要になります。しかし、この要件がないため、すべての投稿を追跡する必要はありません。

質問#1:実際に使用する「モデルの書き込み」という用語を使用するのは実際に正しいですか?または、ESの場合、「書き込みモデル」は常に「イベントストア」を表しますか?もしそうなら、コマンドを処理する必要があるデータとクエリを処理する必要があるデータの間に何らかの分離がありますか?

を処理GetPostsQueryするには、すべての投稿のリストが必要です。つまり、読み取りモデルにはすべての投稿のリストが必要です。私はを聞いてこのモデルを維持しPostCreatedEventます。

GetFollowersQueryとの両方を処理するには、GetFollowedUsersQueryユーザー間のすべての接続のリストが必要です。このモデルを維持するために耳を傾けるつもりですUserFollowedEvent。ここに質問#2があります:書き込みモデルの接続リストをここで使用しても実質上問題ありませんか?または、将来、書き込みモデルよりも詳細な情報が必要になる可能性があるため、個別の読み取りモデルを作成する必要がありますか?

最後に、処理するGetFriendFeedRecordsQueryには次のことが必要です。

  • 聞く UserFollowedEvent
  • 聞く PostCreatedEvent
  • どのユーザーがどの他のユーザーをフォローしているかを知る

ユーザーAがユーザーBをフォローし、ユーザーBがユーザーCをフォローし始めると、次のレコードが表示されます。

  • ユーザーAの場合:「友達ユーザーBはユーザーCのフォローを開始しました」
  • ユーザーBの場合:「ユーザーCのフォローを開始しました」
  • ユーザーCの場合:「ユーザーBがあなたをフォローしています」

ここだ質問#3:私は接続のリストを取得するために使用すべきかのモデル?書き込みモデルを使用する必要がありますか?読み取りモデルを使用する必要があります- GetFollowersQuery/ GetFollowedUsersQuery?または、GetFriendFeedRecordsQueryモデル自体UserFollowedEventにすべての接続の独自のリストを処理させ、維持させる必要がありますか?


CQRSシステムでは、クエリデータモデルとコマンドデータモデルが異なるデータベースのデータを消費している可能性があることに注意してください。そして、両方のモデルが互いに独立して生活している可能性があります(異なるアプリ)。そうは言っても、答えは「依存」です(通常どおり)。興味
-Laiv

回答:


7

グレッグヤング(2010)

CQRSは、以前は1つしかなかった2つのオブジェクトの作成です。

Bertrand MeyerのCommand Query Separationの観点から考えると、モデルには、コマンドをサポートするインターフェイスとクエリをサポートするインターフェイスの2つの異なるインターフェイスがあると考えることができます。

interface IChangeTheModel {
    void createUser(string username)
    void followUser(int userAId, int userBId)
    void createPost(int userId, string text)
}

interface IDontChangeTheModel {
    Iterable<Post> getPosts(int userId)
    Iterable<Follower> getFollowers(int userId)
    Iterable<FollowedUser> getFollowedUsers(int userId)
}

class TheModel implements IChangeTheModel, IDontChangeTheModel {
    // ...
}

グレッグヤングの洞察は、これを2つの別々のオブジェクトに分離できるということでした

class WriteModel implements IChangeTheModel { ... }
class ReadModel  implements IDontChangeTheModel {...}

オブジェクトを分離した後、オブジェクトの状態をメモリに保持するデータ構造を分離するオプションがあり、それぞれの場合に最適化できます。または、書き込み状態とは別に読み取り状態を保存/永続化します。

質問#1:「モデルの書き込み」という用語を使用するのは実際には正しいですか?または、ESの場合、「書き込みモデル」は常に「イベントストア」を表しますか?もしそうなら、コマンドを処理する必要があるデータとクエリを処理する必要があるデータの間に何らかの分離がありますか?

WriteModelという用語は、通常、モデルの可変表現(つまり、永続ストアではなくオブジェクト)と理解されています。

TL; DR:あなたの使用は問題ないと思います。

ここに質問#2があります:ここで書き込みモデルの接続リストを使用しても実質上問題ありませんか?

それは「素晴らしい」-です。概念的には、同じ構造を共有する読み取りモデルと書き込みモデルには何も問題はありません。

実際には、モデルへの書き込みは通常アトミックではないため、1つのスレッドがモデルの状態を変更しようとしているときに2番目のスレッドがそれを読み取ろうとしているときに問題が発生する可能性があります。

質問#3:接続のリストを取得するには、どのモデルを使用する必要がありますか?書き込みモデルを使用する必要がありますか?読み取りモデル-GetFollowersQuery / GetFollowedUsersQueryを使用する必要がありますか?または、GetFriendFeedRecordsQueryモデル自体にUserFollowedEventを処理させ、すべての接続の独自のリストを維持させる必要がありますか?

書き込みモデルを使用するのは間違った答えです。

それぞれが特定のユースケースに合わせて調整される複数の読み取りモデルを作成することは、まったく合理的です。私たちは、「読み取りモデル」と言うが、そこに特定のユースケースのために最適化され、それぞれが多くの読み取りモデルである可能性があり、そしてないことを理解されていない、それは意味がない例を実装します。

たとえば、キーと値のストアを使用して一部のクエリをサポートし、他のクエリのグラフデータベース、またはそのクエリモデルが理にかなっているリレーショナルデータベースを使用する場合があります。コース用の馬。

まだパターンを学習している特定の状況では、デザインを「シンプル」に保つことをお勧めします-書き込みモデルのデータ構造を共有しない読み取りモデルを1つ用意します。


1

概念データモデルが1つあることを検討することをお勧めします。

次に、書き込みモデルは、トランザクション更新用に最適化されたそのデータモデルの具体化です。これは、正規化されたリレーショナルデータベースを意味する場合があります。

読み取りモデルは、アプリケーションが必要とするクエリを実行するために最適化された同じデータモデルの具体化です。結合の数が少ないクエリを処理するために意図的に非正規化されていますが、リレーショナルデータベースである可能性があります。


(#1)CQRSの場合、書き込みモデルはイベントストアである必要はありません。

(#2)読み取​​りモデルが書き込みモデルにないものを格納するとは思わないでしょう。なぜなら、CQRSでは、読み取りモデルをモデルを書きます。

(#3)CQRSでは、クエリは読み取りモデルに反する必要があります。あなたは別のことをすることができます:それは大丈夫です、ただCQRSに従っていません。


要約すると、概念的なデータモデルは1つだけです。CQRSは、コマンドネットワークと機能をクエリネットワークと機能から分離します。パフォーマンスの最適化として非常に異なるテクノロジーを使用して、書き込みモデルと読み取りモデルを分離できることを前提としています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.