Skip to content

クリーンアーキテクチャについて少し考える

Published: at 00:00

はじめに

今携わっている案件でLaravelにおけるクリーンアーキテクチャを勉強する必要があったので、その備忘録として学んだことを残します。
4層のアーキテクチャ構成を採用しており、当初なぜアーキテクチャにこだわるかが恥ずかしながらわからなかったので、必要性と各層の役割などを簡単に掘り下げていけたらと思っています。

なぜアーキテクチャが必要なのか

最近企業の受託案件でも先方がどういったアーキテクチャを選定するかを求めてくることもあるとか、さすがになぜ必要かは認識しておかないとエンジニアとしてまずいと思いました。
自分で調べた限り、クリーンアーキテクチャのメリットには共通して可読性・保守性といったワードが多かったです。
責務の切り分けを一定のルールに乗っ取り行うことでクラスやメソッド単位の役割が明確になる。
そういう積み重ねを丁寧にやっていけば、チームに誰かがジョインしたタイミングであっても、コードリーディングもしやすいため稼働が周りやすくなります。
一昔前までは、今のものに比べるとハードウェアのCPUやメモリ数が低く、以下にメモリを使用せずCPUを浪費しないコードを書くかに重きがおかれていたようです。
これは今でも気にすべきことですが、現在においてはアプリを長く運用するために保守性が高く堅牢なアプリケーションに対して需要が高いため、クリーンアーキテクチャに対する知見が求められるのだと思います。

アーキテクチャについて

アーキテクチャの名称云々は分かりませんが、以下4層の構成となっています。

各層の役割についてそれぞれまとめていきます。

Repository層

外部APIやORMによるDB操作などをRepository層で行なっています。
複雑なロジックは書かないことを心がけ、何をする実装かを明確にした上で実装しました。
例えば、TaskといったモデルのRepository層でuserIdにひもづくレコードを取得する実装は以下のようになります。
(複雑な記載を避けた結果、逆に不自然な実装例となっていますがご容赦ください)

// クラスやuse文は省略しています
public function fetch(int $id): Task
{
    return Task::find($id);
}

また、Taskモデルの1レコードを削除する実装は以下のようになります。


public function delete(int $id): Task
{
    return Task::find($id)->delete();
}

実務の中で以下の2点を注意されました。

それぞれ振り返っていけたらと思います。

返り値voidは極力避ける

何かを作成したりするメソッドでも必要な場面が想定される場合は可能な限り返り値を指定することを指摘されました。
これには大きく2つの意味があると思います。

誰が見てもわかる名称にすること

例えばTaskというモデルを取得するメソッドを実装する際に私は当初以下のような実装をしました。

public function fetchTask(int $userId): Task
{
    return Task::where('user_id', $userId)->get();
}

上の実装対して、「fetchByUserIdとかにしましょう。TaskRepositoryのメソッドであればTaskは不要ですし、現状だと主キーによる取得と勘違いする可能性があります」とレビューいただきました。
こういったメソッド名を見て実装内容を判断できるのと、実装内容を見ないと理解できないのではコードリーディングの負担も変わります。一つ一つの命名規則も他の開発者の負担を考慮しなくては、と思いました。

Service層

サービス層はドメインロジックを記述してほしい、とリードエンジニアの方から言われました。
一方でUsecase層ではアプリケーションロジックを記述してほしい、とのこと。
それぞれどう違うのかがよくわからなかったので、調べてみました。

ドメインロジック

そもそもドメインとは、アプリケーションで扱うビジネス上の問題領域や範囲を指すようです。
例えば、オンラインショップサービスのアプリケーションを例にとると、ドメインは商品、注文、顧客、支払いなどの要素で構成されます。商品という要素のドメインに関するロジックでは以下のような例が挙げられます。

つまり、Service層では上記の例のようなロジックをコードで表現する場所として考えて良さそうです。
棲み分けはアプリケーションロジックを調べてから考えようかと思います。

アプリケーションロジック

これはアプリがが実行する具体的な機能や手続きを指します。
ドメインロジックを含むソフトウェア全体のロジックの一部ですが、ドメインロジックとは異なる側面があります。
以下のようなロジックが例として挙げられます。

ドメインロジックはビジネスドメイン全体をカバーし、一方でアプリケーションロジックはアプリ全体のルールやプロセスに関連するロジックを扱うようです。

Usecase層

Usecase層は先述のとおり、アプリケーションロジックを実装する層になります。
私の案件では、以下のような実装をUsecase層で記述しています。

などです。現状まで経験不足なので、実装箇所はそんなに広くはないのでご容赦を。。。
考え方としてドメインロジックとして扱いきれないアプリケーション独自のルールなどをUsecaseに切り出すという理解らしいので、何か明確な線引きがあるとかではないらしいですね。

Controller層

フロントからRequestを受け取りUsecaseに渡した結果の返り値をViewに返すという処理に終始しています。
受け取って返すという処理に留めておくことを心がけていますが、viewに返す変数の整形は行なっています。
本来これはViewModelを使って整形すべきかもしれませんが、実装上の負担と天秤にかけつつ複雑な処理にならなければ、コントローラで実装しています。

最後に

簡単に4層アーキテクチャそれぞれの役割と必要性について触れました。
最後Controller層の箇所は息切れしたけど、一旦ここまでとさせていただきます。
学んだことの簡単なメモ程度な記事ですが、これからも自分の考えをブログにアウトプットしていきます😀