131

RDBMS (SQL Server など) を使用してイベント ソーシング データを格納する場合、スキーマはどのようになりますか?

いくつかのバリエーションが抽象的な意味で語られているのを見たことがありますが、具体的なものは何もありません.

たとえば、「製品」エンティティがあり、その製品への変更は、価格、コスト、および説明の形式で行われるとします。次のことを行うかどうかについて混乱しています。

  1. 製品のすべてのフィールドを含む「ProductEvent」テーブルを用意します。各変更は、そのテーブルの新しいレコードを意味し、必要に応じて「誰が、何を、どこで、なぜ、いつ、どのように」(WWWWWWH) を加えます。コスト、価格、または説明が変更されると、製品を表すためにまったく新しい行が追加されます。
  2. 製品のコスト、価格、および説明を、外部キー関係を使用して製品テーブルに結合された個別のテーブルに格納します。これらのプロパティが変更された場合は、必要に応じて WWWWH を使用して新しい行を書き込みます。
  3. WWWWWH と、イベントを表すシリアル化されたオブジェクトを "ProductEvent" テーブルに格納します。つまり、特定の製品のアプリケーション状態を再構築するには、アプリケーション コードでイベント自体をロードし、シリアル化解除して再生する必要があります。 .

特に、上記のオプション 2 が心配です。極端に言えば、製品テーブルはプロパティごとにほぼ 1 つのテーブルになり、特定の製品のアプリケーション状態をロードするには、各製品イベント テーブルからその製品のすべてのイベントをロードする必要があります。このテーブルの爆発は、私には悪いにおいがします。

「場合による」と確信しており、単一の「正解」はありませんが、何が受け入れられ、何がまったく受け入れられないかを感じようとしています。また、NoSQL がここで役立つことも認識しています。ここでは、イベントを集約ルートに対して保存できます。つまり、オブジェクトを再構築するためのイベントを取得するためのデータベースへの単一の要求のみを意味しますが、NoSQL データベースを使用していませんその瞬間、私は代替案を探しています。

4

6 に答える 6

119

イベント ストアは、イベントの特定のフィールドまたはプロパティについて知る必要はありません。そうしないと、モデルを変更するたびにデータベースを移行する必要が生じます (古き良き状態ベースの永続化と同様)。したがって、オプション 1 と 2 はまったくお勧めしません。

以下は、Ncqrsで使用されるスキーマです。ご覧のとおり、「Events」テーブルには、関連データが CLOB (つまり、JSON または XML) として格納されます。これは、オプション 3 に対応します (1 つの汎用「イベント」テーブルしか必要ないため、「ProductEvents」テーブルはありません。Ncqrs では、集約ルートへのマッピングは「EventSources」テーブルを介して行われ、各 EventSource は実際の集約ルート。)

Table Events:
    Id [uniqueidentifier] NOT NULL,
    TimeStamp [datetime] NOT NULL,

    Name [varchar](max) NOT NULL,
    Version [varchar](max) NOT NULL,

    EventSourceId [uniqueidentifier] NOT NULL,
    Sequence [bigint], 

    Data [nvarchar](max) NOT NULL

Table EventSources:
    Id [uniqueidentifier] NOT NULL, 
    Type [nvarchar](255) NOT NULL, 
    Version [int] NOT NULL

Jonathan Oliver の Event Store 実装の SQL 永続化メカニズムは、基本的に、BLOB フィールド「Payload」を持つ「Commits」と呼ばれる 1 つのテーブルで構成されます。これは Ncqrs とほぼ同じですが、イベントのプロパティをバイナリ形式でシリアル化する (たとえば、暗号化のサポートを追加する) だけです。

Greg Young は、 Greg の Web サイト で詳しく説明されているように、同様のアプローチを推奨しています。

彼の典型的な「イベント」テーブルのスキーマは次のとおりです。

Table Events
    AggregateId [Guid],
    Data [Blob],
    SequenceNumber [Long],
    Version [Int]
于 2011-08-15T13:02:12.060 に答える
2

ソリューション (1 & 2) は、ドメイン モデルが進化するにつれて、すぐに問題になる可能性があると思います。新しいフィールドが作成され、意味が変わるものもあれば、使用されなくなるものもあります。最終的に、テーブルには数十の null 許容フィールドが含まれ、イベントのロードは混乱します。

また、イベント ストアは書き込みのみに使用する必要があることに注意してください。クエリを実行して、集計のプロパティではなく、イベントをロードするだけです。それらは別個のものです (それが CQRS の本質です)。

解決策 3 人々が通常行うことですが、それを達成する方法はたくさんあります。

例として、EventFlow CQRSを SQL Server で使用すると、次のスキーマでテーブルが作成されます。

CREATE TABLE [dbo].[EventFlow](
    [GlobalSequenceNumber] [bigint] IDENTITY(1,1) NOT NULL,
    [BatchId] [uniqueidentifier] NOT NULL,
    [AggregateId] [nvarchar](255) NOT NULL,
    [AggregateName] [nvarchar](255) NOT NULL,
    [Data] [nvarchar](max) NOT NULL,
    [Metadata] [nvarchar](max) NOT NULL,
    [AggregateSequenceNumber] [int] NOT NULL,
 CONSTRAINT [PK_EventFlow] PRIMARY KEY CLUSTERED 
(
    [GlobalSequenceNumber] ASC
)

どこ:

  • GlobalSequenceNumber : プロジェクション (readmodel) を作成するときに、単純なグローバル ID を使用して、欠落しているイベントを順序付けまたは識別することができます。
  • BatchId : アトミックに挿入されたイベントのグループの識別 (TBH、これが役立つ理由がわからない)
  • AggregateId : 集計の識別
  • データ: シリアル化されたイベント
  • メタデータ: イベントからのその他の有用な情報 (例: 逆シリアル化に使用されるイベント タイプ、タイムスタンプ、コマンドからの発信者 ID など)
  • AggregateSequenceNumber : 同じ集約内のシーケンス番号 (これは、順不同で書き込みを行うことができない場合に役立つため、このフィールドを使用して楽観的同時実行を実現します)

ただし、ゼロから作成する場合は、YAGNI 原則に従い、ユース ケースに必要最小限のフィールドで作成することをお勧めします。

于 2019-06-28T15:00:51.777 に答える
1

考えられるヒントは、デザインの後に「ゆっくりと変化するディメンション」(type=2) があり、カバーするのに役立ちます。

  • イベントの発生順序 (代理キー経由)
  • 各状態の耐久性 (有効期間 - 有効期間)

左折り関数も実装しても問題ないはずですが、将来のクエリの複雑さを考える必要があります。

于 2015-03-13T00:06:08.417 に答える