いつ読むか?

  • ポート&アダプターのリファレンス実装を探している
  • 一般的なポート&アダプターのパケージ構造を知る
  • アーキテクチャーを構成する要素の命名の参考にする

読書メモ

p. 115

そして、この問題に対する答えがインターフェイス分離の原則(Interface Segregation Principle)なのです。この原則が提唱していることは肥大化したインターフェイスを分割し、クライアント(ここではサービス)が必要とするメソッだけを提供するインターフェイスを定義すべきであるということです。もし、このインターフェイス分離の原則をサンプルのアプリケーションが持つ送信ポートに適用した場合、図7.3で示すような結果になります。

+-------------------------+  :  +--------------------+  :  +----------------------+
| application.domain      |  :  | application.port   |  :  | adapter.out          |
| .service                |  :  | .out               |  :  | .persistence         |
|                         |  :  |                    |  :  |                      |
|  +------------------+   |  :  |  <<interface>>     |  :  |                      |
|  | SendMoney        +---+--+->+  +--------------+  |  :  |                      |
|  | Service          |   |  :  |  | LoadAccount  +<--------+                     |
|  |                  +---+--+  |  | Port         |  |  :  |                      |
|  +------------------+   |  : \|  +--------------+  |  :  |  +---------------+  |
|                         |  :  |                    |  :  |  |               |  |
|                         |  :  |  <<interface>>     |  :  |  |  永続化       |  |
|  +------------------+   |  :  |  +--------------+  |  :  |  |  アダプタ     |  |
|  | RegisterAccount  +---+--+->+  | UpdateAccount+<--------+  |               |  |
|  | Service          |   |  :  |  | StatePort    |  |  :  |  |               |  |
|  +------------------+   |  :  |  +--------------+  |  :  |  +-------+-------+  |
|                         |  :  |                    |  :  |          |           |
|                         |  :  |  <<interface>>     |  :  |          |           |
|                         |  :  |  +--------------+  |  :  |          |           |
|                         |  :  |  | CreateAccount+<---------+        |           |
|                         |  :  |  | Port         |  |  :  |          |           |
|                         |  :  |  +--------------+  |  :  |          |           |
+-------------------------+  :  +--------------------+  :  +----------+-----------+

この例は out ポートだけだが、in ポートも同様。 例えば、in ポートのユースケース・インターフェースは1メソッドだけをもたせる。 そのアダプターの実装であるサービスは複数の in ポートを実装してもよい (つまり複数メソッドを持たせてもよい)。

p. 138

class SendMoneyServiceTest {

// このテストクラスで使うフィールドの宣言など

(略)

//送金が成功する場合
@Test
void transactionSucceeds() {
//Given(前提)
Account sourceAccount = givenSourceAccount(); // 送金元口座のモック
Account targetAccount = givenTargetAccount(); //送金先口座のモック

givenWithdrawalWillSucceed(sourceAccount);//引き出しが成功する設定にする
givenDepositWillSucceed(targetAccount);//預け入れが成功する設定にする

Money money = Money.of(500L);

SendMoneyCommand command = new SendMoneyCommand(
sourceAccount.getId(),

テストを書くとき準備ステップは、givenWithdrawalWillSucceed()givenDepositWillSucceed() のように、Given( / Wen / Then) の命名を使うと意図が伝わりやすくなると思う。

p. 178

図10.1 構成 (configuration) コンポーネントはどの層のクラスであってもオブジェクトを生成できるように、すべての層へのアクセスが可能となっている

+------------------------------------------------------------------+
|                          構成コンポーネント                          |
+------------------------------------------------------------------+
        |                                                |
        v                                                |
+-------+--------------------------------------------------------+  |
|       |                                                        |  |
|  +---------+   +-------+   +-----------+   +-------+          |  |
|  |         |   |       |   |           |   |       |          |  |
|  | Web     +-->+ 受信  +-->+ ユースケース+-->+ 送信  +--+       |  |
|  | アダプタ |   | ポート|   |           |   | ポート|  |       |  |
|  |         |   |       |   +-----+-----+   |       |  |  +---------+
|  +---------+   +-------+         |         +-------+  +->+ 永続化  |
|                                  v                       | アダプタ|
|                           +------+------+             +--+         |
|                           |  エンティティ |             |  +---------+
|                           +------+------+             |
|                                  ^                    |
|  +---------+   +-------+   +-----+-----+   +-------+  |  +---------+
|  | 外部    |   |       |   |           |   |       |  |  | 外部    |
|  | システム+-->+ 受信  +-->+ ユースケース+-->+ 送信  +--+  | システム|
|  | 用受信  |   | ポート|   |           |   | ポート|     | 用送信  |
|  | アダプタ|   |       |   +-----------+   |       |     | アダプタ|
|  +---------+   +-------+                   +-------+     +---------+
|                                                                |
+----------------------------------------------------------------+

構成コンポーネントが、Web アダプターであるコントローラーも生成する。 コントローラーにはユースケースが DI される。

p. 211

図12.2 アーキテクチャーの構成要素を意識したパッケージ構成

buckpal
+-- adapter
|   +-- in
|   |   \-- web
|   |       \-- o SendMoneyController
|   \-- out
|       \-- persistence
|           +-- o AccountPersistenceAdapter
|           \-- o SpringDataAccountRepository
+-- application
|   +-- domain
|   |   +-- model
|   |   |   +-- + Account
|   |   |   \-- + Activity
|   |   \-- service
|   |       \-- o SendMoneyService
|   \-- port
|       +-- in
|       |   \-- + SendMoneyUseCase
|       \-- out
|           \-- + UpdateAccountStatePort
\-- common
記号意味
+public
oパッケージプライベート

inout でパッケージを明確に分離している。 ポート&アダプター (ヘキサゴナルアーキテクチャー) を知っていれば、自然と理解しやすいパッケージ分割の命名だと思う。

  • アダプターの in ポートはコントローラーで、in ポートの実装であるユースケースを使用する
    • = Clean Architecture の Controllers (Interface Adapters)
  • アプリケーションの in ポートはユースケースの実装 (ここではサービス)
    • = Clean Architecture の Use Cases (Application Business Rules)