ChatGPTに教えてもらうドメイン駆動設計とFastAPI

# ChatGPTに教えてもらうドメイン駆動設計とFastAPI
## はじめに

最近、趣味でプログラミングの勉強をしている中で、「**ドメイン駆動設計（DDD）**」と「**FastAPI**」に興味を持ちました。特にDDDは、書籍で何度か学ぼうと試みたものの、正直に言うといまいちピンと来ず、実際に手を動かしながらやってみることで理解を深めようと決心しました。

しかし、DDDの概念や設計手法をゼロから自力で実践するのは難しく、ここでChatGPTのサポートを頼ることにしました。ChatGPTは質問に即座に回答してくれるので、理解に詰まったときにその都度解消でき、効率よく学習を進めることができました。

今回作成したのは、Google Keepと連携するシンプルなAPIです。ChatGPTにアシスタントとして手伝ってもらいながら、DDDやオニオンアーキテクチャを意識して設計し、FastAPIを使って実装しました。このブログでは、その学習の過程や実際のコード、ChatGPTのサポートがどのように役立ったかについて紹介します。

## ドメイン駆動設計とは

### DDDの概要

**ドメイン駆動設計（Domain-Driven Design、DDD）**とは、システムが扱う「ドメイン」（業務領域）に焦点を当て、ビジネスロジックを最適に表現するための設計手法です。DDDでは、業務の専門家と開発者が協力して、システムが解決すべきビジネス問題を深く理解し、その知識をコードに反映させます。

DDDには、**エンティティ**や**値オブジェクト**、**アグリゲート**などの概念が登場しますが、これを理解するだけでも一苦労です。今回は、ChatGPTのサポートを受けながら、これらの概念を少しずつ理解し、実際に使いながら学ぶことを目指しました。

### DDDを学ぶモチベーション

なぜDDDに興味を持ったのかというと、趣味で作るアプリでも「**ビジネスロジックをどうやって整理してコードに落とし込むか**」という点に悩むことがあったからです。DDDを取り入れることで、コードの見通しが良くなり、後で見返したときにも理解しやすいものになると期待しました。また、DDDの知識は将来の仕事でも役立つ可能性があるため、趣味の開発を通して学びたいと考えました。

### オニオンアーキテクチャとは

