安全に壊れるための設計原則
安全に壊れるための設計原則
Section titled “安全に壊れるための設計原則”「正常に動く」よりも「異常時に安全に壊れる」ことを優先する設計原則を詳しく解説します。
境界防御 (Boundary Defense)
Section titled “境界防御 (Boundary Defense)”外部(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 formsfrom 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などの攻撃を防止
副作用の局所化
Section titled “副作用の局所化”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を変更してもビジネスロジックは変更不要
- テスト容易性: モックで簡単にテスト可能
- 保守性: フレームワークの変更に強い
安全に壊れるための設計原則のポイント:
- 境界防御: 外部データは常に汚染されていると仮定し、型・形式・範囲を検査
- 副作用の局所化: 副作用をロジックの末尾に集約し、純粋処理と分離
- 依存の隔離: ビジネスロジックが特定ライブラリに依存しないよう抽象化
これらの原則により、「異常時に安全に壊れる」堅牢なシステムを構築できます。