django-clean-drf について
Clean Architectureに沿ったDjango REST Frameworkアプリ開発を支援するスキルです。レイヤー構造と内向きの依存関係を重視し、ユースケース、サービス、薄いビュー、シリアライザー、テスト、API開発のパターンを示します。
主な機能
- 01レイヤー構造と内向き依存
- 02入出力DTOを使うユースケース
- 03サービス層のドメインロジック
- 04薄いビューと読み書き用シリアライザーの分離
- 05型ヒントとテスト容易性の指針
- 06アーキテクチャとAPIの参考資料
活用シーン
- 01Django REST Frameworkアプリの新規構築
- 02業務ロジックをユースケースとサービスに分割
- 03CRUDエンドポイントとシリアライザーの実装
- 04テスト可能なDjango APIコードの作成
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ agusabas/ django-skills --skill django-clean-drfインストール後、Claude Code で/django-clean-drfを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/api-patterns.mdauthentication.mdcoding-standards.mddjango-admin.mdexamples.mdmodels-domain.mdproduction-api.mdproject-structure.mdquery-optimization.mdservices-pattern.mdtesting-clean-arch.mduse-cases-pattern.mdviews-serializers.md詳細
Django Clean DRF アーキテクト
概要
このスキルは、クリーンアーキテクチャの原則に従って Django REST Framework アプリケーションを作成するための専門的なガイダンスを提供します。関心事を分離し、テスト容易性を最大限に高め、保守しやすい本番品質のコードを生成する、厳格なレイヤーアーキテクチャを適用します。
アーキテクチャのレイヤー:
HTTP Layer (Views/Serializers)
↓
Application Layer (Use Cases + DTOs)
↓
Domain Layer (Services + Models)
↓
Infrastructure Layer (ORM, External APIs, Cache)
主な機能:
- 厳格な依存関係ルールを備えたレイヤーアーキテクチャ(依存関係は内側にのみ向かいます)
- データクラスの入力/出力 DTO を使用したユースケースパターン
- ドメインロジックのサービスレイヤー(静的クエリ、インスタンスの変更)
- ユースケースに処理を委譲する薄いビュー
- 読み取り用と作成/更新用のシリアライザーを分離
- ビジネスロジックで例外を使わない明示的なエラーハンドリング
- 最新の Python 3.12+ および Django 5+ の機能
- 完全な型ヒントと、設計段階からのテスト容易性
使用するタイミング
次のような状況に遭遇した場合に、このスキルを呼び出してください。
新しいアプリケーションのセットアップ:
- 「次のような新しい Django API を作成して……」
- 「クリーンアーキテクチャで新しい DRF プロジェクトを始めて」
- 「レイヤーアーキテクチャで新しいアプリをセットアップして」
- 「ドメイン駆動の Django アプリケーションを初期化して」
- 「SOLID に従った Django アプリのひな形を作成して」
ユースケースの実装:
- 「次のユースケースを作成して……」
- 「次のビジネスロジックを実装して……」
- 「新しい操作/アクションを追加して……」
- 「このトランザクションをアトミックに処理して」
サービスレイヤー:
- 「次のサービスを作成して……」
- 「このビジネスロジックはどこに配置すればよいですか?」
- 「次のドメイン検証を実装して……」
- 「クリーンアーキテクチャに従ってデータをクエリして」
アーキテクチャに関する質問:
- 「この Django アプリはどのように構成すればよいですか?」
- 「次の処理はどのレイヤーで扱えばよいですか?」
- 「肥大化したビュー/モデルを避けるにはどうすればよいですか?」
- 「テストしやすくするにはどうすればよいですか?」
- 「Django に SOLID の原則を適用して」
API 設計:
- 「次の API エンドポイントを作成して……」
- 「シリアライザーを設計して……」
- 「このエンティティの CRUD を実装して」
手順
Django Clean DRF に関するリクエストに対応する際は、次のワークフローに従ってください。
1. 要件を分析してコンテキストを確立する
ドメインを理解する:
- 関連するエンティティ/モデルは何ですか?
- 必要な操作/アクションは何ですか?
- 適用すべきビジネスルールは何ですか?
- 外部依存関係(API、キュー)はありますか?
既存のプロジェクト構成を確認する:
- 既存のアプリとその構成を確認する
- すでに使用されているパターンを特定する
- 機能の利用可否を確認するため、Django/Python のバージョンを確認する
スコープを判断する:
- 単一のユースケースですか、それとも完全な CRUD ですか?
- 新しいアプリですか、それとも既存アプリの拡張ですか?
- API のみですか、それとも管理画面も含みますか?
2. 関連するリファレンスドキュメントを読み込む
タスクに応じて、同梱ドキュメントの該当箇所を参照してください。
アーキテクチャ:
- 新しいアプリのセットアップ →
references/project-structure.md - ユースケースの実装 →
references/use-cases-pattern.md - サービスレイヤー →
references/services-pattern.md - モデルとドメインロジック →
references/models-domain.md - ビューとシリアライザー →
references/views-serializers.md - テスト →
references/testing-clean-arch.md - 完全な例 →
references/examples.md
API 開発:
- クエリの最適化/N+1 →
references/query-optimization.md - ページネーション、フィルタリング、検索 →
references/api-patterns.md - 認証/JWT/権限 →
references/authentication.md - 本番環境へのデプロイ →
references/production-api.md - API 向け Django Admin →
references/django-admin.md
コード品質:
- コーディング規約/Ruff/セキュリティ →
references/coding-standards.md
3. クリーンアーキテクチャの原則に従って実装する
重要:レイヤーの依存関係ルール
Views → Use Cases → Services → Models
↓ ↓ ↓ ↓
Serializers DTOs (internal) ORM
- 依存関係は内側にのみ向かいます(外側のレイヤーが内側のレイヤーに依存します)
- ユースケースでビューをインポートしたり、サービスでユースケースをインポートしたりしてはいけません
ユースケースパターン:
from dataclasses import dataclass
from uuid import UUID
from django.db import transaction
@dataclass(frozen=True, slots=True)
class CreateOrderInput:
customer_id: UUID
items: list['OrderItemInput']
notes: str | None = None
@dataclass(frozen=True, slots=True)
class CreateOrderOutput:
success: bool
order_id: UUID | None = None
error: str | None = None
class CreateOrderUseCase:
def __init__(
self,
order_service: OrderService,
inventory_service: InventoryService,
):
self._order_service = order_service
self._inventory_service = inventory_service
@transaction.atomic
def execute(self, input_dto: CreateOrderInput) -> CreateOrderOutput:
# Validate
is_valid, error = self._inventory_service.validate_availability(input_dto.items)
if not is_valid:
return CreateOrderOutput(success=False, error=error)
# Execute
order = self._order_service.create(
customer_id=input_dto.customer_id,
items=input_dto.items,
)
return CreateOrderOutput(success=True, order_id=order.id)
サービスパターン:
class OrderService:
# STATIC methods for QUERIES (no state changes)
@staticmethod
def get_by_id(order_id: UUID) -> Order | None:
return Order.objects.select_related('customer').filter(id=order_id).first()
# INSTANCE methods for MUTATIONS (state changes)
def create(self, customer_id: UUID, items: list[OrderItemInput]) -> Order:
order = Order.objects.create(customer_id=customer_id)
for item in items:
OrderItem.objects.create(order=order, **item.__dict__)
return order
# VALIDATION returns tuple[bool, str]
def validate_can_cancel(self, order: Order) -> tuple[bool, str]:
if order.status == Order.Status.SHIPPED:
return False, "Cannot cancel shipped orders"
return True, ""
薄いビューパターン:
class CreateOrderView(APIView):
permission_classes = [IsAuthenticated]
def post(self, request: Request) -> Response:
serializer = CreateOrderSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
use_case = CreateOrderUseCase(
order_service=OrderService(),
inventory_service=InventoryService(),
)
input_dto = CreateOrderInput(**serializer.validated_data)
output = use_case.execute(input_dto)
if not output.success:
return Response({'error': output.error}, status=400)
return Response({'order_id': str(output.order_id)}, status=201)
シリアライザーの分離:
# WRITE serializer (input validation)
class CreateOrderSerializer(serializers.Serializer):
items = OrderItemInputSerializer(many=True, min_length=1)
notes = serializers.CharField(max_length=500, required=False)
# READ serializer (output display)
class OrderReadSerializer(serializers.ModelSerializer):
customer_name = serializers.CharField(source='customer.name', read_only=True)
class Meta:
model = Order
fields = ['id', 'customer_name', 'status', 'created_at']
read_only_fields = fields
ドメインロジックを備えたモデル:
class Order(models.Model):
class Status(models.TextChoices):
PENDING = 'pending', 'Pending'
CONFIRMED = 'confirmed', 'Confirmed'
SHIPPED = 'shipped', 'Shipped'
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
customer = models.ForeignKey('Customer', on_delete=models.PROTECT, related_name='orders')
status = models.CharField(max_length=20, choices=Status.choices, default=Status.PENDING)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
indexes = [models.Index(fields=['customer', 'status'])]
@property
def is_cancellable(self) -> bool:
return self.status in (self.Status.PENDING, self.Status.CONFIRMED)
4. 検証と仕上げ
アーキテクチャのチェックリスト:
- ビューは薄く保ちます(ロジックは20行未満)
- ユースケースは単一の操作を処理します
- サービスには再利用可能なドメインロジックを記述します
- 依存関係は内側にのみ向かいます
- レイヤー間に循環インポートはありません
コード品質のチェックリスト:
- すべての公開インターフェースに型ヒントを付けます
- データクラスのDTOは
slots付きのfrozenにします - ビジネスロジックのエラーに例外を使わず、Outputを返します
- バリデーションは
tuple[bool, str]を返します - 状態変更を
@transaction.atomicで囲みます
パフォーマンスのチェックリスト:
- ForeignKeyへのアクセスには
select_relatedを使います - 逆リレーションには
prefetch_relatedを使います - 頻繁にクエリされるフィールドにインデックスを設定します
- シリアライザーでN+1クエリが発生しないようにします
ディレクトリ構造
apps/<app_name>/
├── __init__.py
├── models.py # Domain entities with @property logic
├── views.py # Thin HTTP handlers
├── serializers.py # Read and Write serializers
├── urls.py # Route definitions
├── admin.py # Django Admin
├── services/
│ ├── __init__.py # Export services
│ └── <entity>_service.py
├── use_cases/
│ ├── __init__.py # Export use cases
│ └── <action>_<entity>.py # One file per use case
├── tests/
│ ├── __init__.py
│ ├── test_models.py
│ ├── test_services.py
│ ├── test_use_cases.py
│ ├── test_views.py
│ └── factories.py # Factory Boy factories
└── migrations/
同梱リソース
references/ - クリーンアーキテクチャとAPIに関する包括的なドキュメント
アーキテクチャのパターン
-
references/project-structure.mdディレクトリ構成、ファイルの命名、モジュールの構成、依存関係のルール -
references/use-cases-pattern.mdInput/Output DTO、コンストラクターインジェクション、トランザクション処理、エラー処理 -
references/services-pattern.md静的メソッドとインスタンスメソッド、バリデーションのパターン、サービス間の連携 -
`references/models-domain.md`` モデル内のドメインロジック、TextChoices、UUIDキー、インデックス
-
references/views-serializers.md薄いビュー、権限、ReadシリアライザーとWriteシリアライザー -
references/testing-clean-arch.mdユースケースの単体テスト、サービスのモック化、統合テスト -
references/examples.mdCRUDの完全な例、複雑なワークフロー、テストの例
APIのベストプラクティス
-
references/query-optimization.mdN+1クエリ、select_related/prefetch_related、インデックス、一括操作、集計 -
references/api-patterns.mdページネーション、フィルタリング、検索、並べ替え、APIバージョニング、エラー処理、スロットリング -
references/authentication.mdJWT認証、権限、APIキー、ロールベースのアクセス制御、セキュリティヘッダー -
references/production-api.md設定の構成、データベース設定、キャッシュ、ロギング、Sentry、ヘルスチェック、Docker -
references/django-admin.mdModelAdminの設定、インライン、カスタムアクション、クエリの最適化、権限、セキュリティ
コード品質
references/coding-standards.mdRuffの設定、インポートの順序、セキュリティの実践、YAGNI、ロギング、ドキュメント
コア原則
ビジネスロジックに例外を使わない
# BAD
def create_order(self, data):
if not self.can_create():
raise ValidationError("Cannot create order")
return Order.objects.create(**data)
# GOOD
def create_order(self, data) -> CreateOrderOutput:
if not self.can_create():
return CreateOrderOutput(success=False, error="Cannot create order")
order = Order.objects.create(**data)
return CreateOrderOutput(success=True, order_id=order.id)
バリデーションはタプルを返す
# BAD
def validate(self, order) -> bool:
return order.status != 'shipped'
# GOOD
def validate(self, order) -> tuple[bool, str]:
if order.status == 'shipped':
return False, "Cannot modify shipped orders"
return True, ""
テスト容易性を高めるコンストラクターインジェクション
# BAD - Hard to test
class CreateOrderUseCase:
def execute(self, input_dto):
service = OrderService() # Hardcoded dependency
return service.create(input_dto)
# GOOD - Easy to mock
class CreateOrderUseCase:
def __init__(self, order_service: OrderService):
self._order_service = order_service
def execute(self, input_dto):
return self._order_service.create(input_dto)
追加の注意事項
Pythonのバージョン: X | Noneのユニオン構文を使うには3.10以上が必要です。3.12以上を推奨します
Djangoのバージョン: 4.2 LTS以上が必要です。5.0以上を推奨します
命名規則:
- ユースケース:
<action>_<entity>.py(例:create_order.py) - サービス:
<entity>_service.py(例:order_service.py) - シリアライザー:
<Entity>ReadSerializer、<Entity>CreateSerializer
他のスキルとの連携:
- バックグラウンドタスクには
django-celery-expertを使います - 既存のコードをこのアーキテクチャに移行するには
refactordjangoを使います