🐕

アーキテクチャを意識していますか?

に公開

背景

サークルで開発をしている際に、アプリケーションの核に近い箇所を修正しようとすると、それに伴っていろんな箇所で修正が発生する影響で、どこを修正したらいいか分からないということがしばしば発生していたため、保守性をあげたかった。また、アーキテクチャについて細かく解説しているサイトがあまりなかったため、この記事を書いてみようと思った。

注意

この記事は全くの初学者向けです。
また、この記事はヘキサゴナルアーキテクチャをベースとして話を進めていきます。
自分も全然まだまだ勉強中なため、間違っている情報があるかも知れません。その点はご了承ください。

参考

アーキテクチャを学習するために、以下の本を読み、この本を参考にspringbootですごく簡単なプロジェクトを作り理解を深めました。
詳しく学習したい方は、是非ご購入してください。(アフィリエイトではありません。)
https://www.amazon.co.jp/手を動かしてわかるクリーンアーキテクチャ-ヘキサゴナルアーキテクチャによるクリーンなアプリケーション開発-Tom-Hombergs/dp/429501978X

アーキテクチャとは?

アーキテクチャとは、プロジェクト全体の構成のことです。
具体的には、RestAPIでのPOSTリクエストを例に挙げると、
HTTPリクエスト→JSONパース→処理→結果を返す
みたいな流れだと思います。(すごくざっくりですが)
このように、一個の処理をするだけでも、さまざまなプロセスを経ていることがわかると思います。
これを役割ごとに分けた構成がアーキテクチャです。

アーキテクチャを意識する意義

アーキテクチャを意識することで、主に二つの恩恵を受けることができます。

  1. 保守容易性の向上
  2. テストのしやすさ

具体的なことは後で説明します。一旦こんなメリットを得ることができます。

アーキテクチャの種類

自分もまだまだ一部しか知らないかも知れませんが、代表的なアーキテクチャは大体以下の通りだと思います

  • ヘキサゴナルアーキテクチャ
  • クリーンアーキテクチャ
  • オニオンアーキテクチャ
  • レイヤードアーキテクチャ(多層アーキテクチャ)

なんか色々よく分からない横文字が並んでいますが、個人的には、上3つはほぼ同じです。あくまで個人的な意見です。
細かい呼び名や粒度は異なりますが、概念自体に大きな違いはないです。
明確な違いがあるのは、レイヤードアーキテクチャ(多層アーキテクチャ)です。
レイヤードアーキテクチャは古典的なアーキテクチャで、ほぼ大半のプロジェクトのアーキテクチャはこれだと思います。
なぜ上三つがほぼ同じで、レイヤードアーキテクチャだけが明確に違うのかどうかは後で説明します。

責務分けや層について

主に、一番外側から、インフラ層、アプリケーション層、ドメイン層、というような三つの層があります。(ヘキサゴナルアーキテクチャの話です)
ヘキサゴナルアーキテクチャではざっくりと以下の図のようになっています。

よく六角形で表されますが、六角形である意味は特にありません。
とりあえずこれを念頭においておいてください。

そして、それぞれの層の責務は以下の通りです。

責務
インフラ コントローラや永続化等の外部アダプタ
アプリケーション 内部ポート等
ドメイン エンティティ等

まず、インフラ層に関してですが、インフラ層はコントローラや永続化等の外部アダプタと呼ばれる役割を担います。先ほどの図を見てわかる通り、アプリケーションの外部に配置されるものです。

次に、アプリケーション層に関してですが、アプリケーション層は、内部ポートを担います。アプリケーション層は一般的には、ユースケースを担うと言われていますが、ヘキサゴナルアーキテクチャではユースケースは受信ポートとして振る舞うため、ざっくり内部ポートを担うと言って差し支えないです。

最後に、ドメイン層に関してですが、ドメイン層では、ドメインモデルやドメインロジックを担います。
ドメインロジックとは、具体的な処理を定義する場所です。例えば、ブログ投稿機能を実装しようとした場合、ブログ記事の新規作成、更新、削除といった流れを定義するような感じです。
ドメインはDDD(ドメイン駆動設計)の文脈ではエンティティとも言われますが、エンティティの理解は正直難しいです。DDDが何なのかは後で解説します。

DDDでは主に、エンティティ、値オブジェクトという概念があります。
以下のサイト等を参考にしてください。
https://nrslib.com/clean-ddd-entity/
https://qiita.com/2san/items/eb9d7df6961542069233

上のサイトでエンティティのわかりやすい例があるのですが、水っていうエンティティがあった場合、そこで水を定義する必要があるのですが、そこで、『水は100度で沸騰する』、『水は0度で凍る』といったドメインルールを定義して水を定義するものです。そのドメインルールがメソッドに当たります。
そのため、今回のブログの例で言うと、ブログは作成、更新、削除ができる、といったルールを定義します。
ただし、少し踏み入った話なのですが、そのようなドメインロジックをどこで定義するのかどうかでDDDとの親和性は変わっていきます。

DDD(ドメイン駆動設計)とは

アーキテクチャを語る上で欠かせない概念がDDDです。
以下のサイトがわかりやすく解説されています。
https://zenn.dev/sutamac/articles/7e864fb9e30d70

