0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AIと開発するなら最初に考えたいFastAPIのディレクトリ構成

0
Posted at

AI時代に考えるFastAPIのディレクトリ構成

〜「結局どこに何を書けばいいの?」を整理してみた〜

最近、個人開発でFastAPIを使う機会が増えてきました。

Railsを書いていたこともあり、MVCのようにある程度決まった構成があるものだと思っていたのですが、FastAPIはかなり自由度が高く、

「この処理ってどこに書くの?」

という状態からスタートしました。

さらに最近はClaude CodeやCursor、ChatGPTなどを使って開発することも多くなり、AIに実装をお願いする機会も増えています。

ただ、何もルールを決めずにAIへ実装をお願いすると、

  • main.pyに全部書かれる
  • SQLがAPIの中に書かれる
  • 認証処理とDB処理が同じファイルにある
  • 同じような処理が何回も実装される

ということが普通に起こります。

もちろん動きます。

でも、後から自分で読むとかなり辛いです。

最近の開発では

「コードを書く」

よりも

「AIが書いたコードをレビューする」

時間の方が圧倒的に長くなってきました。

だからこそ、

AIが書きやすい構成ではなく、人間が読みやすい構成

を最初に決めておくことが重要だと感じています。

この記事は、自分自身の備忘録も兼ねて

  • FastAPIではどのようなディレクトリ構成にすると管理しやすいのか
  • それぞれのフォルダは何を担当するのか
  • AIへ実装を依頼するときに何を決めておくべきなのか

をまとめた記事になります。

この記事で紹介する構成は唯一の正解ではありません。

ですが、個人開発から中規模開発までであれば十分扱いやすく、AIとの開発でもかなり管理しやすい構成だと思っています。


まずはFastAPIを作るとこんな構成になる

FastAPIを初めて触る場合、最初は次のような構成になることが多いと思います。

project/
│
├── app/
│   └── main.py
│
├── requirements.txt
│
└── .venv/

一見するとかなりシンプルです。

最初のAPIを作るだけなら、この構成でも十分動きます。

では、それぞれ何を担当しているのでしょうか。


main.pyとは

main.pyはFastAPIの起動ファイルです。

FastAPIを起動すると最初に読み込まれるファイルであり、プロジェクトの入口になります。

例えば最小構成だとこのようになります。

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def hello():
    return {"message": "Hello FastAPI"}

ここでは主に

  • FastAPIアプリケーションの作成
  • Routerの登録
  • Middlewareの登録
  • CORS設定
  • 起動時・終了時の処理

などを行います。

逆に、ここへ

  • SQLを書く
  • パスワードを暗号化する
  • JWTを発行する
  • ビジネスロジックを書く

といったことは基本的に行いません。

最初は数十行しかありませんが、何も考えずに開発を続けると数百〜数千行まで膨れ上がります。

私も最初は全部ここへ書いてしまい、「この処理どこにあるんだ?」と探す時間がかなり増えました。


requirements.txtとは

Pythonでは、プロジェクトごとに使用するライブラリを管理するためにrequirements.txtを使用します。

例えばFastAPIのプロジェクトなら、このような内容になります。

fastapi
uvicorn
sqlalchemy
alembic
pydantic
python-jose
passlib
python-dotenv

新しいPCで開発を始めたり、チームメンバーが環境を構築するときは

pip install -r requirements.txt

を実行するだけで、必要なライブラリを一括でインストールできます。

最近ではuvPoetryを利用してpyproject.tomlで管理することも増えてきましたが、requirements.txtは今でも非常によく使われています。

AIへ実装をお願いする場合でも、

「requirements.txtに必要なライブラリを追加してください」

と依頼するだけで済むので、プロジェクトには必ず置いておくことをおすすめします。


ここまでがFastAPIを作成した直後の構成です。

しかし、ログイン機能やユーザー管理などを追加していくと、この構成だけでは管理しきれなくなります。

次は、実際に私が個人開発で採用しているディレクトリ構成を紹介します。


私が現在採用しているディレクトリ構成

現在、個人開発では次のような構成をベースにしています。

