デファクトツール/フレームワーク比較
「Event Sourcing をやろう」と決めた次の問いは、「何を使うか」 だ。前章で見た5つの責務(Event / Stream / Store / Projection / Snapshot)を、どこまで自前で書き、どこからは既製のものに任せるか ── ここで方針を間違えると、半年後に後悔する。
この章では、2026年5月時点で デファクトとして実務で選ばれる 6つの選択肢を比較する。
1. KurrentDB(旧 EventStoreDB)── Event Sourcing 専用 DB
2. Axon Framework ── JVM 上の DDD/CQRS/ES オールインワン
3. Marten ── .NET + PostgreSQL の Document/Event Store
4. Akka Persistence / Pekko ── Actor 上の永続化機構
5. AWS DynamoDB + EventBridge ── マネージドでの組み立て
6. 自作 PostgreSQL ベース ── 言語問わずの最小構成
最後に、選定の判断軸を整理する。
全体マップ:3つの軸で見る
ツールを比較する前に、座標軸を引いておく。
quadrantChart
title 抽象度 × プラットフォーム結合度
x-axis "言語非依存(多言語対応)" --> "特定エコシステム結合"
y-axis "インフラのみ提供(自前実装多い)" --> "フレームワーク含む(実装最小)"
quadrant-1 "高抽象+特定言語"
quadrant-2 "高抽象+多言語"
quadrant-3 "低抽象+多言語"
quadrant-4 "低抽象+特定言語"
"Axon Framework": [0.85, 0.92]
"Akka/Pekko Persistence": [0.85, 0.78]
"Marten": [0.78, 0.7]
"KurrentDB": [0.25, 0.6]
"AWS DynamoDB+EB": [0.4, 0.4]
"PostgreSQL自作": [0.2, 0.18]
3つ目の軸 ── 「ES だけ vs DDD/CQRS/ES 一式」 ── も合わせて見ておく。
ES だけ ────────────────────────── 一式
KurrentDB 自作 Marten AWS Axon Akka
これらの軸の組み合わせで、ツールごとの性格が見えてくる。
1. KurrentDB(旧 EventStoreDB)── Event Sourcing 専用 DB
概要
Greg Young 自身が CTO として関与した、Event Sourcing 専用の DB。2012年に EventStoreDB として登場し、2024年11月に Kurrent へリブランド、製品名は KurrentDB(バージョン25.0以降)になった。
特徴
✅ Event Sourcing 専用設計
Append-only / 楽観的並行性制御 / Stream / サブスクリプションが標準装備
「自分で実装すべきこと」が最も少ない
✅ 言語非依存(gRPC ベース)
.NET / Java / Node.js / Go / Python など主要言語のクライアントが揃う
✅ サブスクリプション機構が強力
Catch-up / Persistent / Filtered の3種類を提供
Projection を実装する基盤として完成度が高い
❌ 採用判断のハードル
別途「専用 DB」を運用する負担(バックアップ・監視・アップグレード)
既存スタックに PostgreSQL や DynamoDB がある場合、追加 DB の正当化が必要
❌ Read Model(CQRS の Q 側)は別途用意
KurrentDB はあくまで Event Store であり、Read Model のストアではない
TypeScript での使い方
import { KurrentDBClient, jsonEvent } from '@kurrent/kurrentdb-client'
const client = KurrentDBClient.connectionString(
'kurrentdb://localhost:2113?tls=false'
)
// 書き込み
await client.appendToStream(
'screening-process-sp-001',
[jsonEvent({
type: 'FirstInterviewScheduled',
data: { scheduledFor: '2026-05-20', interviewerId: 'iv-7' },
})],
{ expectedRevision: 'no_stream' },
)
// 読み込み
const events = client.readStream('screening-process-sp-001')
for await (const { event } of events) {
console.log(event?.type, event?.data)
}
向いているケース
- Event Sourcing を「主アーキテクチャ」として腰を据える
- 言語スタックが多様(マイクロサービス間で言語が異なる)
- サブスクリプション機構を自前実装したくない
- 専用 DB の運用負担を払える組織体制がある
公式ドキュメント:docs.kurrent.io
2. Axon Framework ── JVM のオールインワン
概要
JVM 上で DDD / CQRS / Event Sourcing を統合的に実装するためのフレームワーク。AxonIQ 社が開発・サポートしている。Java / Kotlin で使われ、累計70M ダウンロードを超える。
特徴
✅ DDD/CQRS/ES の語彙が直接コードに現れる
@Aggregate / @CommandHandler / @EventHandler / @EventSourcingHandler
アノテーションでアーキテクチャの意図を表現できる
✅ Axon Server とのセット運用が強力
Event Store + Command Bus + Query Bus + Saga 管理が一括で揃う
分散環境でのメッセージルーティングが自動化される
✅ Saga(プロセスマネージャ)の標準サポート
複数集約をまたぐ業務プロセスをコードで素直に書ける
❌ JVM ロックイン
他言語からは(基本的に)使えない。マイクロサービスが多言語なら不向き
❌ 学習コストが高い
DDD の概念を一通り知っていることが前提
アノテーション魔術が多く、デバッグ時に内部理解が必要
❌ Axon Server の運用
Standard Edition は無償だが、Enterprise 機能(クラスタ等)は商用ライセンス
Java での使い方
@Aggregate
public class ScreeningProcess {
@AggregateIdentifier
private String processId;
private ScreeningStage currentStage;
@CommandHandler
public void handle(ScheduleFirstInterviewCommand cmd) {
if (currentStage != ScreeningStage.DOCUMENT_PASSED) {
throw new IllegalStateException("Must pass document screening first");
}
AggregateLifecycle.apply(new FirstInterviewScheduledEvent(
processId, cmd.getScheduledFor(), cmd.getInterviewerId()
));
}
@EventSourcingHandler
public void on(FirstInterviewScheduledEvent event) {
this.currentStage = ScreeningStage.FIRST_INTERVIEW_SCHEDULED;
}
}
向いているケース
- Java / Kotlin / Spring エコシステム中心の組織
- DDD/CQRS/ES の3点セットを「一気通貫」で導入したい
- Saga による複数集約調整が頻出する業務
- 商用サポートが必要なエンタープライズ
3. Marten ── .NET + PostgreSQL
概要
.NET 環境で、PostgreSQL を Document DB + Event Store として使うためのライブラリ。JasperFx Software が開発。MIT ライセンス。
特徴
✅ PostgreSQL があれば動く
専用 DB が不要、既存の PostgreSQL クラスタに乗せられる
バックアップ・監視・運用ノウハウを既存資産で賄える
✅ Document DB と Event Store のハイブリッド
集約は Document として保存可、イベントストリームも保存可
段階的に Event Sourcing を導入する戦略を取りやすい
✅ Projection の async daemon が標準装備
非同期 Projection、リトライ、再構築を仕組みとして提供
✅ アクティブな開発
2025年6月時点で「Quick append mode で2倍高速化」「PostgreSQL Partitioning 対応」など
パフォーマンス改善が継続している
❌ .NET ロックイン
Java/Node.js/Go では使えない
❌ PostgreSQL ロックイン
他の RDB やマネージドサービスへは移植できない
C# での使い方
// イベントの追記
await session.Events.Append(processId,
new FirstInterviewScheduled(processId, scheduledFor, interviewerId)
);
await session.SaveChangesAsync();
// 集約の復元(Marten が自動的に Stream を読んで Apply する)
var process = await session.Events.AggregateStreamAsync<ScreeningProcess>(processId);
// Projection の登録
storeOptions.Projections.Add<ScreeningDashboardProjection>(ProjectionLifecycle.Async);
向いているケース
- .NET 中心のプロジェクト
- PostgreSQL を既に運用している(または運用ノウハウがある)
- 「全集約を ES に倒す」ではなく「一部だけ ES、残りは Document」と段階導入したい
- 専用 DB の運用負担を増やしたくない
公式:martendb.io
4. Akka Persistence / Apache Pekko Persistence
概要
Actor モデル上で、Actor の状態を Event Sourcing で永続化する機構。元は Lightbend 社の Akka に組み込まれていたが、2022年9月の Akka のライセンス変更(Apache 2.0 → Business Source License)を受け、コミュニティが Akka 2.6.x を Apache Foundation 配下にフォークした Apache Pekko が誕生した。
2026年時点の状況:
- Akka:BSL ライセンス(年間売上 25M USD 超は商用契約必要)
ただし36ヶ月後に各リリースは Apache 2.0 へ自動転換
- Pekko:Apache 2.0 のまま、コミュニティ駆動で開発継続
特徴
✅ Actor モデルとの統合
1つの Actor が1つの集約に対応する設計が自然
高並行・分散の文脈で、Akka Cluster と組み合わせると強力
✅ 多様なバックエンド
Cassandra / JDBC / Couchbase など、Plugin で永続化先を選べる
✅ Snapshot / Projection の機構が組み込み
❌ Actor モデル自体の学習コスト
DDD/ES だけでなく Actor モデルの理解も必要
❌ ライセンスの注意
Akka を選ぶか Pekko を選ぶかの判断が必要
Scala での使い方(Pekko)
object ScreeningProcessActor {
sealed trait Command
case class ScheduleFirstInterview(scheduledFor: LocalDateTime, interviewerId: String) extends Command
sealed trait Event
case class FirstInterviewScheduled(scheduledFor: LocalDateTime, interviewerId: String) extends Event
def apply(processId: String): Behavior[Command] =
EventSourcedBehavior[Command, Event, State](
persistenceId = PersistenceId.ofUniqueId(s"screening-$processId"),
emptyState = State.empty,
commandHandler = (state, cmd) => /* バリデーション → Event を返す */,
eventHandler = (state, event) => /* state を更新 */,
)
}
向いているケース
- 既に Akka/Pekko を使っている JVM プロジェクト
- 高並行・分散処理が業務要件
- Actor モデルがチームの設計言語として機能している
公式:pekko.apache.org / akka.io
5. AWS DynamoDB + EventBridge ── マネージドの組み立て
概要
専用ツールではなく、AWS のマネージドサービスを組み合わせて Event Sourcing を実現する アプローチ。DynamoDB を Event Store として、DynamoDB Streams + EventBridge + Lambda で Projection を構築する。
構成例
graph LR
Cmd["Lambda<br/>(Command)"] --> DDB[("DynamoDB<br/>━━━━━<br/>PK: streamId<br/>SK: version")]
DDB -->|変更を検知| Streams["DynamoDB<br/>Streams"]
Streams --> Pipes["EventBridge<br/>Pipes"]
Pipes --> Proj["Projection<br/>Lambda"]
Proj --> RDB[("Read DB")]
style DDB fill:#fff3e0,stroke:#fb8c00
style RDB fill:#e8f5e9,stroke:#43a047
特徴
✅ サーバレス
専用 DB のプロビジョニング不要、トラフィックに応じて自動スケール
✅ AWS エコシステム統合
EventBridge → Step Functions / SNS / SQS への連携が容易
既に AWS を使っているなら追加コストが小さい
✅ 楽観的並行性制御は DynamoDB の condition expression で実現可能
❌ Event Store としての機能は自前で組み立てる
Stream の読み取り、Snapshot、再生機構などはアプリ側で実装
❌ ベンダーロックイン
設計が AWS 固有になり、他クラウドへの移植性は低い
❌ DynamoDB のサイズ・コスト特性を理解する必要
単一 PK あたりのアイテム数制限、ホットパーティション問題への対処
TypeScript での使い方(簡略)
// 楽観的並行性制御つき書き込み
// テーブルは PK=streamId, SK=version の複合キーで構成
await dynamodb.send(new PutItemCommand({
TableName: 'events',
Item: {
streamId: { S: 'screening-process-sp-001' },
version: { N: '5' },
eventType: { S: 'FirstInterviewScheduled' },
payload: { S: JSON.stringify({ scheduledFor, interviewerId }) },
occurredAt: { S: new Date().toISOString() },
},
// 同じ (streamId, version) のアイテムが存在しない場合のみ書き込む
ConditionExpression: 'attribute_not_exists(streamId)',
}))
// version=5 が既に存在すれば ConditionalCheckFailedException
// (= 楽観的並行性制御。PK存在チェックがSKと組み合わさって複合キーで効く)
向いているケース
- すでに AWS でサーバレス構成を採用している
- 専用 DB の運用負担を避けたい
- スパイクの大きいトラフィックでオートスケール優先
- Event Sourcing を「主軸」ではなく「特定境界文脈で」採用する
参考:Event-driven Architecture on AWS
6. 自作 PostgreSQL ベース ── 言語問わずの最小構成
概要
普通の PostgreSQL に events テーブルを1つ作って、自前で Event Sourcing を実装する アプローチ。実は多くのプロジェクトが、最初はこれで始める。
最小スキーマ
CREATE TABLE events (
global_position BIGSERIAL PRIMARY KEY, -- 全Stream横断の位置
stream_id TEXT NOT NULL, -- "screening-process-sp-001"
stream_version INTEGER NOT NULL, -- Stream内のバージョン
event_type TEXT NOT NULL,
payload JSONB NOT NULL,
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
occurred_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
UNIQUE (stream_id, stream_version) -- 楽観的並行性制御の核
);
CREATE INDEX idx_events_stream ON events(stream_id, stream_version);
CREATE INDEX idx_events_type ON events(event_type, global_position);
特徴
✅ 既存 PostgreSQL に乗せられる
追加 DB の運用なし、バックアップやレプリケーションは既存運用に統合
✅ 言語非依存
Node.js でも Python でも Go でも、SQL を書ける言語ならどれでも
✅ 仕組みが透明
「Event Sourcing がどう動いているか」が SQL レベルで見える
チームの学習教材としても優れる
✅ 段階導入しやすい
既存システムに events テーブルを追加するだけで導入できる
❌ サブスクリプション機構を自前実装
LISTEN/NOTIFY、Logical Replication、ポーリングなどから選んで実装
ここで品質差が出る
❌ Snapshot 戦略を自前実装
❌ 大規模化への備え
数億イベントになるとパーティショニング戦略が必要になる
Node.js/TypeScript エコシステムの選択肢
「自作」と言っても、TypeScript ではいくつか有力なライブラリがある。
- Emmett (event-driven-io)
KurrentDB と PostgreSQL の両方をバックエンドにできる ES ライブラリ。
軽量で関数的アプローチ、TypeScript ファースト。
- @ricofritzsche/eventstore
PostgreSQL + TypeScript で楽観的ロック・サブスクリプションをサポート。
DCB(タグベースのクエリ)の実装にも使われている。
- evtstore (Seikho)
Node.js / TypeScript の Event Sourcing & CQRS ライブラリ。
向いているケース
- 既に PostgreSQL を運用しており、追加 DB を増やしたくない
- 言語スタックが TypeScript / Python / Go など、JVM や .NET 以外
- Event Sourcing を「特定の境界文脈だけ」に部分導入したい
- チームに Event Sourcing の仕組みを浸透させる教材としても使いたい
比較表:6つの選択肢
| ツール | 言語/PF | バックエンド | DDD/CQRS統合 | サブスク | 自前実装の量 | ライセンス |
|---|---|---|---|---|---|---|
| KurrentDB | gRPC(多言語) | 専用 | △(ES特化) | ◎ | 少 | 商用+OSS |
| Axon | JVM | Axon Server / RDB | ◎(一気通貫) | ◎ | 最少 | OSS+商用 |
| Marten | .NET | PostgreSQL | △(ES特化) | ○ | 少 | MIT |
| Akka/Pekko | JVM/Scala | Cassandra/JDBC等 | ○(Actor) | ○ | 中 | Akka:BSL / Pekko:Apache2 |
| DynamoDB+EB | 多言語 | DynamoDB | × | ○ | 中〜多 | AWS従量 |
| PostgreSQL自作 | 多言語 | PostgreSQL | × | △(自前) | 最多 | OSS |
◎ = 強力 ○ = 一般的 △ = 限定的 × = なし
Event Sourcing と Event Streaming の違い(混同注意)
ここで一度、Kafka を Event Sourcing に使えるか という頻出の問いに触れておく。
Event Sourcing:
目的:「業務状態の真実をイベント列で表現する」
要件:永続的・任意時点読み取り・楽観的並行性制御・集約単位の原子追記
Event Streaming(Kafka, Pulsar など):
目的:「イベントを高速に流す」
要件:高スループット・順序保証(パーティション内)・パブサブ
Kafka を Event Sourcing の Event Store として使うのは、設計上ミスマッチが多い。
Kafka を Event Store にする際の問題:
1. Stream(特定集約のイベント)を直接読むのに最適化されていない
- 全パーティションをスキャンするか、1集約 = 1パーティションが必要
- 集約数が多いとパーティション数が破綻する
2. 楽観的並行性制御が標準では存在しない
3. 古いイベントの保持期間が無限ではない(compaction か永続保持の設定が必要)
正しい使い方は、Event Store にイベントが追記された後、Kafka で他のサービスに配信する という二段構成だ。Event Store と Event Streaming は「役割の異なる別物」と理解する。
graph LR
Cmd[書き込み] --> ES[("Event Store<br/>KurrentDB 等<br/>━━━━━<br/>業務状態の真実")]
ES --> Out[("Outbox")]
Out --> Kafka[("Kafka<br/>━━━━━<br/>イベント配信")]
Kafka --> S1["サービスA"]
Kafka --> S2["サービスB"]
Kafka --> S3["分析パイプライン"]
ES -.責務:永続化と整合性.- ES
Kafka -.責務:配信とスループット.- Kafka
style ES fill:#fff3e0,stroke:#e65100
style Kafka fill:#e8f5e9,stroke:#2e7d32
style Out fill:#f3e5f5
Event Store は「業務状態の真実」、Kafka は「イベントを高速に流すパイプライン」── この役割分担が崩れると、Kafka を Event Store にしようとして破綻する。
選定の判断軸
最後に、現場で実際に使える判断軸を3つの問いとしてまとめる。
flowchart TD
Start[Event Sourcing ツール選定] --> Q1{言語スタックは?}
Q1 -->|JVM| Q1a{DDD/CQRS一気通貫?}
Q1 -->|.NET| Marten[Marten]
Q1 -->|Node.js/<br/>多言語| Q2{専用DB追加運用<br/>を許容できる?}
Q1 -->|Scala/Actor志向| Akka[Akka or Pekko<br/>Persistence]
Q1a -->|Yes| Axon[Axon Framework]
Q1a -->|No / Actor志向| Akka
Q2 -->|Yes| Kurrent[KurrentDB]
Q2 -->|No| Q3{既存DB資産は?}
Q3 -->|PostgreSQL あり| Self[PostgreSQL自作<br/>+Emmett等ライブラリ]
Q3 -->|AWS中心| AWS[DynamoDB+EventBridge]
style Axon fill:#e3f2fd
style Marten fill:#e3f2fd
style Akka fill:#e3f2fd
style Kurrent fill:#e8f5e9
style Self fill:#fff3e0
style AWS fill:#fff3e0
問い1:言語スタックは何か?
JVM 中心 → Axon Framework か Akka/Pekko Persistence
.NET 中心 → Marten が第一候補
Node.js / TypeScript 中心 → KurrentDB(gRPC)か PostgreSQL 自作(Emmett等)
多言語マイクロサービス → KurrentDB(gRPC)か AWS DynamoDB+EB
問い2:「専用 DB を追加運用する」コストを払えるか?
払える → KurrentDB(最も完成度が高い専用 DB)
払えない → 既存 DB を活用
- PostgreSQL あり → Marten / 自作 PostgreSQL
- DynamoDB あり → AWS の組み合わせ
問い3:DDD/CQRS とのセット導入か、ES だけ部分導入か?
DDD/CQRS/ES 一気通貫で入れたい → Axon Framework
ES だけ部分導入から始めたい → Marten / 自作 PostgreSQL / KurrentDB
採用管理システムへの適用
題材としているシステムが TypeScript / Node.js で、既に PostgreSQL を運用している前提なら、現実的な選択肢は次の2つに絞られる。
選択肢A:自作 PostgreSQL(Emmett 等のライブラリ活用)
- 既存スタックに統合できる
- サブスクリプションは LISTEN/NOTIFY か Logical Replication で実装
- 中規模までは十分動く
選択肢B:KurrentDB(gRPC で接続)
- Event Sourcing の機能完成度が最高
- 専用 DB の運用負担を払う覚悟が要る
- 大規模・高スループット・複雑なサブスクリプションがあるなら有利
「まずは小さく始めて、必要になったら KurrentDB に移行する」が現実的な戦略だ。Event の表現自体は移行可能なので、最初の選択は不可逆ではない。
まとめ
Event Sourcing のツール選定は、3つの軸の掛け合わせで決まる:
1. 言語/プラットフォーム
2. 既存 DB 資産の活用 vs 専用 DB 採用
3. ES 単体 vs DDD/CQRS/ES 一気通貫
ツールが決まったら、次は 「どう設計し、どう運用するか」 だ。次章では、現場で効くベストプラクティスを集めて見ていく。イベントの命名・粒度から、スキーマ進化・PII 対応・テスト戦略まで、Event Sourcing を「動かす」だけでなく「育てる」ための実践知を整理する。