Copilot × Clean Architecture | クリーンアーキテクチャとは

Copilot × Clean Architecture

エピソード紹介

こんな方へ特におすすめ

  • クリーンアーキテクチャが何かイメージを掴みたい方

概要

こんにちは。サイオステクノロジーのはらちゃんです!

フロントエンドを開発していたとき、アトミックデザインという手法を知りました。ざっくり言うと小さな部品を用意して、使いまわせるようにする設計です。

バックエンドを開発するときに、同じような設計手法があるはずだと気になりました。

そこで知った手法こそ、クリーンアーキテクチャです。

本シリーズでは、Copilotを活用しつつ、クリーンアーキテクチャに沿って小規模なプロダクト「RepoScanner」を設計・実装した経緯をまとめます。

シリーズの前提知識として、このエピソードでは、クリーンアーキテクチャの基本を簡潔に説明します。

定義

はじめに、どのような方針であるのかイメージを掴みましょう。

クリーンアーキテクチャは、ソフトウェアを複数の関心ごとに分離し、変更に強くテストしやすい構造を保つ設計原則です。中心にビジネスルールとなるドメインを置き、外側にインフラやUIを置くことで、依存の向きを守ります。

クリーンアーキテクチャの構造
クリーンアーキテクチャの解説でよくみられるイラストです。

主なレイヤー

全体は以下のような層に分かれています。

src/
├── domain/              # 【最内部】ビジネスルール
│   ├── entities/        # ここにエンティティを置く
│   └── repositories/    # リポジトリインターフェースを定義する
├── use-cases/           # 【内側】アプリケーション固有の手順 
│   └── createUser.ts    # ユースケースの実装をする
├── infrastructure/      # 【外側】技術固有の実装
│   └── persistence/     # 永続化の実装をする
└── functions/           # 【最外部】エントリポイント
  └── http/              # ここで依存性注入を行う

具体的にどのような役割なのか見ていきましょう。

Domain

Domain層は、「コードでビジネスを記述する」場所であり、技術的な詳細(データベースやWebフレームワーク)とは無関係に、ビジネスの正解を定義する最も重要な層です。

  • エンティティ(概念)
    「何ができるか」の定義。型やNullを許容するかなどのビジネスルールを持つ。

具体的には、以下のような定義をします。ここではユーザー情報を用意しています。

Python
@dataclass(slots=True)
class User:
    user_id: UUID
    display_name: str | None
    created_at: datetime
    last_login_at: datetime | None
  • リポジトリ(ふるまい)
    「どのように取得 / 保存するか」の定義。エンティティの操作を抽象化する。

具体的には、以下のような定義をします。

定義したエンティティを用いて、「ログイン時に得られた情報を元に、ユーザーを作成 / 更新し、結果としてUserエンティティを返す」という処理をしています。

リポジトリのメソッド引数で再び型を明記することで、Pythonの型ヒントを機能させる意味もあります。

Python
class UserRepository(Protocol):
    def upsert_login_user(self, display_name: str | None) -> User: ...

Use Cases

Use Cases層は、どのようにエンティティやリポジトリを組み合わせて業務フローにするかを表現する層です。

司令塔としての役割があり、受け取った入力に対しリポジトリを使ってデータを取得 / 更新します。

例えば、以下のような具体的な機能を実現しています。

  • トークンでユーザー情報を取得して情報を更新する
  • DBから項目を受け取ってデータを返す

Infrastructure

Infrastructure層は、Use Cases層とさらに外側の層を繋ぐための層です。

データの変換と橋渡しを行う、アダプターという役割を担って外部ライブラリやDB を利用します。

例えば、以下のようにデータの相互変換や外側への具体的なアクセス実装をしています。

  • db.py ファイルでSQL に接続する
  • SQL文で操作を指定する

functions

Functions層はビジネスルールを組み合わせ、外側から受け取ったリクエストを処理する層です。

これにより、UIの変更(HTML→JSON)や、データベースの変更(MySQL→PostgreSQL)が、Domain層に一切影響を与えないように保護しつつ、具体的な技術要素との連携を実現しています。

例えば、以下のような具体的な機能を実現をしています。

  • HTTP API のスキーマを定義する
  • main.py ファイルでエントリポイントの指定をする
  • Pydantic を利用してバリテーションをする

依存性ルール(外側→内側)

コードの依存は常に外側から内側へ向かいます。内側の層が外側の具体実装を import してはいけません。

依存性逆転の原則に従い、ドメインやユースケースがインターフェースを定義し、インフラがそれを実装します。

functions(最外部)
  -> infrastructure(外側実装)
    -> use-cases(アプリ手順)
      -> domain(最内部ルール)

依存を分けるメリット

大きく2つ考えられます。

  • 影響範囲が狭い
    ドメイン / ユースケースを外部変更から隔離でき、要件変更など対応しやすい。
  • 依存性注入(DI)とテスト
    ユースケースはリポジトリなど依存をコンストラクタやファクトリで注入して受け取る。
    テストではその注入ポイントにモックやインメモリ実装を差し替えることで、ユースケース単体で即テストできる。

依存を分けるデメリット

こちらも、大きく2つ考えられます。

  • 初期コスト
    インターフェース定義やDIの仕組み、アダプター実装が増える。
  • 実装の一貫性維持
    境界のインターフェース設計を誤ると、後続実装で整合性を保つのが難しい。
    層をまたぐ設計と抽象が増え、設計理解の学習コストが上がる。

三層構造との違い

ここまで学んで、依存を分けるなら三層構造でもやっていなかったか?と疑問に思いました。

クリーンアーキテクチャと三層構造は、どちらもソフトウェアの「関心事を分離する」ための設計手法ですが、最大の違いは「依存関係の方向」と「システムにおいて何を中心と捉えるか」にあります。

三層構造は、システムを役割ごとに「上から下へ」3つの層に分けるアーキテクチャです。

  • プレゼンテーション層
  • ビジネスロジック層
  • データアクセス層

ここでは、プレゼンテーション層はビジネスロジック層に依存し、ビジネスロジック層はデータアクセス層に依存します。つまり、システムの中心がデータベースになることが多いです。

システムの中心がビジネスロジックであるクリーンアーキテクチャとは依存関係の方向が違うことがわかりました。

まとめ

今回は、クリーンアーキテクチャとは何かをご紹介しました。

  • 依存は外側→内側。インフラ実装を内側に漏らさない。
  • エンティティは何ができるか、リポジトリはどのようにするか。
  • DI を使えばユースケースはテストしやすくなる。
  • ユースケース単体と境界・統合を優先して整備する。

これからクリーンアーキテクチャの思想に則った設計を行うのでお楽しみに!

参考

ご覧いただきありがとうございます! この投稿はお役に立ちましたか?

役に立った 役に立たなかった

0人がこの投稿は役に立ったと言っています。
エンジニア募集中!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です