project/
│
├── app/
│   ├── main.py
│   │
│   ├── api/
│   │   ├── router.py
│   │   └── v1/
│   │       ├── auth.py
│   │       ├── users.py
│   │       └── posts.py
│   │
│   ├── core/
│   │   ├── config.py
│   │   ├── security.py
│   │   └── dependencies.py
│   │
│   ├── db/
│   │   ├── database.py
│   │   ├── session.py
│   │   └── base.py
│   │
│   ├── models/
│   │   ├── user.py
│   │   └── post.py
│   │
│   ├── schemas/
│   │   ├── user.py
│   │   ├── auth.py
│   │   └── post.py
│   │
│   ├── services/
│   │   ├── user_service.py
│   │   ├── auth_service.py
│   │   └── post_service.py
│   │
│   ├── repositories/
│   │   ├── user_repository.py
│   │   └── post_repository.py
│   │
│   ├── utils/
│   │   ├── hash.py
│   │   ├── token.py
│   │   └── logger.py
│   │
│   └── tests/
│
├── requirements.txt
└── .env

最初は少し多く感じるかもしれません。

私も最初は

「こんなにフォルダいるの?」

と思っていました。

でも、ユーザー認証や投稿機能、管理画面などを追加していくと、このくらい分かれていた方が圧倒的に読みやすくなります。

重要なのは

「どこに何を書くか」を最初に決めておくこと

です。


FastAPIの処理はどう流れるの?

FastAPIでは基本的に次のような流れで処理が進みます。

クライアント

        │

        ▼

API (Router)

        │

        ▼

Service

        │

        ▼

Repository

        │

        ▼

Database

        │

        ▼

Repository

        │

        ▼

Service

        │

        ▼

API

        │

        ▼

クライアント

これだけ見ると少し遠回りに感じるかもしれません。

ですが、この役割分担のおかげでコードがかなり読みやすくなります。

例えば、

「ユーザー登録」

を考えてみます。

ユーザー登録をするときに必要なのは

  • メールアドレスが重複していないか確認
  • パスワードをハッシュ化
  • DBへ保存
  • レスポンスを返す

です。

もしこれを全部APIに書いてしまうと、

@router.post("/users")

の中に全部書くことになります。

最初は問題ありません。

でも、

  • ログイン
  • パスワード変更
  • プロフィール更新

が追加されると、一気に読みにくくなります。

そのため、

FastAPIでは責務ごとに分けることが多いです。


api

app/
└── api/
    ├── auth.py
    ├── users.py
    └── posts.py

ここにはAPIだけを書きます。

例えば

@router.post("/users")

のようなエンドポイントです。

APIの役割は

「リクエストを受け取ること」

です。

逆に、

ここでは

  • SQL
  • 認証処理
  • パスワード暗号化
  • 複雑な計算

などは基本的に書きません。

イメージとしては

API



Serviceを呼ぶ



結果を返す

だけです。

APIを薄くしておくことで、

後から読む人も

「このAPIはどのServiceを呼んでいるのか」

だけ見れば理解できるようになります。


services

app/
└── services/

ここがFastAPIで一番重要なフォルダだと思っています。

ここには

ビジネスロジック

を書きます。

例えば

ユーザー登録なら

メール重複確認

↓

パスワード暗号化

↓

Repositoryへ保存依頼

↓

レスポンス作成

という流れになります。

つまり、

「何をするか」

を書く場所です。

逆に

SQLはここへ書きません。

DBの保存方法はRepositoryへ任せます。

そうすることで、

Serviceは

「どう保存するか」

ではなく

「何をするか」

だけ考えれば良くなります。


repositories

app/
└── repositories/

Repositoryは

DB担当です。

ここでは

create_user()

find_by_email()

delete_user()

update_user()

のような、

データベースとのやり取りだけを書きます。

例えば

ユーザーを検索したいなら

Repositoryへ

find_by_email(email)

と依頼するだけです。

Serviceは

SQLがどう書かれているか知る必要はありません。

この分離のおかげで、

例えばMySQLからPostgreSQLへ変更したとしても、

修正するのはRepositoryだけになります。

Serviceはそのまま利用できます。

これがRepositoryを分ける一番大きなメリットです。


models

app/
└── models/
    ├── user.py
    └── post.py

modelsにはSQLAlchemyのモデルを書きます。

データベースのテーブルをPythonで表現したものだと思ってください。

例えばユーザーテーブルなら

class User(Base):
    __tablename__ = "users"

    id = Column(Integer, primary_key=True)
    name = Column(String)
    email = Column(String)

