Skip to content

安全に壊れるための設計原則

「正常に動く」よりも「異常時に安全に壊れる」ことを優先する設計原則を詳しく解説します。

外部(API・DB・ユーザ入力)からのデータは常に汚染されていると仮定し、型・形式・範囲を検査してからロジックに渡す。

# ❌ 悪い例: 無防備な入力受付
from django.http import JsonResponse
def create_user(request):
# 問題: 型チェックなし、バリデーションなし
name = request.POST.get('name')
age = request.POST.get('age')
user = User.objects.create(name=name, age=age)
return JsonResponse({'id': user.id})
# ✅ 良い例: 境界防御の実装
from django import forms
from django.views.decorators.http import require_http_methods
class CreateUserForm(forms.Form):
name = forms.CharField(max_length=100, min_length=1)
age = forms.IntegerField(min_value=0, max_value=150)
email = forms.EmailField()
@require_http_methods(["POST"])
def create_user(request):
form = CreateUserForm(request.POST)
if not form.is_valid():
return JsonResponse({'errors': form.errors}, status=400)
user = User.objects.create(
name=form.cleaned_data['name'],
age=form.cleaned_data['age'],
email=form.cleaned_data['email'],
)
return JsonResponse({'id': user.id})

なぜ重要か:

  • 型安全性: Django Formsで型チェック
  • バリデーション: 形式・範囲を検査
  • セキュリティ: SQLインジェクション、XSSなどの攻撃を防止

DB更新・通知・外部呼出などの副作用をロジックの末尾に集約し、それ以前を状態を持たない純粋処理として保つ。

# ❌ 悪い例: 副作用が散在
class OrderService:
def create_order(self, order_data):
# 副作用1: DB更新
order = Order.objects.create(**order_data)
# ビジネスロジック(副作用が混在)
if order.amount > 10000:
# 副作用2: 外部API呼び出し
notification_service.send_email(order.user_id)
# 副作用3: 別のDB更新
AuditLog.objects.create(event='ORDER_CREATED', order_id=order.id)
return order
# ✅ 良い例: 副作用の局所化
class OrderService:
def create_order(self, order_data):
# 1. 純粋処理: ビジネスロジック(副作用なし)
order = self.validate_and_create_order(order_data)
# 2. 副作用の集約: すべての副作用を末尾に
self.persist_order(order)
self.notify_if_needed(order)
self.audit_order_creation(order)
return order
def validate_and_create_order(self, order_data):
# バリデーションとオブジェクト作成のみ
if order_data['amount'] <= 0:
raise ValueError("Invalid amount")
return Order(**order_data)
def persist_order(self, order):
order.save()
def notify_if_needed(self, order):
if order.amount > 10000:
notification_service.send_email(order.user_id)
def audit_order_creation(self, order):
AuditLog.objects.create(event='ORDER_CREATED', order_id=order.id)

なぜ重要か:

  • テスト容易性: 純粋関数は単体テストが容易
  • 可読性: 副作用が明確に分離される
  • デバッグ容易性: 副作用の発生箇所が明確

ビジネスロジックが特定ライブラリやORMの仕様に依存しないよう、インターフェース層で抽象化する。

# ❌ 悪い例: ORMに直接依存
class OrderService:
def find_order(self, order_id):
# Django ORMの仕様に依存
return Order.objects.get(id=order_id)
# ✅ 良い例: インターフェースで抽象化
from abc import ABC, abstractmethod
# ドメイン層のインターフェース
class OrderRepository(ABC):
@abstractmethod
def find_by_id(self, order_id: int) -> Order | None:
pass
@abstractmethod
def save(self, order: Order) -> Order:
pass
# インフラ層の実装
class DjangoOrderRepository(OrderRepository):
def find_by_id(self, order_id: int) -> Order | None:
try:
return Order.objects.get(id=order_id)
except Order.DoesNotExist:
return None
def save(self, order: Order) -> Order:
order.save()
return order
# サービス層: ドメイン層のインターフェースに依存
class OrderService:
def __init__(self, repository: OrderRepository):
self.repository = repository
def find_order(self, order_id: int) -> Order:
order = self.repository.find_by_id(order_id)
if not order:
raise OrderNotFoundError(order_id)
return order

なぜ重要か:

  • 交換容易性: ORMを変更してもビジネスロジックは変更不要
  • テスト容易性: モックで簡単にテスト可能
  • 保守性: フレームワークの変更に強い

安全に壊れるための設計原則のポイント:

  • 境界防御: 外部データは常に汚染されていると仮定し、型・形式・範囲を検査
  • 副作用の局所化: 副作用をロジックの末尾に集約し、純粋処理と分離
  • 依存の隔離: ビジネスロジックが特定ライブラリに依存しないよう抽象化

これらの原則により、「異常時に安全に壊れる」堅牢なシステムを構築できます。