概要(バックエンド設計=「見えないところの“筋肉と血管”を設計すること」)
バックエンド設計は、
「画面に見えないところで、アプリをちゃんと動かすための仕組みをどう組み立てるか」
を考える作業です。
どんなデータを扱うか(モデル・DB 設計)
どんな機能があるか(ユースケース・API・ビュー設計)
誰が何をできるか(認証・認可)
どの順番で処理するか(フロー・トランザクション)
将来の変更や拡張に耐えられるか(分割・責務・依存関係)
こういうことを、
「コードを書く前」と「書きながら」ずっと考え続けるのが
バックエンド設計です。
ここでは Django を前提にしながら、
バックエンド設計を始めるときの考え方
モデル(DB)設計の軸
ビュー・API 設計の軸
認証・権限・セキュリティの考え方
Django らしい“分割”と責務の分け方
を、具体例を交えてかみ砕いて話していきます。
まず「何をしたいアプリか」を言語化する
機能ではなく「ストーリー」で考える
バックエンド設計の一歩目は、
いきなりテーブル名や URL を考えることではありません。
「このアプリは、誰が、何のために、何をする場所なのか」
を、ストーリーとして言語化するところから始めます。
例えば、こんなアプリを考えてみます。
「学習ログアプリ」
ユーザーが毎日の勉強内容を記録できる
科目、勉強時間、メモを残せる
カレンダー形式で振り返れる
自分の合計勉強時間を見られる
このくらいのストーリーがあれば、
バックエンド側で「何を保存して、どう取り出すか」が見えてきます。
ここをすっ飛ばして「とりあえず User モデルと Post モデル…」と始めると、
途中で「これ、何のためのフィールドだっけ?」と迷子になりがちです。
ユースケースをざっくり列挙する
ストーリーが見えたら、
ユーザーの行動を「ユースケース」として分解します。
学習ログアプリなら、例えばこうです。
ユーザー登録・ログインする
今日の学習ログを新規登録する
過去の学習ログ一覧を見る
特定の日の学習ログ詳細を見る
合計勉強時間を集計して見る
この「ユースケース」が、
後でビューや API の設計にそのまま対応していきます。
バックエンド設計は、
「ユースケースを支えるデータと処理をどう組み立てるか」
という視点で考えるとブレにくくなります。
モデル(DB)設計:データの“骨格”を決める
エンティティ(もの)と関係を見つける
学習ログアプリの例で、
「どんなデータが必要か」を考えてみます。
ユーザー(User)
学習ログ(StudyLog)
最低限、この二つが見えてきます。
StudyLog は、
どのユーザーのログか(user)
いつのログか(日付)
何を勉強したか(subject)
どれくらい勉強したか(minutes)
メモ(note)
といったフィールドを持ちそうです。
Django モデルにすると、こんな感じです。
from django.db import models
from django.contrib.auth.models import User
class StudyLog(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
date = models.DateField("日付")
subject = models.CharField("科目", max_length=100)
minutes = models.PositiveIntegerField("勉強時間(分)")
note = models.TextField("メモ", blank=True)
created_at = models.DateTimeField("作成日時", auto_now_add=True)
class Meta:
ordering = ["-date", "-created_at"]
def __str__(self):
return f"{self.date} {self.subject} {self.minutes}分"
Pythonここで深掘りしたいポイントは二つです。
一つ目は、「User との関係を必ず持つ」こと。
user を外部キーにすることで、「誰のログか」が明確になります。
二つ目は、「集計や検索を意識した型にする」こと。
minutes を文字列ではなく整数(PositiveIntegerField)にしておけば、
「合計時間」「平均時間」などの集計が簡単にできます。
正規化しすぎず、「今のユースケース」に合わせる
科目(subject)をどう扱うか、という設計も面白いポイントです。
選択肢が固定なら、choices で持つ手もあります。
SUBJECT_CHOICES = [
("math", "数学"),
("english", "英語"),
("science", "理科"),
]
subject = models.CharField(max_length=20, choices=SUBJECT_CHOICES)
Pythonユーザーが自由に科目を増やしたいなら、
別テーブル(Subject モデル)に切り出してもいいでしょう。
class Subject(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
PythonStudyLog は Subject への外部キーを持つ形になります。
どちらが正解、という話ではなく、
今のユースケースでは、どこまで柔軟性が必要か
将来の拡張をどこまで見込むか
を考えて決めるのが「設計」です。
初心者のうちは、
「最初から完璧な正規化」を目指すよりも、
「今の機能を素直に支えられる形」にする方が、
結果的に分かりやすくなります。
ビュー・API 設計:ユースケースをエンドポイントに落とす
「1 ユースケース = 1 画面 or 1 API」を意識する
先ほどのユースケースを、
ビューや API に落とし込んでみます。
今日の学習ログを新規登録する
→ /logs/new/(HTML フォーム) or POST /api/logs/(API)
過去の学習ログ一覧を見る
→ /logs/ or GET /api/logs/
特定の日の学習ログ詳細を見る
→ /logs/2025-01-19/ or GET /api/logs/2025-01-19/
合計勉強時間を集計して見る
→ /stats/ or GET /api/stats/
このとき大事なのは、
URL を「名詞+動詞」ではなく「リソース(名詞)中心」で考える
HTTP メソッドで「何をするか」を分ける
という API 的な発想です。
例えば、/api/logs/ に対して
GET なら「一覧取得」
POST なら「新規作成」
というように、
同じ URL でもメソッドで意味を変えるのが自然です。
ビューの責務を「薄く」保つ
Django のビューは、
つい何でもかんでも書きたくなりますが、
責務を薄く保つのが設計としては大事です。
例えば、学習ログの新規登録ビューを考えます。
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .models import StudyLog
@login_required
def log_create(request):
if request.method == "POST":
date = request.POST.get("date")
subject = request.POST.get("subject")
minutes = request.POST.get("minutes")
note = request.POST.get("note", "")
# ここで全部バリデーションして、エラー処理して…とやり始めると
# ビューがどんどん太っていく
return render(request, "logs/log_form.html")
Pythonここで、
バリデーションや変換ロジックを全部ビューに書き始めると、
あっという間に 100 行、200 行になります。
そこで Django では、
フォーム(forms.py)やシリアライザ(DRF)に
「入力のチェックと変換」を任せる設計がよく使われます。
# logs/forms.py
from django import forms
from .models import StudyLog
class StudyLogForm(forms.ModelForm):
class Meta:
model = StudyLog
fields = ["date", "subject", "minutes", "note"]
Pythonビューはこうなります。
@login_required
def log_create(request):
if request.method == "POST":
form = StudyLogForm(request.POST)
if form.is_valid():
log = form.save(commit=False)
log.user = request.user
log.save()
return redirect("log_list")
else:
form = StudyLogForm()
return render(request, "logs/log_form.html", {"form": form})
Pythonビューの責務は、
どのフォームを使うか決める
フォームにリクエストデータを渡す
OK なら保存してリダイレクト
NG ならフォームをそのままテンプレートに渡す
だけになり、
「入力のチェックと変換」はフォームに閉じ込められます。
これが「責務を分ける」バックエンド設計の典型パターンです。
認証・認可・セキュリティを設計に組み込む
「誰のデータか」を常に意識する
バックエンド設計で一番やってはいけないのは、
「ユーザー ID をフォームや JSON で送らせて、そのまま使う」
というパターンです。
学習ログアプリで、
もしクライアントから user_id を送らせてStudyLog.objects.create(user_id=data["user_id"], ...)
のようにしてしまうと、
他人の user_id を指定してログを作れてしまいます。
正しい設計は、
「ユーザーは request.user から取る」
「クライアントに user_id を送らせない」
です。
log = form.save(commit=False)
log.user = request.user
log.save()
PythonAPI でも同じで、serializer.save(user=request.user) のように、
サーバー側で user を決めます。
バックエンド設計では、
「このデータは誰のものか」を常に意識し、
それをクライアントに決めさせない、という姿勢がとても重要です。
認可(何をしてよいか)をどこでチェックするか
「ログインしているかどうか」だけでなく、
「このユーザーはこの操作をしてよいか」を決めるのが認可です。
学習ログアプリなら、
自分のログは見られる・編集できる
他人のログは見られない・編集できない
というルールが自然です。
ビューの中で、
log = get_object_or_404(StudyLog, id=pk, user=request.user)
Pythonと書くことで、
「user=request.user 以外のログは 404 にする」
という認可を実現できます。
Django REST Framework なら、
permissions を使ってもう少し宣言的に書けますが、
根本の考え方は同じです。
バックエンド設計では、
どの操作に、どんな認可ルールが必要か
それをどこでチェックするか(ビュー、ミドルウェア、権限クラスなど)
を、ユースケースとセットで考えていきます。
Django らしい“分割”と責務の整理
「層」で考えると整理しやすい
Django は「MTV(Model-Template-View)」と言われますが、
バックエンド設計の観点では、
もう少し層を意識すると整理しやすくなります。
モデル層:DB とデータ構造(models.py)
フォーム/シリアライザ層:入力のチェックと変換(forms.py / serializers.py)
サービス層(必要なら):ビジネスロジック(services.py など)
ビュー/API 層:HTTP リクエストとレスポンスの橋渡し(views.py / api_views.py)
例えば、「学習ログを登録したときに、
その日の合計時間を再計算して別テーブルに保存する」
といったロジックが出てきたとします。
これを全部ビューに書くと、
ビューがどんどん太っていきます。
そこで、サービス層を作る設計もあります。
# logs/services.py
from .models import StudyLog, DailySummary
def create_log_and_update_summary(user, date, subject, minutes, note=""):
log = StudyLog.objects.create(
user=user,
date=date,
subject=subject,
minutes=minutes,
note=note,
)
total = StudyLog.objects.filter(user=user, date=date).aggregate_sum("minutes")
DailySummary.objects.update_or_create(
user=user,
date=date,
defaults={"total_minutes": total},
)
return log
Pythonビューはこうなります。
from .services import create_log_and_update_summary
@login_required
def log_create(request):
...
if form.is_valid():
data = form.cleaned_data
create_log_and_update_summary(
user=request.user,
date=data["date"],
subject=data["subject"],
minutes=data["minutes"],
note=data["note"],
)
return redirect("log_list")
Pythonこうすると、
ビューは「HTTP のことだけ」
サービス層は「ビジネスロジック」
モデルは「データ構造」
という分担がはっきりします。
最初からサービス層を作る必要はありませんが、
ロジックが複雑になってきたら
「どこに責務を移すか」を考えるのが
バックエンド設計の腕の見せどころです。
アプリの分割も「責務」で決める
Django のアプリ分割も、
バックエンド設計の一部です。
学習ログアプリなら、
accounts アプリ(ユーザー登録・プロフィールなど)
logs アプリ(学習ログ本体)
stats アプリ(統計・グラフ表示)
のように分けることもできます。
分ける基準は、
一緒に変更されることが多いか
別プロジェクトに切り出したくなるか
責務がはっきり分かれているか
といった観点です。
最初は 1 アプリでも構いませんが、
「この機能は独立しているな」と感じたら
アプリを分ける選択肢を持っておくと、
長期的にメンテしやすい構造になります。
まとめ(バックエンド設計は「データとユースケースをつなぐ設計」)
Python+Django のバックエンド設計を、初心者目線でまとめるとこうなります。
まず「誰が何をするアプリか」をストーリーとユースケースで言語化し、それをもとにモデル(DB)の形を決める。
ユースケースを URL・ビュー・API に落とし込み、ビューは「HTTP の橋渡し」に責務を絞り、入力チェックはフォーム/シリアライザに任せる。
「このデータは誰のものか」を常に意識し、request.user を軸に認証・認可を設計することで、他人のデータを触れない構造にする。
Django の分割構造(モデル・ビュー・テンプレート・フォーム・サービス・アプリ分割)を使って、責務を分け、後から変更しやすい“骨格”を作る。