超簡単に説明すると、アプリケーションを開発する上での設計思想のことです。
DDDでは、ドメインにドメインロジックとかを色々まとめるのですが、それをすることで、ドメイン層と永続化層との密結合を防ぐことができます。

DDD(ドメイン駆動設計)との親和性について

他の記事を読んでいると、クリーンアーキテクチャはDDDとの親和性が高いと言われていますが、個人的には少し語弊があると思います。
結論から言うと、『親和性を高くすることができる』の方が説明としては正しいと考えています。
ヘキサゴナルアーキテクチャにおいての話にはなりますが、ヘキサゴナルアーキテクチャでは、ドメインサービスとドメインモデルという概念があるのですが、ドメインロジックをサービスで担うのか、ドメインモデルで担うのかで変わっていきます。ドメインモデルにまとめてしまおうという設計思想を『濃いドメインモデル』と言ったりします。
濃いドメインモデルではよりDDDの設計思想に近づきます。
ただし、中には中間的なロジック等が出てきたりして、そのロジックをドメインサービス内で定義する機会も出てきます。その場合は、『薄いドメインモデル』や『ドメインモデル貧血症』と言われたりします。

良いアーキテクチャとは?

良いアーキテクチャとは何なのかについて解説します。
良いアーキテクチャの指標として、代表的な二つの概念があります
* 単一責任の原則
* 依存性逆転の原則

意味不明な日本語ですが、これについて考えるには、先ほど言った、アーキテクチャを意識することで得られる恩恵について考える必要があります。
先ほど書いた恩恵について、テストのしやすさは一旦置いておいて、保守容易性の向上と言いましたが、保守容易性とは、簡単にいうと、どこかで変更が生まれた際に、それに付随してどれほど変更が必要になるかということです。
これを向上させるには、極論、ある箇所を変更しても、違う理由で他で変更が生まれないようにすれば最高ですよね?
これが単一責任の原則です。
要は、責務ごとに分けた後、そのコードはその責務だけを全うして、他の責務を担わないようにするということです。
つまり、明確に責務を分けて、異なる責務間の依存性を少なくすれば(よく関心の分離とか言われます)付随する変更が少なくなると思います。

そのため、責務や層の間の依存関係を一方向にするのが普通です。
その依存関係の方向についてですが、これが一番重要です。
基本的に、アプリケーション層はアプリケーション全体の核を担う(厳密にはさらにその内部のドメインの部分ですが)ため、あまり付随して変更が発生したくないですよね?
例えば、データベースと直接やり取りをする部分で変更が生まれた時に、アプリケーションの核の部分で変更が生まれたら嫌ですよね?
それを解消するのが、依存性逆転の原則です。
要は、依存の方向が、すべてアプリケーションの核に向かっていれば、アプリケーションの核より外側で変更が生まれても核部分に直接変更は生まれません。
よくクリーンアーキテクチャでは以下の画像が使われます。

これを見てわかる通り、すべてはドメイン部分に向かっていることがわかります。(エンティティと書いてありますが、エンティティはここでいうドメイン部分のことです。)

ここまできたら、先ほどのテストのしやすさについても理解できると思います。
これらのアーキテクチャの概念を守ることができれば、各責務ごとの疎結合が実現できています。
そのため、単体テストをする際に、各責務単体のテストをしやすくなります。
これがテストのしやすさということです。

なぜレイヤードアーキテクチャ違うのか

先ほど、『上三つは同じだが、レイヤードアーキテクチャだけは明確に違う』と書きましたが、なぜなのかについて説明します。
結論から言うと、上三つのアーキテクチャでは、先ほど説明した、単一責任の原則と依存性逆転の原則がしっかりと守られているが、クリーンアーキテクチャでは守られていないためです。
単一責任の原則、依存性逆転の原則が守られていると、必然的に全ての依存関係の方向はドメインに向くアーキテクチャになります。そのため、クリーンアーキテクチャ、オニオンアーキテクチャ、ヘキサゴナルアーキテクチャに大きな違いはありません。
では、レイヤードアーキテクちゃはどうなっているのでしょうか?
以下の記事を参考にしてみてください。
https://qiita.com/kichion/items/aca19765cb16e7e65946

簡単に説明すると、レイヤードアーキテクチャは依存関係の方向は一方向になっていて、ある意味ではドメイン中心の設計となっていますが、依存の終端はインフラ層となっているため、本来アプリケーションの外側に配置されるべきはずのデータベースが中心となっているアーキテクチャです。これにより、ドメイン層とインフラ層の密結合が生まれてしまっています。

まとめると、議論すべきはレイヤードアーキテクチャかそれ以外かであり、オニオンアーキテクチャかクリーンアーキテクチャかヘキサゴナルアーキテクチャかを議論するのは不毛だと思います。これらの違いは粒度であるため、若干保守性に違いが生まれますが、この議論が起こるのは、超大規模プロジェクト等です。この記事では概念について説明しているため、これら三つの違いは重要ではありません。

最後に

途中で書くのが飽きてしまって、あまりまとまってない状態で終わってしまいましたw
気が向いたらもっと細かく書きたいと思いますw

Discussion