クイズの説明
Step 2で学んだヘキサゴナルアーキテクチャの理解度をチェックします。
- 全8問
- 合格ライン: 80%(7問正解)
- 不合格の場合は復習してから再挑戦してください
問題
Q1. ヘキサゴナルアーキテクチャにおけるDriving Port(入力ポート)の役割はどれですか?
- A) データベースに接続する方法を定義する
- B) アプリケーションが提供する機能(Use Case)を定義する
- C) HTTPレスポンスのフォーマットを定義する
- D) 外部APIの認証方法を定義する
答えを見る
正解: B
Driving Port(入力ポート)は、アプリケーションが外部に提供する機能を定義するインターフェースです。具体的にはUse Case単位でインターフェースを切り、CreateOrderUseCase、CancelOrderUseCaseのように、アプリケーションが「何ができるか」を表現します。
Q2. 以下のコードの問題点はどれですか?
// domain/entities/Order.ts
import { Column, Entity, PrimaryColumn } from 'typeorm';
@Entity()
class Order {
@PrimaryColumn()
id: string;
@Column()
status: string;
}
- A) クラス名がPascalCaseでない
- B) ドメインモデルがORMフレームワークに依存している
- C) プロパティがprivateでない
- D) コンストラクタが定義されていない
答えを見る
正解: B
ドメインモデルは外部ライブラリやフレームワークに依存してはいけません。TypeORMの@Entity()や@Column()デコレータがドメインモデルに侵入しているため、テスト時にTypeORMの接続が必要になり、ドメインの隔離が破れています。ドメインモデルは純粋なTypeScriptクラスとして実装すべきです。
Q3. Driven Portを設計する際の原則として正しいものはどれですか?
- A) 特定のデータベースのクエリ構文で定義する
- B) ドメインの言葉を使い、技術的詳細を含めない
- C) StripeGateway、PrismaRepositoryのように実装技術名を含める
- D) 可能な限り多くのメソッドを1つのPortにまとめる
答えを見る
正解: B
Driven Portはドメインの言葉でインターフェースを定義します。OrderRepository.findById()やPaymentGateway.charge()のように、ビジネスの概念で命名します。特定の技術名(Stripe、Prisma等)はAdapter側の実装の話であり、Portには含めません。
Q4. Driving Adapter(例: HTTPコントローラー)の責任として適切なのはどれですか?
- A) ビジネスルールのバリデーションを行う
- B) データベースに直接アクセスする
- C) HTTPリクエスト/レスポンスとCommand/DTOの変換を行う
- D) ドメインイベントを発行する
答えを見る
正解: C
Driving Adapterの責任は「変換」です。外部の入力(HTTPリクエスト)をUse Caseが理解できる形式(Command)に変換し、Use Caseの結果をHTTPレスポンスに変換します。ビジネスルールのバリデーションはドメインモデル、データベースアクセスはDriven Adapterの責任です。
Q5. テスト用のInMemoryRepositoryを使う主な利点はどれですか?
- A) 本番環境で高速にデータを保存できる
- B) データベース接続なしでUse Caseのテストが実行できる
- C) SQLのパフォーマンスチューニングが不要になる
- D) データの永続化が自動的に行われる
答えを見る
正解: B
InMemoryRepositoryはDriven Port(OrderRepository等)のインメモリ実装です。Map等のデータ構造を使ってメモリ上にデータを保持するため、データベース接続が不要です。これにより、Use Caseのテストが高速に実行でき、外部依存なしにビジネスロジックを検証できます。
Q6. 以下のディレクトリ構造で、依存関係として正しい記述はどれですか?
src/
├── domain/
│ ├── entities/Order.ts
│ └── ports/out/OrderRepository.ts (interface)
├── application/
│ └── CreateOrderUseCaseImpl.ts
└── adapters/
└── out/PrismaOrderRepository.ts (implements OrderRepository)
- A) domain → adapters
- B) application → adapters
- C) adapters → domain
- D) domain → application
答えを見る
正解: C
adapters層のPrismaOrderRepositoryは、domain層のOrderRepositoryインターフェースを実装(implements)しています。つまりadaptersがdomainに依存しています。applicationもdomainのPortに依存します。domainは最も内側の層であり、外側の層に一切依存しません。
Q7. 以下のコードで、ヘキサゴナルアーキテクチャの原則に違反している箇所はどこですか?
class CreateOrderUseCaseImpl implements CreateOrderUseCase {
constructor(
private orderRepo: PrismaOrderRepository, // (1)
private stripe: Stripe // (2)
) {}
async execute(command: CreateOrderCommand): Promise<OrderId> {
const order = Order.create(command.customerId, command.items);
await this.orderRepo.save(order);
await this.stripe.paymentIntents.create({ // (3)
amount: order.totalAmount.toCents(),
});
return order.id;
}
}
- A) (1)のみ
- B) (1)と(2)
- C) (1)と(2)と(3)
- D) (3)のみ
答えを見る
正解: C
(1) Use CaseがPrismaOrderRepository(具象クラス)に依存しており、OrderRepository(インターフェース)に依存すべきです。(2) Stripe(外部ライブラリの具象クラス)に直接依存しており、PaymentGateway(Port)に依存すべきです。(3) Stripeの具体的なAPIを直接呼び出しており、PaymentGatewayのメソッドを通じて呼び出すべきです。全箇所が原則に違反しています。
Q8. Value Objectの設計原則として正しいものはどれですか?
- A) 可変(mutable)で、自由に値を変更できる
- B) 不変(immutable)で、自己検証を行い、等値比較で比較する
- C) 必ずデータベースのIDを持つ
- D) 外部APIの型定義をそのまま使う
答えを見る
正解: B
Value Object(値オブジェクト)は不変であり、コンストラクタまたはファクトリメソッドで自己検証を行います。同一性はIDではなく値で比較します(例: Money(100, ‘JPY’) === Money(100, ‘JPY’))。外部ライブラリの型に依存せず、ドメインの概念を純粋に表現します。
結果
7問以上正解の場合
合格です。おめでとうございます。
Step 2「ヘキサゴナルアーキテクチャ」を完了しました。 次は Step 3「クリーンアーキテクチャ」に進みましょう。
6問以下の場合
もう少し復習しましょう。
| 問題 | 復習セクション |
|---|---|
| Q1 | step2_1 Ports & Adaptersの概念 |
| Q2 | step2_2 ドメインモデルの隔離 |
| Q3 | step2_3 Port(インターフェース)の設計 |
| Q4 | step2_4 Adapter(実装)の設計 |
| Q5 | step2_4 テスト用Adapter |
| Q6 | step2_1 / step2_4 依存の方向 |
| Q7 | step2_1 / step2_3 PortとAdapterの関係 |
| Q8 | step2_2 Value Object |
Step 2 完了
お疲れさまでした。
学んだこと
- Ports & Adaptersの概念(Driving/Driven)
- ドメインモデルの隔離(外部依存の排除)
- Portの設計(1 Use Case = 1 Port、ドメインの言葉)
- Adapterの設計(変換のみ、テスト用InMemory)
次のステップ
Step 3: クリーンアーキテクチャ(3時間)
ヘキサゴナルの考え方をさらに発展させた、クリーンアーキテクチャの4層構造を学びます。
推定所要時間: 30分