のようになります。

ここでは

  • テーブル名
  • カラム
  • リレーション

などを書きます。

逆に、

  • ログイン処理
  • メール送信
  • JWT発行

などは書きません。

あくまで

「データベースの設計図」

を書く場所です。


schemas

app/
└── schemas/
    ├── user.py
    ├── auth.py
    └── post.py

FastAPIを触り始めた頃、一番混乱したのが

「ModelとSchemaって何が違うの?」

ということでした。

簡単に言うと

Modelは

データベース

Schemaは

APIの入出力

です。

例えばユーザー登録なら

class UserCreate(BaseModel):
    name: str
    email: str
    password: str

レスポンスなら

class UserResponse(BaseModel):
    id: int
    name: str
    email: str

のようになります。

ここで重要なのは

パスワードです。

登録時には必要ですが、

レスポンスでは返したくありません。

そのため

UserCreate

UserUpdate

UserResponse

のように分けて管理します。

Modelだけで全部管理しようとすると、

意図しないデータを返してしまう可能性があります。


db

app/
└── db/
    ├── base.py
    ├── database.py
    └── session.py

ここではデータベースとの接続設定を書きます。

例えば

  • SQLAlchemy Engine
  • Session
  • Base

などです。

APIやServiceから直接接続を書くのではなく、

ここでまとめて管理します。

そうすることで

接続先が変わったとしても

修正する場所はここだけになります。


core

app/
└── core/
    ├── config.py
    ├── dependencies.py
    └── security.py

プロジェクト全体で利用する設定を書きます。

例えば

  • JWT設定
  • SECRET_KEY
  • OAuth
  • 認証設定
  • 共通Dependency

などです。

私は

「設定関係は全部core」

と決めています。


utils

app/
└── utils/
    ├── hash.py
    ├── logger.py
    └── token.py

共通で利用する便利な処理を書きます。

例えば

  • パスワードHash
  • JWT生成
  • Logger
  • 日付変換
  • 共通関数

などです。

ここで一つ注意したいのは、

何でもutilsへ入れないことです。

例えば

ユーザー登録

utilsではありません。

Serviceです。

utilsには

「どこからでも使える便利機能」

だけを書くようにしています。


.env

プロジェクトのルートには

.env

を置くことが多いです。

ここには

DATABASE_URL=

SECRET_KEY=

OPENAI_API_KEY=

など、

GitHubへ公開したくない情報を書きます。

逆に、

これらを

config.py

へ直接書いてしまうのは避けた方が良いです。

APIキーやパスワードをGitHubへ公開してしまう原因になります。


tests

app/
└── tests/

個人開発だと後回しになりがちですが、

APIが増えてくると

かなり重要になります。

例えば

tests/

user/

auth/

post/

のように

機能ごとに分けています。


Alembic

FastAPIではDBマイグレーション用に

Alembicを使うことがほとんどです。

alembic/

versions/

env.py

などが作成されます。

ここでは

データベースの変更履歴

を管理します。

例えば

usersテーブル追加

↓

email追加

↓

profile追加

という履歴が残るため、

チーム開発では必須と言っていいくらい重要です。


ここまで整理すると役割はこうなる

フォルダ 役割
api APIの入口
services ビジネスロジック
repositories DB操作
models DBテーブル
schemas APIの入力・出力
db DB接続
core 設定
utils 共通処理
tests テスト
alembic マイグレーション

私はこの表を頭に入れておくだけでも

かなり迷わなくなりました。


では実際にどう動くのか

ここまで説明しても

正直、

「結局どの順番で呼ばれるの?」

という疑問が残ると思います。

そこで次は

ユーザー登録API

を例に、

POST /users

を実行した瞬間、

各フォルダがどのように動くのかを実際のコードを交えながら紹介します。

ここを理解すると、

FastAPIのファイル構成が一気に理解しやすくなると思います。


実際にユーザー登録APIはどう動くの?

ここまでフォルダの役割を説明してきましたが、

正直、

「役割は分かったけど、結局どの順番で動くの?」

と思う人も多いと思います。

そこで今回は

POST /users

というAPIを例に見ていきます。

ユーザー登録の流れはこのようになります。

リクエスト

    │

    ▼

users.py(API)

    │

    ▼

