アプリケーションのユースケース定義
このセクションでは、Lino CLI を使用してアプリケーションのユースケースを定義する方法を探ります。ユースケースは、ユーザーや外部システムとアプリケーション間の特定のやり取りを表し、ビジネスルールをカプセル化し、ドメイン駆動のアーキテクチャを促進します。CQRS パターンによる読み取り操作と書き込み操作の分離、そして Result パターンを使用したレスポンスの標準化について説明します。
CQRS(Command Query Responsibility Segregation)パターンは、アプリケーションの状態を変更する操作(Commands)と、データを単に取得する操作(Queries)を分離することを提案します。このアプローチにより、各操作タイプを個別に最適化でき、スケーラビリティ、パフォーマンス、システムの保守性が向上します。
Result パターンは、操作の戻り値を標準化するために使用され、成功または失敗、エラーメッセージ、結果データに関する情報をカプセル化します。これにより、アプリケーションのさまざまな層で一貫したレスポンス処理が可能になります。
Commands と Queries のファイル構成、データのバリデーション、Handlers におけるドメインロジックの適用、そして標準化された結果の返却方法について解説します。これらはすべて、Lino CLI による自動生成を通じて実現されます。
注意: 必須ではありませんが、現在 Lino はアプリケーション層で CQRS パターンを適用するための 2 つのオプションを提供しています。1 つは Jimmy Bogard による MediatR
ライブラリ、もう 1 つは Martin Othamar による Mediator
ライブラリです。
ユースケースの概要
ユースケースは、ユーザーや外部システムとアプリケーション間の完全なやり取りを表し、特定のビジネスシナリオを説明します。Linoでは、各ユースケースは次のように分かれています:
- コマンド(Command):システムの状態を変更する意図(作成、更新、削除など)を表します。
- クエリ(Query):ドメインの状態を変更せずにデータを取得する意図を表します。
この分離により、コードの明確性が高まり、テストが容易になり、読み取りと書き込みの独立したスケーラビリティが可能となり、クリーンアーキテクチャの原則や ドメイン駆動設計(DDD)のベストプラクティスに沿います。
コマンド(Commands)
コマンド(Command)は、システムの状態を変更するアクションを実行するために必要なデータのみを含む不変のメッセージです(例:CreateInvoice
、DeactivateUser
)。
コマンドには、操作を実行するために必要な本質的な情報だけを含めるべきです。
コマンドの特徴
- 不変性:
record
または公開のsetterを持たないget
のみのクラスとして実装されます。 - 命令形の名前:実行されるアクションを反映し、例として
CreateOrder
、UpdateCustomerAddress
などがあります。 - 最小限のデータ:操作を実行するために必要なフィールドのみを含み、大量のデータを返しません。
- 分離された検証:各コマンドは独自の検証ルールを持ち、ハンドラー(Handler)に到達する前に一貫性が保証されます。
コマンドバリデーター(Command Validators)
コマンドバリデーターは、コマンドが適切に構成されているか、ビジネス要件を満たしているかをハンドラーに渡す前に検証します。Linoでは.NETプロジェクトで広く使われ、ルール作成にFluent APIを提供するFluentValidationライブラリを使用しています。
一般的な検証ルール
NotEmpty
,NotNull
- 必須フィールド用InclusiveBetween
- 数値範囲用(例:金額、最小/最大数量)MaximumLength
,MinimumLength
- 文字列長用RuleForEach
- コレクション(List<T>
)の要素検証用Must
- カスタムルール用(例:ドキュメント形式、日付検証)
コマンドハンドラー(Command Handlers)
コマンドハンドラーは、そのコマンドに関連するドメインロジックを実行する責任があります。リポジトリ、ユニットオブワーク(IUnitOfWork
)、補助サービスを調整し、必要に応じてドメインイベントを発行します。
ハンドラー実装のパターン
- 依存性注入を用いてリポジトリ、外部サービス、UnitOfWorkなどの依存関係を受け取ります。
- ドメインエンティティをマッピングしインスタンス化して初期の一貫性を保証します。
- ビジネスルールを適用(追加の検証、値の計算、ドメインイベントの発行)
IUnitOfWork
やリポジトリを介して変更を永続化します。- 成功または失敗を示すコマンド結果(Command Result)(
Result<T>
)を返します。
コマンド結果とResultパターン
コマンド結果は、呼び出し元(例:APIやフロントエンド)が次の処理を続行するために必要な最小限のデータだけを含むシンプルなDTOであるべきです。一般的には、作成または更新されたエンティティの識別子(Id
)や、失敗時にはエラーに関する標準化された情報を含みます。
コマンドが失敗する可能性があるシナリオでは、Resultパターンを使用します。これは操作の結果を成功か失敗かでカプセル化する抽象化で、.NETではResult<T>
のような型(または類似のライブラリ)が一般的に使われ、以下の特徴があります:
- 成功時に
Value
を設定可能(例:シンプルなDTO) - 失敗時には標準化されたコードやメッセージ(
Error
)を保持し、HTTPコードや検証の詳細などのメタデータも含められます。 - API全体のエラーフローを統合し、層間での一貫性を維持します。
CLIによるコマンドの作成
Linoでは、以下のコマンドを使って新しいコマンドに必要なすべてのアーティファクトを簡単に生成できます:
lino command new
このコマンドを実行すると、インタラクティブなウィザードが以下の内容を尋ねます:
- サービス — コマンドを作成するサービス名
- モジュール — モジュール化されたサービスの場合、対象モジュールの指定
- エンティティ — コマンドに関連するドメインエンティティ(任意ですが推奨)
- コマンド名 — 最終的なコマンド名(例:
CreateOrder
)
確認後、Linoは以下のファイルを自動的に作成します:
CreateOrderCommand.cs
CreateOrderCommandValidator.cs
CreateOrderCommandHandler.cs
CreateOrderCommandResult.cs
生成された構造の例
CreatePerson
コマンドを例にとると、生成される構造は次のようになります:
MyApp/ └── src/ └── Services/ └── MyService/ └── Application/ ├── MyApp.MyService.Application.csproj └── UseCases/ └── People/ ├── Commands/ │ └── CreatePerson/ │ ├── CreatePersonCommand.cs │ ├── CreatePersonCommandValidator.cs │ ├── CreatePersonCommandHandler.cs │ └── CreatePersonCommandResult.cs └── Queries/ └── ...
クエリ(Queries)
クエリは、ドメインの状態を変更することなくデータを取得する意図を表します。クエリは読み取り効率に優れており、必要なフィールドのみを返し、要求されていない限りエンティティ全体をロードしません。
クエリの特徴
- 不変 — コマンドと同様に、クエリは作成後に変更されるべきではありません。
- 説明的な名前 — 取得する情報を明確に表す名前(例:
GetCustomerById
、ListOrdersByDateRange
)。 - フィルターとページング — 日付、ステータス、ページ番号、並び順など、特定の検索条件に対応するパラメータを受け取ることができます。
- プロジェクション — 完全なドメインオブジェクトを読み込むことなく、必要なフィールドのみを含むDTOやビューモデルを返すべきです。
クエリバリデーター
クエリバリデーターは、フィルターやページングの値(page、pageSize)、セキュリティルール(ユーザーの権限、データの可視性)など、入力パラメータの検証を担当します。FluentValidationを使用して実装されます。
クエリにおける一般的なバリデーションルール
GreaterThanOrEqualTo
またはLessThanOrEqualTo
— (例:日付などの範囲フィルター)。Length
— 名前やメールアドレスなどの文字列フィルターに使用。InclusiveBetween
— ページごとのアイテム数など、ページングに関するパラメータに使用。
クエリハンドラー
クエリハンドラーは、リポジトリやデータベースコンテキストをクエリし、最適化されたプロジェクションを返します。ドメインエンティティを完全に読み込むのは避けます。
Linoでは、.Select()
のようなメソッドを使って、データを期待されるDTO形式に直接マッピングするプロジェクションの使用が推奨されており、効率性とパフォーマンス向上に寄与します。
クエリ結果
Linoでは、クエリの結果は常にQueryResult
というサフィックスが付いたrecordで表されます。
この命名規則は、レスポンスタイプがアイテムのリスト(ページングの有無を問わず)でも、単一の詳細アイテムでも適用されます。
CLIでクエリを作成する
コマンドと同様に、Linoは以下のコマンドを提供しています:
lino query new
ウィザードでは以下が求められます:
- サービス — クエリを作成する対象のサービス。
- モジュール — 該当する場合は、ドメインモジュールを指定。
- エンティティ — クエリに関連付けられるエンティティまたはアグリゲート(任意)。
- クエリ名 — 例:
GetOrderById
またはListProductsByCategory
。
Linoは以下のファイルを自動生成します:
GetOrderByIdQuery.cs
GetOrderByIdQueryValidator.cs
GetOrderByIdQueryHandler.cs
GetOrderByIdQueryResult.cs
クエリ用に生成された構造の例
MyApp/ └── src/ └── Services/ └── MyService/ └── Application/ ├── MyApp.MyService.Application.csproj └── UseCases/ └── Orders/ ├── Commands/ | └── ... └── Queries/ └── GetOrderById/ ├── GetOrderByIdQuery.cs ├── GetOrderByIdQueryValidator.cs ├── GetOrderByIdQueryHandler.cs └── GetOrderByIdQueryResult.cs
結論
LinoでUse Cases(CommandsおよびQueries)を定義し構築する方法を理解した後、書き込みと読み取りのロジックを明確に分離して、堅牢でスケーラブルなビジネスシナリオを作成する準備が整いました。 開発を加速するためにCLIを使用してください。ただし、ドメインの特定のニーズに応じて実装を常に見直し、調整することを忘れないでください。
次に、データ永続性、ドメインイベント、インフラサービスの取り扱い方法を学び、アプリケーション全体のアーキテクチャを構築しましょう。