blog.bouzuya.net

2021-09-01 Clean Architecture での Port による依存の方向のごまかし

Ports and Adapters の気持ちで Clean Architecture の同心円の図を眺めたら Use Cases 層からの依存が両方向になった話のメモを書く。まとまっていないのでそのうち同じようなことを書く気がする。

Twitter は https://twitter.com/bouzuya/status/1433056261273051140 から。

いつものように rust365 で bouzuya/rust-sandbox の stamp-rally をつくろうとしていた。どうもディレクトリ・ crates の構成がしっくりこなかったことがはじまり。

最初は clean architecture をもとに 4 層にしていた。

  • entity
  • use case
  • adapter
  • driver

気になる点はいくつかある。 driver にいまひとつ存在感がない。 adapter は過剰に詰め込まれている。 main をどう置くべきか迷う。 main は adapter を使うのだろうか……。 adapter が main みたいになってしまってそれも妙だ。 use case を束ねるような抽象的な app を置いてそこに adapter を注入して……などと考えてみたものの app と呼ぶものの外側に adapter があるのも妙だ。すべてを組み合わせたものを指して app だと思う。

  • entity
  • use case
  • adapter
  • app

driver を削って app を導入した。 app は他のすべてに依存して組み立てる。まだ実際に試していないのでモヤッとしているけど良さそうな感じがする。

まだ気になる点がある。それは port の存在だ。 adapter は Ports and Adapters から導入された用語なのだろうけど adapter があるのに port がない。これは Clean Architecture の書籍によると use case の中に Input Boundary や Output Boundary や Data Access Interface として導入する例が挙げられている。よく使われていそうなもので言うと Repository インタフェースみたいなものが port だ。 Repository インタフェースの実装が adapter だ。

use case の Input Boundary が必要なのかどうかは議論の余地がある。 Clean Architecture の図にあるような外から内への依存を実現するだけなら Input Boundary は不要だ。直接 use case に依存すればいいからだ。依存関係逆転の原則 (DIP) によって導入される port は依存の方向を一定にするために必要だ。依存の方向に問題を引き起こさない依存について Input Boundary (Input Port) は導入すべきか怪しい。

試しに port を入れてみたところ次のような依存関係ができた。

(external)
|
| (input)
v
adapter
|
| (use)
v
port ... Input Boundary
^
| (impl)
|
use case
|
| (use)
v
entity
^
| (use)
|
use case ... Output Boundary
|
| (use)
v
port
^
| (impl)
|
adapter
|
| (output)
v
(external)

同心円の中心である entity に向けて内側の依存ができると思いきや途中の port の箇所で反転している。 use case はそれぞれ entity (同心円の内側) と port (同心円の外側) に依存している。

当然と言えば当然で DIP によって逆転されていた向きが別の層として切り離すことで再び表面化されただけだ。

Clean Architecture は依存の向きを内側にする点を重視している。これはおそらくわかりやすさのためであると思う。実際には内側にするために DIP を使って逆転している。 port を use case の層にねじこむことでごまかしている。別にそれが悪いと言いたいわけではない。

port という層を設けて use case を両側に依存させることでそこから内側の領域 (ドメイン) を詳細・環境から切り離せるわけだ。それによるメリットがフレームワーク依存から脱したりテスタビリティを上げたりにつながる……と。

中途半端だけど時間がないのでここまで。


今日のコミット。