AIコーディング革命に備えるシステム設計:イベントソーシング×アクターモデルで実現する後悔なきアーキテクチャ
はじめに
システム開発において「後悔」は、大きく2つの側面から生じます。1つは、データの状態変更に関する後悔です。人間が書いたコードでもAIが生成したコードでも、意図しない変更や誤ったプログラムによってデータが変更されてしまった場合、その修正や回復が困難となります。もう1つは、アーキテクチャの選択に関する後悔です。安易な初期実装によるスケーラビリティの限界や、逆に過剰な初期投資による非効率な運用など、アーキテクチャの決定は長期的な影響を及ぼします。
AIによるプログラミングが一般化しつつある現代において、これらの課題はより一層重要性を増しています。AIは高速で大量のコードを生成できる一方で、その変更が予期せぬ影響を及ぼす可能性があり、また急速なビジネスの成長にも対応できる柔軟性が求められます。
本記事では、イベントソーシングとアクターモデルという2つのパターンを組み合わせることで、これら両方の「後悔」を防ぐアーキテクチャの実現方法を解説します:
-
イベントソーシング:
- データの変更履歴を完全に保持
- 意図しない変更の追跡と復旧を可能に
- AIと人間のコード変更を透過的に管理
-
アクターモデル:
- 最小限の初期投資で開始可能(フレームワークによる)
- 需要に応じたリニアなスケール
- アーキテクチャの根本的な変更なしに成長
AIプログラミング時代の課題
デバッグ可能性の重要性
AIが生成したコードは、人間が書いたコードと比べて以下のような特徴があります:
- コードの生成速度が速い
- 大量のコードを短時間で生成できる
- 生成過程の意図が不明確になりやすい
このような特徴から、システムの変更履歴や操作の文脈を詳細に記録することが重要になります。
後悔しないためのシステム設計
従来のステート中心のアプローチでは、一度データが変更や削除されると、その変更を追跡したり元に戻したりすることが困難です。特にAIが生成したコードによる予期せぬ変更が発生した場合、深刻な問題となる可能性があります。
イベントソーシングを採用することで、以下のような「後悔しない」システム設計が可能になります:
- データの変更を永続的なイベントとして記録するため、何も失われることがない
- AIが生成したコードによる意図しない変更も、すべて追跡可能
- 問題が発見された場合でも、任意の時点の状態に正確に戻すことが可能
- 人間のレビューが漏れた変更でも、後から発見・修正・巻き戻しが可能
これらの特徴により、AIを活用した高速な開発においても、以下のような安全性を確保できます:
- 開発スピードを維持したまま、完全なデータの追跡可能性を確保
- AIによる変更の影響を完全に可視化し、リスクを最小化
- 人間による事後確認と修正を、いつでも確実に実施可能
イベントソーシングの価値
イベントソーシングは、基本機能、コンテキストの保持、そして予期せぬ分析価値という3つの側面から、システムに大きな価値をもたらします。以下では、それぞれの側面について詳しく説明します。
基本機能 - システムの信頼性を支える3つの柱
イベントソーシングは、システムの信頼性と回復力を支える以下の3つの重要な機能を提供します:
-
完全な変更履歴
- すべての変更をイベントとして永続的に記録
- データの削除や上書きを行わない履歴保持
- 任意の時点の状態を正確に再現可能
-
監査と追跡
- AIによる変更を含むすべての操作を追跡
- 完全な監査証跡の自動生成
- 変更の意図と結果を明確に記録
-
高度な回復性
- データの不整合が発見された場合の状態復元
- 人間のレビューが漏れた変更の事後修正
- システム全体の信頼性向上
コンテキストの保持
イベントソーシングにより、以下のような情報を自然に保持できます:
- 変更の意図と理由
- 変更時の環境情報
- 関連する業務ルール
- ユーザーやシステムの状態
予期せぬ分析価値の発見
イベントソーシングの重要な利点の1つは、イベント記録時には意識していなかった情報が、後から貴重な分析資産となることです。
例えば、ECサイトのショッピングカートを考えてみましょう:
-
従来の方式:
- カートの現在の状態のみを保存
- アイテムの追加・削除で以前の状態は上書き
- ユーザーの関心や迷いの履歴は失われる
-
イベントソーシング:
- カートへの追加・削除をすべてイベントとして保存
- ユーザーが「悩んだ」商品を特定可能
- 過去データを活用した商品レコメンデーション機能の実装が可能
この特徴により、以下のような事後的な分析や機能追加が可能になります:
- 頻繁に検討されるが最終的に購入されない商品の特定
- ユーザーの興味・関心の変遷パターンの分析
- より的確なレコメンデーションエンジンの開発
従来のログやヒストリー機能では、機能実装後のデータしか取得できませんが、イベントソーシングでは過去に遡って完全な行動履歴を分析できます。
アクターモデルの重要性
アクターモデルは、イベントソーシングを補完し、分散環境におけるスケーラビリティと信頼性を提供します。以下では、現代のシステム開発における主要な課題への対応方法を説明します。
リニアスケーラビリティの実現 - シングルサーバーからグローバル規模へ
アクターモデルは、アーキテクチャの根本的な変更を必要とせず、自然なシステムの成長を実現する段階的なスケーリングを可能にします:
-
初期段階:
- 最小限のリソースで開始可能
- 単一サーバーでの効率的な運用
- 投資対効果の高いスタート
-
成長段階:
- サーバー追加による直線的な性能向上
- 必要な部分のみのスケールアウト
- インフラコストと性能の最適なバランス
-
大規模システム:
- 数百台規模までのリニアなスケーリング
- アーキテクチャの根本的な変更不要
- コンポーネント単位での柔軟な拡張
この特性により、「スケーラビリティの壁」に直面することなく、また過剰な初期投資も避けながら、ビジネスの成長に合わせた自然な拡張が可能になります。
イベントソーシングとの相乗効果
アクターモデルとイベントソーシングの組み合わせにより、以下のような利点が得られます:
- イベントの処理を並列化し、高速な処理を実現
- 各アクターが独立してイベントを処理することで、状態の整合性を自然に保持
- システムの複雑性を管理可能な範囲に抑制
- AIが生成したコードの影響をアクター単位で分離し、安全性を向上
- スケーラブルなイベントストリーム処理を実現
具体的なソリューション:Sekiban.Pure.Orleans
特徴と利点
私たちが開発した Sekiban.Pure.Orleans は、イベントソーシングとアクターモデルを組み合わせたアーキテクチャソリューションです。
初期バージョンのSekiban.Coreはシングルサーバー対応のソリューションとして開発され、多くのプロジェクトで活用されてきました。しかし、スケールアウト時のデータ整合性という課題に直面し、その解決策としてアクターモデルに着目。Microsoft Orleansを採用することで、分散環境でも高い整合性を保ちながらスケーラブルな運用を実現しました。
主な特徴:
- C#のモダンな記法サポート
- Microsoft Orleansの仮想アクターモデルによる分散処理
- イベントソーシングの簡単な実装
- 低コストでの運用が可能
- テンプレートによる迅速なプロジェクト開始
2024年初頭にプレビュー版をリリースし、以下の利点を提供しています:
- 分散環境での確実なデータ整合性
- スケールアウト時の信頼性
- シンプルな開発者体験
C#環境設定の後に以下の2行を実行するだけで、すでにコマンド、イベント、集約を含んだサンプルプロジェクトをダウンロードして、開発を開始することができます:
dotnet new install OrleansSekiban.Template
dotnet new sekiban-orleans -n MySampleCode
サンプルプロジェクトは、.NET Aspireで構成されていて、postgresのパスワードを設定して実行するだけで、以下のコンポーネントを含む完全なローカル開発環境が構築されます:
- アクターのクラスタストレージ
- ステートの永続化ストレージ
- イベントストア
- ApiService
- Blazorによるフロントエンドプロジェクト
コスト効率
初期コストを抑えた構成例:
以下のAzureサービスを組み合わせることで、月額1万円以内でのスタートが可能です:
- Azure App Service B1 (1.75GBメモリ, 1コア)
- Cosmos DB Serverless(アクターステートとイベントストア用)
- Azure Table Storage(プロジェクションデータ用)
- Azure Blob Storage(大容量データ保存用)
この構成により:
- スケーラビリティ:需要に応じて柔軟に拡張可能
- 運用コスト:Serverlessによる使用量ベースの課金
- 初期投資:最小限のリソースから開始可能
将来的な成長に合わせて、必要なコンポーネントのみをスケールアップすることができます。
実装例
サンプルプロジェクトには、以下のような実用的な実装例が含まれています。
集約ルート
// 天気予報の集約ルート
public record WeatherForecast(
string? Summary,
int TemperatureC,
DateTime Date) : IAggregatePayload<WeatherForecast>
{
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
public static WeatherForecast Create(WeatherForecastInputted ev) =>
new(ev.Summary, ev.TemperatureC, ev.Date);
}
イベントの定義
// 天気予報入力イベント
public record WeatherForecastInputted(
string Summary,
int TemperatureC,
DateTime Date) : IEventPayload;
コマンドとハンドラー
namespace MySampleCode.Domain;
// コマンドとハンドラーを1ファイルで定義
public record InputWeatherForecastCommand(string Summary, int TemperatureC, DateTime Date) : ICommand<WeatherForecast>
{
public PartitionKeys SpecifyPartitionKeys(InputWeatherForecastCommand command) =>
PartitionKeys.Generate<WeatherForecastProjector>();
public ResultBox<EventOrNone> Handle(InputWeatherForecastCommand command, ICommandContext<IAggregatePayload> context)
=> EventOrNone.Event(new WeatherForecastInputted(command.Location, command.Date,command.TemperatureC, command.Summary));
}
プロジェクター(読み取りモデル)
// 天気予報のプロジェクター
public class WeatherForecastProjector :
IProjector<WeatherForecastProjector, WeatherForecastProjection>
{
public required string Summary { get; init; }
public required int TemperatureC { get; init; }
public required DateTime Date { get; init; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
// イベントからプロジェクションを更新
public static WeatherForecastProjector OnEvent(WeatherForecastInputted ev) =>
new()
{
Summary = ev.Summary,
TemperatureC = ev.TemperatureC,
Date = ev.Date
};
}
クエリー
// 天気予報のクエリー定義
public record GetWeatherForecastsQuery() : IQuery<List<WeatherForecastProjector>>;
// クエリーハンドラー
public class GetWeatherForecastsQueryHandler :
IQueryHandler<GetWeatherForecastsQuery, List<WeatherForecastProjector>>
{
private readonly IProjectionRepository _repository;
public GetWeatherForecastsQueryHandler(IProjectionRepository repository) =>
_repository = repository;
public async Task<List<WeatherForecastProjector>> Handle(
GetWeatherForecastsQuery query,
CancellationToken cancellationToken = default) =>
await _repository.Lists<WeatherForecastProjector>();
}
このサンプルコードは、イベントソーシングとアクターモデルの主要な概念を実践的に示しています:
- 集約ルートによる状態管理
- イベントによる変更の記録
- コマンドとハンドラーによる操作
- プロジェクターによる読み取りモデルの提供
- クエリーによるデータ取得
まとめ:「後悔しない」アーキテクチャの実現
イベントソーシングとアクターモデルの組み合わせは、AIプログラミング時代における「後悔しない」システム構築を可能にする理想的なアーキテクチャです:
-
完全な安全性と回復力
- AIが生成したコードの影響を完全に把握可能
- どんな変更も履歴として永続的に保持
- 問題発生時は任意の時点に正確に復元可能
-
迅速な開発と確実な品質
- AIの高速な開発能力を最大限に活用
- 問題の早期発見と確実な解決
- 人間とAIの理想的な協調を実現
-
将来を見据えた拡張性
- ビジネスの成長に合わせて自然に拡張
- 並列処理による卓越したパフォーマンス
- 効率的な運用コストで持続可能な運用
Sekiban.Pure.Orleans
は、これらの「後悔しない」アーキテクチャの利点を、簡単かつ低コストで実現できる実用的なソリューションです。特にスタートアップや中小企業にとって、将来の成長を見据えた理想的な選択となります。イベントソーシングとアクターモデルの採用により、AIと人間が安全に協調しながら、取り返しのつかない変更を心配することなく、革新的なシステムを構築することができます。
Discussion