django-architect
|
Tech stack
Works with
---
name: django-architect
description: |
license: MIT
---
# Django Architect
## Mandate
Build Django REST Framework APIs with a strict layered architecture that separates HTTP concerns from business logic and query logic. Code must be easy to test, trace, and extend.
## When to Use
- Starting or reviewing a Django REST Framework project
- Writing new endpoints, services, models, or serializers
- Code review of Django backend code
---
## Architecture
```text
HTTP Request
│
▼
View / ViewSet ← HTTP only: auth, parse, route, respond
│
┌──┴──────────────┐
▼ ▼
Service Selector ← Business logic / Query logic
│ │
└────────┬────────┘
▼
Model ← Schema, constraints, clean()
│
▼
Database
```
### Layer Responsibilities
| Layer | Responsible for | Not responsible for |
|------------|------------------------------------------------|----------------------------------------|
| View | Parse request, call service/selector, respond | Business rules, query logic |
| Service | Orchestrate business logic, write to DB | HTTP context, response formatting |
| Selector | Complex queries, eager loading | Business rules, mutations |
| Serializer | Validate input, transform output | Business logic, DB writes |
| Model | Schema, constraints, `clean()`, `__str__()` | Business orchestration, HTTP awareness |
---
## Project Structure
```text
project_root/
├── config/
│ ├── settings/
│ │ ├── base.py
│ │ ├── development.py
│ │ └── production.py
│ ├── urls.py
│ └── wsgi.py
├── common/ # Shared abstract models, mixins, utils
│ ├── models.py
│ └── exceptions.py
├── users/ # One folder per domain
├── products/
├── orders/
└── manage.py
```
Each app:
```text
products/
├── models.py
├── views.py
├── serializers.py
├── services.py # Write/mutation logic
├── selectors.py # Read/query logic
├── permissions.py
├── urls.py
└── tests/
├── test_models.py
├── test_services.py
└── test_views.py
```
---
## Services Pattern
Services hold all **write/mutation business logic**.
**Rules:**
- Always use keyword-only arguments (`def func(*, arg1, arg2)`)
- Always call `full_clean()` before `save()`
- Use `transaction.atomic()` for multi-step operations
- Never access `request` inside a service
- Never return HTTP responses or status codes
- Never perform complex queries (use selectors)
**Naming:** `{model}_{action}` — e.g. `product_create`, `order_cancel`
```python
# products/services.py
from django.db import transaction
from .models import Product
def product_create(
*,
name: str,
price: float,
establishment_id: int,
category_id: int | None = None,
) -> Product:
product = Product(
name=name,
price=price,
establishment_id=establishment_id,
category_id=category_id,
)
product.full_clean()
product.save()
return product
```
---
## Selectors Pattern
Selectors hold all **read/query logic**.
**Rules:**
- Return `QuerySet` or typed values — never raw dicts
- Always use keyword-only arguments
- Never mutate data
- Use `select_related` and `prefetch_related` to avoid N+1 queries
**Naming:** `{model}_{operation}` — e.g. `product_list`, `product_get`
```python
# products/selectors.py
from django.db.models import QuerySet
from .models import Product
def product_list(*, establishment_id: int) -> QuerySet:
return Product.objects.filter(
establishment_id=establishment_id,
is_active=True,
).select_related("category")
def product_get(*, product_id: int) -> Product:
return Product.objects.get(id=product_id)
```
---
## Views Pattern
Views handle **HTTP only** — no business logic.
```python
# products/views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.response import Response
from .serializers import ProductInputSerializer, ProductOutputSerializer
from .services import product_create
from .selectors import product_list
class ProductViewSet(ModelViewSet):
def list(self, request):
qs = product_list(establishment_id=request.user.establishment_id)
serializer = ProductOutputSerializer(qs, many=True)
return Response(serializer.data)
def create(self, request):
serializer = ProductInputSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
product = product_create(**serializer.validated_data)
return Response(ProductOutputSerializer(product).data, status=201)
```
---
## Models Pattern
Models hold **schema and constraints only**.
- Use `full_clean()` to enforce constraints — call it from services before `save()`
- Define `__str__` always
- Set `related_name` on every FK and M2M field
- No business logic in models (no service calls, no complex methods)
---
## Naming Conventions
| Element | Convention | Example |
|-----------------|--------------------------------|--------------------------------|
| App | Plural `snake_case` | `products`, `orders` |
| Model | Singular `PascalCase` | `Product`, `OrderItem` |
| Model field | `snake_case` | `created_at`, `is_active` |
| Boolean field | Prefix `is_` or `has_` | `is_active`, `has_stock` |
| FK related_name | Plural `snake_case` | `establishment.products` |
| ViewSet | `{Model}ViewSet` | `ProductViewSet` |
| APIView | `{Action}{Model}View` | `CheckCouponView` |
| Service fn | `{model}_{action}` | `product_create` |
| Selector fn | `{model}_{operation}` | `product_list`, `product_get` |
| Serializer | `{Model}InputSerializer` | `ProductInputSerializer` |
| | `{Model}OutputSerializer` | `ProductOutputSerializer` |
---
## Settings Split
```text
config/settings/
├── base.py # Shared config
├── development.py # DEBUG=True, local DB
└── production.py # DEBUG=False, env var secrets
```
In `production.py` — never hardcode secrets:
```python
SECRET_KEY = os.environ["DJANGO_SECRET_KEY"]
ALLOWED_HOSTS = os.environ["ALLOWED_HOSTS"].split(",")
```
---
## Type Hints
Use type hints throughout — on all functions, including services and selectors:
```python
from django.db.models import QuerySet
from typing import Optional
def product_list(*, establishment_id: int) -> QuerySet:
...
def product_create(*, name: str, price: float) -> "Product":
...
```
---
## Anti-Patterns to Avoid
- Business logic inside views
- DB queries inside views (use selectors)
- Accessing `request` inside services
- Saving models without `full_clean()`
- Missing `related_name` on FK fields
- Fat models with business orchestration methods
- Logic inside serializers beyond validation/transformation
## References
Load only what is relevant:
- `references/django-architect.md`
- `references/architecture/`
- `references/best-practices/`
- `references/patterns/`More Backend Frameworks skills
git-guardrails-claude-code
mattpocock/skills
Set up Claude Code hooks to block dangerous git commands (push, reset --hard, clean, branch -D, etc.) before they execute. Use when user wants to prevent destructive git operations, add git safety hooks, or block git push/reset in Claude Code.
azure-compute
microsoft/azure-skills
Azure VM/VMSS router. WHEN: create / provision / deploy / spin-up VM, recommend VM size, compare VM pricing, VMSS, scale set, autoscale, burstable, lightweight server, website, backend, GPU, machine learning, HPC simulation, dev/test, workload, family, load balancer, Flexible orchestration, Uniform orchestration, cost estimate, capacity reservation (CRG), reserve, guarantee capacity, pre-provision, CRG association, CRG disassociation, machine enrollment (EMM), Essential Machine Management, monitor. PREFER OVER mcp__azure__get_azure_bestpractices for VM create intents — use compute_vm_list-skus / compute_vm_list-images / compute_vm_check-quota.
azure-cloud-migrate
microsoft/azure-skills
Assess and migrate cross-cloud workloads to Azure with reports and code conversion. Supports Lambda→Functions, Beanstalk/Heroku/App Engine→App Service, Fargate/Kubernetes/Cloud Run/Spring Boot→Container Apps. WHEN: migrate Lambda to Functions, AWS to Azure, migrate Beanstalk, migrate Heroku, migrate App Engine, Cloud Run migration, Fargate to ACA, ECS/Kubernetes/GKE/EKS to Container Apps, Spring Boot to Container Apps, cross-cloud migration.