user_service.py

    │

    ▼

user_repository.py

    │

    ▼

Database

    │

    ▼

user_repository.py

    │

    ▼

user_service.py

    │

    ▼

users.py(API)

    │

    ▼

レスポンス

一見すると遠回りに見えるかもしれません。

ですが、それぞれ役割を分けることで、あとからコードを読んだときに非常に分かりやすくなります。

では実際に見ていきます。


Step1 APIでリクエストを受け取る

まず最初に呼ばれるのはAPIです。

例えば

@router.post("/users")
async def create_user(
    user: UserCreate,
    db: AsyncSession = Depends(get_db)
):
    return await user_service.create_user(db, user)

ここでやっていることはたった一つです。

Serviceへ処理を渡す

だけです。

ここでは

  • SQL
  • JWT
  • パスワード暗号化

などは書きません。

「APIは受付担当」

くらいに考えています。


Step2 Serviceでビジネスロジックを書く

次にServiceが呼ばれます。

例えば

async def create_user(db, user):

    exists = await repository.find_by_email(
        db,
        user.email
    )

    if exists:
        raise Exception("既に存在します")

    hashed_password = hash_password(
        user.password
    )

    return await repository.create_user(
        db,
        user,
        hashed_password
    )

ここでは

何をするのか

を書きます。

今回なら

  • メール重複確認
  • パスワードHash
  • DB保存

ですね。

逆に

SQLは書きません。


Step3 RepositoryがDBへアクセスする

Repositoryでは

データベースとのやり取りだけを書きます。

例えば

async def create_user(
    db,
    user,
    password
):

    new_user = User(
        name=user.name,
        email=user.email,
        password=password
    )

    db.add(new_user)

    await db.commit()

    await db.refresh(new_user)

    return new_user

ここでは

保存する

だけです。

もし

MySQLからPostgreSQLへ変更したとしても、

修正するのはRepositoryだけになります。


この分け方の何が嬉しいの?

例えば

ログイン機能

を追加したとします。

もし全部APIへ書いていたら

users.py

500行

↓

700行

↓

1000行

と増えていきます。

一方で責務を分けていると

users.py

30行

↓

35行

↓

40行

くらいしか増えません。

処理はServiceへ追加されるからです。


AIに実装をお願いするときも分かりやすい

例えばClaude Codeへ

ユーザー登録を作って

だけお願いすると、

APIに全部書かれることがあります。

そこで私は

User登録を実装してください。

・APIはServiceだけ呼ぶ

・Serviceへビジネスロジックを書く

・RepositoryへDB操作を書く

・Schemaは追加してください

・Modelは追加してください

という形で依頼しています。

これだけでかなり読みやすいコードになります。


私が毎回AIへ最初に渡いているルール

新しいプロジェクトを作るときは、

最初に次のルールを共有しています。

・APIにはビジネスロジックを書かない

・ServiceだけがRepositoryを呼ぶ

・RepositoryだけがDBを触る

・ModelとSchemaを混ぜない

・main.pyはRouter登録だけにする

・既存の命名規則を守る

・新しいフォルダを勝手に作らない

・既存コードを優先して利用する

・共通処理はutilsへまとめる

・責務が増えたらファイルを分割する

これだけでもAIが出力するコードの品質はかなり変わります。

最近はAIがコードを書くことよりも、

レビューする時間の方が長くなってきました。

だからこそ、

最初のルール作りが大切だと感じています。


私がやらないようにしていること

FastAPIを書いていて、

今では避けるようになったこともあります。

例えば

APIへSQLを書く

@router.post("/users")

db.add(...)

これは後から読むのがかなり大変になります。


ServiceへSQLAlchemyを書く

Serviceでは

「何をするか」

を書きたいので、

SQLはRepositoryへ任せています。


Modelをレスポンスとして返す

Modelには

データベースの情報が全部入っています。

そのまま返してしまうと、

返したくない情報まで返却してしまうことがあります。

必ずSchemaを経由するようにしています。


utilsに何でも入れる

最初は

便利そうだから

utils.py

へ全部入れたくなります。

ですが、

ユーザー登録などのビジネスロジックまで入れてしまうと、

何がutilsなのか分からなくなります。

「どこからでも利用できる共通処理」

だけを置くようにしています。