DDDを実践する際に役立つ設計手法として、**オニオンアーキテクチャ**があります。オニオンアーキテクチャでは、依存関係が内側から外側にのみ向かうように設計することで、ドメインロジックが外部のフレームワークやインフラストラクチャに依存しない構造を実現します。
![](https://storage.googleapis.com/zenn-user-upload/9a0efb37498a-20241103.png)

具体的には、以下のように各層を配置します：

- 中心に**ドメイン層**（ビジネスロジックを持つ）
- 次に**ユースケース層**（ドメイン層を利用してアクションを実行する）
- 最も外側に**インフラストラクチャ層**と**プレゼンテーション層**（外部システムやUIとの連携）

この構造により、ドメインロジックを中心に据え、変更に強く、テストもしやすいシステムを構築できます。

## 作成したアプリ

### アプリ概要

今回作成したのは、**タイトルと内容を受け取り、それをGoogle Keepに連携して保存するAPI**です。機能自体はシンプルですが、DDDとオニオンアーキテクチャを意識して設計しました。APIの開発にはPythonのFastAPIを使っており、非同期処理に対応しているため、パフォーマンスも良好です。

### ChatGPTへのサポート依頼方法と気を付けた点

ChatGPTにサポートをお願いする際、**具体的な質問をする**ことを意識しました。たとえば、「オニオンアーキテクチャを使ったフォルダ構成はどうすればいいですか？」や「ドメイン層の設計で注意すべきポイントは？」といった形で、課題や疑問点を明確にして伝えることで、適切なアドバイスをもらうことができました。

また、DDDのように抽象的な概念を説明してもらう際には、**具体例を交えて解説を求める**ことで理解が深まりました。ChatGPTが返してくれる例を見ながら実際にコードを書き進めることで、学習のスピードが上がったと感じています。

### フォルダ構成

以下が今回作成したアプリのフォルダ構成です。

```plaintext
fastapi-googlekeep-integration/
├── app/
│   ├── api/                  # APIエンドポイント
│   │   └── v1/
│   │       └── googlekeep.py # Google Keep用エンドポイント
│   ├── domain/               # ドメイン層
│   │   └── models/
│   │       └── note.py       # メモのエンティティ
│   ├── infrastructure/       # インフラストラクチャ層
│   │   └── googlekeep_repository.py # Google Keep連携のリポジトリ
│   ├── usecases/             # ユースケース層
│   │   └── create_note_usecase.py  # メモ作成のユースケース
│   └── main.py               # FastAPIエントリーポイント
└── pyproject.toml
```

#### フォルダ構成とDDD・オニオンアーキテクチャの対応

- **ドメイン層** (`domain/models/note.py`): メモのエンティティを定義しています。この層はビジネスロジックを表現する部分で、他の層に依存しない独立した形にしています。
- **ユースケース層** (`usecases/create_note_usecase.py`): メモを作成する具体的なアクションを担当し、ドメイン層のエンティティとインフラストラクチャ層のリポジトリを結びつけています。
- **インフラストラクチャ層** (`infrastructure/googlekeep_repository.py`): Google Keepとの連携を担当する層です。外部サービスへのアクセスをこの層に集中させることで、ドメインロジックとの分離を図っています。
- **プレゼンテーション層** (`api/v1/googlekeep.py`): APIエンドポイントを定義し、クライアントからのリクエストを受け付けて、ユースケース層のロジックを呼び出します。

### コードの役割

実際に実装したコードとその役割について紹介します。

#### ドメイン層: `note.py`

```python
# app/domain/models/note.py
class Note:
    def __init__(self, title: str, content: str):
        self.title = title
        self.content = content
```

このエンティティは、メモのデータ構造を表しています。`title`と`content`の2つの属性を持ち、ドメインロジックをカプセル化する役割を担っています。

#### ユースケース層: `create_note_usecase.py`

```python
# app/usecases/create_note_usecase.py
from app.domain.models.note import Note
from app.infrastructure.googlekeep_repository import GoogleKeepRepository

class CreateNoteUseCase:
    def __init__(self, google_keep_repo: GoogleKeepRepository):
        self.google_keep_repo = google_keep_repo

    def execute(self, title: str, content: str):
        note = Note(title, content)
        return self.google_keep_repo.save(note)
```

ユースケース層では、メモの作成というアクションをまとめています。このクラスは、

メモのエンティティを生成し、インフラストラクチャ層のリポジトリに保存する役割を持ちます。

#### インフラストラクチャ層: `googlekeep_repository.py`

```python
# app/infrastructure/googlekeep_repository.py
import gkeepapi
from app.domain.models.note import Note

class GoogleKeepRepository:
    def __init__(self, username: str, app_password: str):
        self.keep = gkeepapi.Keep()
        self.keep.login(username, app_password)

    def save(self, note: Note):
        gnote = self.keep.createNote(note.title, note.content)
        self.keep.sync()
        return {"status": "success", "title": gnote.title, "content": gnote.text}
```

Google Keepとの連携を担当するリポジトリで、`gkeepapi`ライブラリを使用してGoogle Keepにメモを保存します。リポジトリパターンを採用することで、ビジネスロジックをインフラストラクチャから分離しています。

#### プレゼンテーション層: `googlekeep.py`

```python
# app/api/v1/googlekeep.py
import os
from fastapi import APIRouter
from app.usecases.create_note_usecase import CreateNoteUseCase
from app.infrastructure.googlekeep_repository import GoogleKeepRepository

router = APIRouter()

@router.post("/keep")
async def create_keep_entry(title: str, content: str):
    username = os.getenv("GOOGLE_KEEP_USERNAME")
    app_password = os.getenv("GOOGLE_KEEP_APP_PASSWORD")

    if not username or not app_password:
        return {"error": "Google Keepの認証情報が設定されていません"}

    google_keep_repo = GoogleKeepRepository(username, app_password)
    use_case = CreateNoteUseCase(google_keep_repo)
    result = use_case.execute(title, content)
    return {"result": result}
```

このエンドポイントは、ユーザーからのリクエストを受け取り、ユースケースを呼び出してメモをGoogle Keepに保存する機能を提供します。

## ChatGPTからのサポートで理解が深まったこと

DDDやオニオンアーキテクチャは、書籍で読んでも抽象的な概念が多く、実際に手を動かしながら理解する必要があると感じました。ChatGPTを使うことで、具体例を交えたアドバイスがもらえ、学びが格段に進みました。また、進捗に応じてその都度質問できるので、つまずいた箇所での解消が早く、効率的に理解を深めることができました。

## まとめ

今回、ChatGPTのサポートを受けながら、趣味としてドメイン駆動設計を取り入れたFastAPIアプリを作成しました。DDDを取り入れることで、コードの保守性が向上し、後から見返しても分かりやすい構造が実現できました。また、ChatGPTの助けを借りることで、概念的な部分も具体的に理解でき、今後の勉強にも役立つと感じています。