AI時代だからこそ、ドメイン駆動開発とクリーンアーキテクチャが重要

AI時代だからこそ、ドメイン駆動開発とクリーンアーキテクチャが重要

はい、どうもこんにちは佐藤です!

「AI に任せれば何でも作れる時代に、アーキテクチャなんて不要では?」

最近そんな声を聞くことが増えました。

これまで、銀行案件・ CRM ・基幹業務系のシステムを長くやってきた私の意見では……。

むしろ逆です!

AI 時代だからこそ、ドメイン駆動開発( DDD )とクリーンアーキテクチャが最強になります。その理由を書いていきますね!


最近よく使うアーキテクチャ

FastAPI で業務システムを作るとき、私はこんな構成を取ることが多いです。

├─ application
│  ├─ command      # 入力 DTO
│  ├─ result       # 出力 DTO
│  └─ usecase
├─ domain
│  ├─ company
│  │  ├─ entity.py
│  │  ├─ domain_service.py
│  │  └─ repository.py      # リポジトリインターフェース
│  ├─ contact
│  │  ├─ entity.py
│  │  ├─ domain_service.py
│  │  └─ repository.py
│  ├─ invoice
│  │  ├─ entity.py
│  │  ├─ domain_service.py
│  │  └─ repository.py
│  └─ ...(業務単位でどんどん増えていく)
├─ infrastructure
│  ├─ company
│  │  ├─ model.py
│  │  └─ repository_impl.py  # リポジトリ実装クラス
│  ├─ contact
│  │  ├─ model.py
│  │  └─ repository_impl.py
│  └─ ...
├─ interface
│  ├─ company
│  │  ├─ controller.py
│  │  ├─ request.py
│  │  └─ response.py
│  ├─ contact
│  │  ├─ controller.py
│  │  ├─ request.py
│  │  └─ response.py
│  └─ ...
└─ test
   ├─ application
   ├─ domain
   └─ ...

interface → application → domain → infrastructure という、いわゆる**オニオンアーキテクチャ(クリーンアーキテクチャ)**の構成です。外側が内側を呼ぶ。内側は外側を知らない。この一方向の依存が、すべての出発点になります。

それぞれの層を順番に見ていきましょう。


インターフェース層:入出口の番人

インターフェース層は、コントローラー・リクエスト・レスポンスを管理する場所です。バックエンドであれば HTTP の入口になりますし、アーキテクチャによっては画面も担当します。

ここで意識しているのが「コントローラーを通らないとドメインにアクセスできない」という制約です。

ドメイン層のリポジトリは、インターフェース(抽象クラス)だけを公開しています。その実装、つまり実際の DB アクセスは infrastructure 層にあります。 DI(依存性注入)を使って差し込む構造になっているため、コントローラー経由でないと永続化処理が走りません。

これにより何が嬉しいかというと、テストがとても書きやすくなるのです。ビジネスロジックのテスト時はフェイク実装を差し込めばいい。 DB を立ち上げずに済むテストがたくさん増えますよね。


ドメイン層:業務知識の砦

ここが一番大切な層です。

ドメイン層は業務単位でまとめた塊を扱います。 DDD のアグリゲートの思想で設計しており、例えば「請求書」であればヘッダーテーブル・明細テーブル・履歴テーブルがひとつの塊として扱われます。

domain/
└─ invoice/
   ├─ invoice.py              # アグリゲートルート
   ├─ invoice_detail.py       # 明細エンティティ
   ├─ invoice_history.py      # 履歴エンティティ
   └─ invoice_repository.py   # リポジトリインターフェース

このドメイン層に書くのは「どのような状態を持つのか」と「どのような操作ができるのか」です。

前回の記事で紹介した状態遷移図と BEAM 分析がここに集約されています。「下書き状態の請求書に承認操作はできない」「差し戻された請求書は再申請できる」といったルールが、ドメインオブジェクトの中に閉じ込められているのです。

業務ルールがここに固まるため、インターフェースが増えても・インフラが変わっても、核心のロジックは揺らぎません。とにかく硬い


インフラストラクチャ層:永続化の担当

infrastructure 層は、ドメイン層のリポジトリインターフェースの実装を担います。 SQLAlchemy の ORM でも、 NoSQL でも、外部 API でも、ここに差し込む形です。

ドメイン層はデータベースを知りません。「どんな DB でも来い」という構造になっています。

将来 PostgreSQL から DynamoDB に乗り換えることになっても、影響を受けるのは infrastructure 層だけ。ドメインのビジネスロジックは一切変えなくて済みますよね。


アプリケーション層:ドメインのオーケストラ

application 層は、複数のドメインをまたいで処理を組み立てる場所です。「会社と担当者と請求書を組み合わせて売上レポートを作る」といった操作がここに書かれます。

ドメインをまたぐことが前提なので、フォルダ分けはドメインごとではなく入力と出力で分けています。

application/
├─ command/    # 入力 DTO(Command オブジェクト)
├─ result/     # 出力 DTO
└─ usecase/    # ユースケース実装

各 DTO からリポジトリエンティティへの変換もこの層が担います。インターフェース層からコマンドが入ってきたら、ユースケースがドメインエンティティに変換して処理を走らせ、結果を DTO に詰めて返す。入力と出力の型が明確なので、ひとつひとつの処理がとても読みやすくなります。


AI 時代にこの構成が刺さる理由

「冗長すぎない?」と感じた方もいますよね。確かに、コード量は多くなります。 DB が変わったら、ドメインエンティティ・インフラモデル・DTO 、それぞれを書き換える必要があるのも事実です。

でも、今は AI がそのコードを書いてくれます。

書く量の問題は AI が解決してくれるのです。では、残る問題は何か。それは「AI が正しいコードを書けているかどうかの確認」です。

今の AI コーディングエージェントは、テストコードを実行してから成果物として渡してくれます。このサイクルを有効に使うには、テストが走りやすい構造が必要です。

このアーキテクチャは、テストとの相性が抜群です。

# ドメイン層のテスト(DB 不要)
def test_invoice_cannot_approve_from_draft():
    invoice = Invoice(status="draft")
    with pytest.raises(InvalidStateTransitionError):
        invoice.approve()

# アプリケーション層のテスト(リポジトリをフェイクで差し替え)
def test_create_invoice_usecase():
    repo = FakeInvoiceRepository()
    usecase = CreateInvoiceUseCase(repo)
    result = usecase.execute(CreateInvoiceCommand(...))
    assert result.invoice_id is not None

ドメイン層のテストは DB すら不要です。ビジネスルールを純粋な Python でテストできます。インフラストラクチャ層だけ実際の DB を使い、残りはフェイク実装で済む。層ごとにテスト戦略を分けられるのです。

ひとつひとつのメソッドの入力と出力が明確なので、人間も AI も読みやすく、 AI が正しいテストを書きやすい構造になっています。 AI をうまく使うには、ファイルの役割分担がはっきりしていることと、テストが書きやすい設計が必要です。このアーキテクチャはその両方を満たしてくれます。


まとめ

というわけで、AI 時代だからこそ DDD とクリーンアーキテクチャが重要だよ!って話でした!

あ。正確には、「書く量は AI が解決してくれるから、設計の硬さに全振りしよう」 って感じですねっ。

コードを書くコストが限りなくゼロに近づいた今、設計に妥協するメリットはほとんどありません。役割が明確に分かれた構造こそが、 AI の生成サイクルを最大限に活かしてくれます。ドメインに業務知識を閉じ込め、テストで品質を担保する。これが AI 時代の堅実なシステム開発です。

ぜひみなさんも、次のプロジェクトから取り入れてみてください!

この記事をシェアする