03. 共通実装パターン
このドキュメントの目的
idp-serverで頻繁に使用する実装パターンを理解し、迷わず実装できるようになることが目標です。
所要時間
⏱️ 約20分
前提知識
パターン一覧
| パターン | 使用タイミング | 層 |
|---|---|---|
| Repository パターン | データアクセス | Adapter層 |
| Context Creator パターン | リクエスト変換 | Control Plane層 |
| Handler-Service パターン | ドメインロジック | Core層 |
| Plugin パターン | 拡張機能 | Core層・Extension層 |
| JsonConverter パターン | JSON変換 | 全層 |
1. Repository パターン
基本ルール
✅ 必須: Tenant第一引数
public interface ClientConfigurationQueryRepository {
// ✅ 正しい: Tenant第一引数
ClientConfiguration get(Tenant tenant, RequestedClientId clientId);
ClientConfiguration find(Tenant tenant, ClientIdentifier clientIdentifier);
List<ClientConfiguration> findList(Tenant tenant, int limit, int offset);
long findTotalCount(Tenant tenant);
}
// ❌ 例外: OrganizationRepositoryのみ(組織はテナントより上位)
public interface OrganizationQueryRepository {
Organization get(OrganizationIdentifier organizationIdentifier);
}
理由: マルチテナント分離を強制。テナント指定忘れでデータ漏洩を防ぐ。
重要: Optionalは使用しない。find()はNull Object Patternを採用(SomeModel.notFound()を返す)。
Query/Command分離
// Query Repository - 読み取り専用
public interface ClientConfigurationQueryRepository {
ClientConfiguration get(Tenant tenant, RequestedClientId clientId);
ClientConfiguration find(Tenant tenant, ClientIdentifier clientIdentifier); // Null Object Pattern
List<ClientConfiguration> findList(Tenant tenant, int limit, int offset);
long findTotalCount(Tenant tenant);
}
// Command Repository - 書き込み専用
public interface ClientConfigurationCommandRepository {
void register(Tenant tenant, ClientConfiguration configuration);
void update(Tenant tenant, ClientConfiguration configuration);
void delete(Tenant tenant, RequestedClientId clientId);
}
用途:
- Query: 読み取りトランザクション最適化(
@Transaction(readOnly = true)) - Command: 書き込みトランザクション(
@Transaction)
Null Object Pattern:
// find()はnullを返さない、空オブジェクトを返す
ClientConfiguration client = repository.find(tenant, clientIdentifier);
if (client.exists()) { // ドメインモデルのexists()メソッドで存在確認
// 処理
}