プロジェクトの規模によって構成は変えてもいい

ここまで紹介した構成は、私が現在の個人開発で採用しているものです。

ただ、これがすべてのプロジェクトに当てはまるわけではありません。

プロジェクトの規模によって、必要な構成は変わってきます。

例えば、簡単なAPIを1つ作るだけなら

app/
├── main.py
├── models/
├── schemas/
└── db/

くらいでも十分だと思います。

無理にRepositoryやServiceまで作ると、かえってファイル数が増えてしまい、管理しづらくなることもあります。

一方で、ログイン機能やCRUDが複数あり、今後も機能追加を予定している場合は、今回紹介したような構成にしておくと後からかなり楽になります。

さらに、チーム開発や受託開発になると、機能ごとにフォルダを分けたり、ドメインごとにディレクトリを切る構成を採用することもあります。

つまり、

「これが唯一の正解」

ではなく、

今のプロジェクトに合った構成を選ぶこと

が一番大切だと思っています。


AI時代だからこそ設計を先に考える

最近はClaude CodeやCursorなどのおかげで、コードを書くスピードはかなり速くなりました。

以前なら数時間かかっていた実装も、数十分で形になることがあります。

その一方で、何もルールを決めずに実装を進めると、

  • 同じような処理が増える
  • ファイルの責務が曖昧になる
  • AIごとに書き方が変わる

といった問題も起きやすくなりました。

実際、私も最初は「とりあえず実装してもらえばいい」と考えていました。

ですが、機能が増えてくると、

「この処理どこにあるんだ?」

「同じような処理が2つある」

「なんでここにSQLが書いてあるんだろう」

ということが増え、結局リファクタリングに時間を使うことになりました。

その経験から、今では実装よりも先に

  • ディレクトリ構成
  • 命名規則
  • 責務

を決めるようにしています。

AIはコードを書くのが得意ですが、プロジェクト全体の設計を考えるのは、まだ人が決めるべき部分だと感じています。


私がこれからも意識したいこと

この記事を書きながら改めて感じたのは、

FastAPIは自由度が高いからこそ、最初の設計が本当に重要だということです。

最初はシンプルな構成でも問題ありません。

ただ、機能が増えてきたら、

「今の構成で読みやすいか?」

を定期的に見直すことが大切だと思います。

私自身も、まだFastAPIを使い始めて日が浅く、試行錯誤しながら開発しています。

だからこそ、「これが正解です」と言うつもりはありません。

この記事も、現時点で自分が一番管理しやすいと感じている構成を整理した備忘録としてまとめました。

もし、

「こういう構成もおすすめ」

「ここはこうした方がいい」

というものがあれば、ぜひコメントで教えていただけると嬉しいです。


まとめ

今回紹介した内容を簡単にまとめると、次のようになります。

  • main.pyはアプリケーションの入口だけを書く
  • apiはリクエストを受け取り、Serviceを呼び出す
  • servicesにはビジネスロジックを書く
  • repositoriesにはデータベース操作を書く
  • modelsはデータベースの設計図
  • schemasはAPIの入力・出力を管理する
  • coreには設定や認証処理を書く
  • utilsには共通で利用する処理を書く
  • AIへ実装を依頼する前に、責務とディレクトリ構成を決めておく

このルールを最初に決めておくだけでも、後からコードを読む負担はかなり減ると感じています。


おわりに

この記事は、自分自身がFastAPIを学ぶ中で、「どこに何を書けばいいのか分からない」と悩んだ経験を整理するために書きました。

今後もFastAPIを使っていく中で、新しい構成や考え方を試すことがあれば、この記事も更新していこうと思います。

同じようにFastAPIを学び始めた方や、AIを活用しながら開発している方の参考になれば嬉しいです。

最後まで読んでいただき、ありがとうございました。


次回書いてみたい記事

現在、次のような内容も整理して記事にしてみようと思っています。

  • AI時代に考えるFastAPIの設計ルール
  • Claude Codeに最初に渡しているCLAUDE.mdを公開してみる
  • FastAPIで個人開発するときのフォルダ構成テンプレート
  • FastAPI × Next.jsで採用しているディレクトリ構成
  • AIにコードを書かせるときに気を付けていること

まだ試行錯誤している内容もありますが、実際に開発しながら得られたことを少しずつまとめていければと思っています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?