設計ガチ現場で生き延びるために設計思想と向き合ってみた
投稿日: 2025年07月27日
実務でRails強強ボスの元で鍛えているのですが、プロジェクトでは「レイヤードアーキテクチャ」を採用していて、このアーキテクチャは「クリーンアーキテクチャ」に着想を得たものであるとのことです。
慣れるにつれて、しっかりとした設計のメリットを実感するようになりました。
そこで、そもそもクリーンアーキテクチャとは何か、少し掘り下げてみることにしました。
「このアーキテクチャではじめにRails書いちゃってるから、他のプロジェクト行ったらコード見てられないと思うよ〜絶対こっちに戻ってきたくなると思うよ〜」って言われて、慣れたら設計しっかりしていることのメリットもわかってきたました。
私はRailsでこの設計を遵守して書いていますが(できてないとレビューでボコられますからね🤛)、設計思想自体はNext.jsでも応用可能なものだと思うので、どなたかの参考になればと思います。
ソフトウェアの構造を「変更に強く、テストしやすく、ビジネスルール中心に保つ」ための設計原則です。
考え方としてはソフトウェアを複数のレイヤーに分割し、それぞれのレイヤーが明確な責任を持ち、依存関係が内側に向かってのみ流れるようにすることです。
これにより、ビジネスロジック(中心にあるロジック)がフレームワーク、データベース、UIなどの外部の要素に依存しないようにします。
要点としては以下のようになります。
関心の分離(Separation of Concerns)
依存性の逆転(Dependency Inversion)
ドメインの独立性(Use Caseを中心に)
フレームワークやUIに依存しない構成
こんな図が出てきました。
この設計だと外部への依存が最低限になっているので単体でのテストがしやすいのと、各層が独立していることで、変更を加えた時の他の層への影響が最小限に抑えられることです。
このアーキテクチャを元にした「レイヤードアーキテクチャ」が私の関わっているプロジェクトで採用されています。
ソフトウェアシステムを機能ごとに複数の「層」(レイヤー)に分割し、階層構造で構築する設計手法です。
各レイヤーは特定の役割と責任を持ち、通常は下のレイヤーの機能を利用して、上のレイヤーにサービスを提供します。依存関係は基本的に上位のレイヤーから下位のレイヤーへの一方通行です。
ボス曰く、多くのプロジェクトでは「controllreに全部書きがち」だそうですが、レイヤードアーキテクチャ採用しているからcontrollerでするのはリクエストを受け取り、レスポンスを返す、以上です。
ビジネスロジックをcontrollerには書かない!責務を完全に分ける!って感じです。
クリーンアーキテクチャの「内側に向かって」だけ正直よくわかりませんでした。
UIやDBの変更が、ドメインロジックに影響を与えないようにすることです。
たとえば、画面のデザインを変えても、アプリの中核となる「商品を購入する」というロジック自体は変わらない設計になっている。
それが「内側に向かってのみ依存する」ということだそうです。
レイヤードアーキテクチャでは方向性までは考えてなく、あくまでロジックを各層に分けることや、依存性を減らすくらいまでです。
依存性が減ると、何かの変更を加える必要が出てきたときに「同時にここも変えなきゃ」が減りますよね。同時に変えなきゃいけないことが減るほど、バグも減りますよね。
保守が楽になり、修正内容に応じてどこを変えればいいかもすぐわかります。
## ディレクトリ構造と責任
| 層 (ディレクトリ) | 説明 |
| :------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `controllers/` | HTTPリクエストを処理します。パラメータの受信とレスポンスの返却を担当します。ビジネスロジックは他の場所に委任されます。 |
| `forms/` | ActiveModelなどのツールを使用して、入力の検証と書式設定用のフォームオブジェクトを定義します。 |
| `services/` | ビジネスロジックが含まれます。ユースケース別に整理されています。 |
| `adapters/` | 外部サービスまたはデータベースとのインターフェースを担当します。インフラストラクチャ関連の問題を担当します。 |
| `models/` | ActiveRecordを使用したドメインモデルです。データの永続性とその関連ロジックを処理します。 |
| `serializers/` | ドメインオブジェクトをJSONやその他の出力形式に変換する方法を定義します。APIレスポンスとビューレイヤーのフォーマットに使用されます。 |
この通りに実装しています。
初めて解説していただいたとき意味不明すぎて何言ってるかわからなかったですが、書いてるとわかってくるんですよね。
手を動かすのって本当に大事。
レビューで、「これもっと抽象化できる」って言われるのですが、これも外部依存を減らすのに本当に重要ですよね。
Next.jsももちろん書くのですが、コンポーネントの分け方では結構「疎結合!天才!」ってコメントいただいたりするのですが、Railsだとまだまだだなと感じます。
抽象化はこれまでも意識していましたが、歴長いエンジニアから見ると私がまだいけるん?って思うほどまだ抽象化できると教えてもらえて、控えめに言っても神だなと思いますね。
依存性を減らして再利用性を上げるために限界まで抽象化するということをもっとできるように経験積んでいきたいなって思いました。
フィードバックありがたすぎます。
これはシステムの設計思想の話なので応用ができます。
どう使うかは自分次第、、、
AIに任せてこの設計思想をNext.jsのプロジェクトで再現すると、、?と聞くとこんなの作ってもらえました。
/src
├── app/ # Next.js App Router (pages/api も同様に利用可)
│ ├── api/
│ │ ├── users/
│ │ │ └── route.ts # controllers/ の役割
│ │ └── products/
│ │ └── route.ts # controllers/ の役割
│ ├── components/ # UIコンポーネント
│ └── (pages)/ # pages routerの場合
│ └── ...
├── domain/ # コアなビジネスロジック (models/ の概念)
│ ├── entities/ # models/ に近い。純粋なドメインオブジェクト定義
│ │ ├── User.ts
│ │ └── Product.ts
│ └── repositories/ # domain/entities/ のデータ操作のインターフェース (adapters/ が実装)
│ ├── UserRepository.ts
│ └── ProductRepository.ts
├── application/ # services/ と forms/ の概念
│ ├── services/
│ │ ├── UserService.ts # 例: src/application/services/UserService.ts
│ │ └── ProductService.ts
│ └── forms/ # forms/ の概念
│ ├── UserForm.ts # ユーザー登録フォームのバリデーション定義など
│ └── ProductForm.ts
├── infrastructure/ # adapters/ の概念
│ ├── adapters/
│ │ ├── DbUserRepository.ts # domain/repositories/ の実装
│ │ ├── ExternalApiService.ts # 外部APIクライアント
│ │ └── (PrismaClient.ts など)
│ ├── database/ # DB接続設定など
│ └── utils/ # 共通ユーティリティ
├── presentation/ # serializers/ の概念と Next.js UI
│ ├── serializers/
│ │ ├── UserSerializer.ts
│ │ └── ProductSerializer.ts
│ └── (components/ や Next.jsのUIレイヤーがここに含まれると考えることもできる)
個人開発でここまでしなくていいと思いますが、考え方の参考になれば、、です。
実際私も働き初めてまず、Next.js(フロントエンド)のアーキテクチャについてはボスと相談してREADMEに書きました。
まず立ち上げ段階ではどういう設計思想を取り入れるかということを最初に考えるんだ〜と学びました!
私は本当にプロジェクトやリポジトリの作成段階から関わっているので、超初期メンバーで貴重な経験させていただいてます
最初は「レイヤードアーキテクチャ??意味がよくわからんなぁ」と思っていたのに、気づけばしっかりハマってる自分がいます。
責務の分離、依存関係の整理、そして抽象化。設計を意識するだけで、コードがグッと読みやすく・テストしやすく、変えやすくなっているのを感じます。
私は今Railsで学んでいますが、設計の考え方は言語やフレームワークに依存しない共通の「軸」だと思います。
この記事が、少しでも誰かが「なんでこんな分け方するんだろ?」「どう分けたらいいんだろ?」と思ったときのヒントになれば嬉しいです。
私もまだまだ修行中なので、引き続き手を動かして設計力つけていきます!