内部および外部インテグレーション
インテグレーションは、Lino が生成したアプリケーションを他のモジュール、内部サービス、サードパーティシステムに接続します。同期呼び出し、in-process インテグレーション、HTTP、イベントのどれを選ぶかは、結合度、レイテンシ、信頼性、データの所有権を考慮して判断します。
目的は単に別のシステムを呼び出すことではありません。誰がデータを提供し、誰が消費し、どの契約が公開され、障害時に何が起き、処理を即時に行う必要があるのか後で処理できるのか、という境界を宣言することです。
インテグレーションの作成
インテグレーションは、別のコンテキスト、内部サービス、または外部システムとの明示的な通信を表します。名前、契約、認証、timeout、エラー戦略、明確な責任を持つべきです。
lino integration new --name <ServiceName> lino integration list
別のインテグレーションを作成する前に、integration list で既存のインテグレーションを確認してください。CommonIntegration のような汎用的なインテグレーションは、明確な境界なしに責任を蓄積しやすいため避けます。
インテグレーションを作成する前に、それが解決する問題を定義します。外部レジストリの参照、請求の送信、サブスクリプションの検証、ユーザー同期、内部モジュールの消費、または別システムへのデータ公開などです。インテグレーションに具体的な意図がない場合、生成される契約になる準備はまだできていない可能性があります。
- 統合先コンテキストで名前を付ける: 汎用名ではなく
Billing、Identity、Catalog、Shippingを優先します。 - 契約と実装を分離する: ドメインとアプリケーションは、transport の詳細に直接依存すべきではありません。
- 外部障害を前提にする: すべてのリモートインテグレーションは、遅くなる、利用不可になる、部分的なエラーを返す、契約が変わる可能性があります。
インテグレーション resources
resources は、インテグレーションによって公開または消費されるオブジェクトを表します。例として customers、invoices、tenants、subscriptions、users、documents があります。
lino integration resource new --service <ServiceName> --module <ModuleName> --entity <EntityName> lino integration resource list --service <ServiceName> --module <ModuleName> --entity <EntityName>
インテグレーション resource は、ドメインエンティティと同じである必要はありません。多くの場合、データ交換の契約、外部ビュー、またはコンテキスト間通信に必要な最小表現です。エンティティ全体を外部契約にコピーすると、内部詳細が漏れ、進化のコストが増えがちです。
- 統合先システムの語彙で resource をモデル化します。
- 内部エンティティを外部契約として公開しません。
- どのフィールドが識別子、フィルター、返却データなのかを定義します。
- 存在する場合は、ページネーション、認証、呼び出し制限を文書化します。
resource が別モジュールのデータを表す場合、消費側に必要なものだけを含めます。同じ原則は shadow entities にも現れます。消費側モジュールは、提供側モジュールの完全なモデルに依存するのではなく、自身の Use Case に沿った小さなローカルコピーを保持します。
インテグレーション操作
操作は、インテグレーション resource で利用できるアクションを表します。作成、参照、更新、キャンセル、送信、同期、検証などです。
lino integration operation new --service <ServiceName> --module <ModuleName> --entity <EntityName> lino integration operation list --service <ServiceName> --module <ModuleName> --entity <EntityName>
各操作は、意図、入力、出力、呼び出し方法、エラー時の振る舞い、安全に再実行できるかどうかを定義する必要があります。
- クエリには timeout と利用不可時の処理が必要です。
- リモートコマンドは可能な限り冪等にします。
- ビジネスが非同期処理を受け入れる場合、外部障害で主トランザクションを壊すべきではありません。
- システム間の呼び出しを追跡するために、ログと correlation id を使用します。
冪等性は書き込み操作で特に重要です。外部システムがアクションを実行した後に試行が失敗した場合、再試行によって請求が重複したり、レコードが重複作成されたり、メッセージが二重送信されたりする可能性があります。可能な限り、冪等性キー、外部識別子、または文書化された再実行ルールを使用します。
インテグレーションの消費
インテグレーション、resource、操作をモデル化した後は、消費機能を使ってアプリケーションを生成された契約に接続します。目的は、HTTP 呼び出し、URL 組み立て、headers、レスポンス parsing、エラー処理を複数の handlers に分散させるのではなく、Use Case が予測可能な入力と出力を持つ名前付き抽象に依存するようにすることです。
lino integration consume
Use Cases は、コード中に散らばった HTTP の詳細ではなく、抽象に依存するべきです。これにより、テスト、実装の差し替え、障害処理の統一が容易になります。また、アプリケーションの各 endpoint が timeout、認証、correlation id、シリアライズ、エラーマッピングを別々に作り直すことも避けられます。
- 外部サービスを呼び出す前にデータを検証します。
- cancellation token と timeout を使用します。
- 外部レスポンスを予測可能な内部エラーへマッピングします。
- 変換せずに生の外部 payload をドメインルールとして保存しません。
HTTP vs. in-process
モジュールが一緒に動作する場合、in-process インテグレーションはシンプルで高速です。HTTP はプロセス間の契約をより明示的にしますが、レイテンシ、ネットワーク障害、レジリエンスの必要性を追加します。
in-process インテグレーションは、すべてへの自由なアクセスを意味してはいけません。同じ runtime 内でも、消費側は内部エンティティ、DbContext、または別モジュールの repository ではなく、契約を通じて通信するべきです。ネットワークコストは低くても、結合のリスクは残ります。
| シナリオ | 一般的な選択 | 注意点 |
|---|---|---|
| 同じモジュラーモノリス内のモジュール | In-process または内部契約 | 境界を保ち、エンティティと永続化の共有を避けます。 |
| 独自に deployment される別サービス | HTTP またはメッセージング | ネットワーク、認証、バージョニング、利用不可を扱います。 |
| 非同期で遅延を許容するプロセス | インテグレーションイベント | 冪等で追跡可能なコンシューマーを設計します。 |
HTTP は、producer と consumer が別々の runtimes を持つ場合、または境界を運用上明示したい場合に適しています。In-process は、同じアプリケーションがコンテキストをホストし、応答が即時に必要な場合に適しています。ただし、依存関係は契約として宣言され続ける必要があります。
イベント vs. 同期インテグレーション
現在の操作を完了するために応答が必要な場合は、同期インテグレーションを使用します。consumer が後で反応でき、eventual consistency が許容される場合はイベントを使用します。
この選択は、技術的な判断である前にビジネス判断です。支払いサービスが承認を確認した後でなければ注文を作成できない場合、同期呼び出しは Use Case の重要な一部になり得ます。注文作成が通知、プロジェクション更新、別モジュールへの同期を起動するだけでよい場合は、イベントの方が適していることが多いです。
| 必要性 | アプローチ | 技術上の注意 |
|---|---|---|
| 即時応答が必須 | 同期インテグレーション | Timeout、fallback、予測可能なエラー、安定した契約。 |
| 遅延を許容できる後続効果 | インテグレーションイベント | Outbox、冪等性、retries、相関ログ。 |
| 少量の外部データを頻繁に読む | Shadow Entity またはローカルプロジェクション | 同期、最終的な更新、最小モデル。 |
