RFC-015: Django e FastAPI Integration Strategy
| Campo | Valor |
|---|---|
| Status | Draft |
| Author | Time de Tecnologia |
| Created | 2026-02-05 |
| Updated | 2026-02-05 |
| Reunião | A ser agendada |
| Deciders | Todo o time técnico |
Status
🟡 Draft - RFC em elaboração, aguardando apresentação
Contexto e Problema
Projeto usa Django E FastAPI em coexistência, mas falta:
- Estratégia de integração clara entre os dois frameworks
- Divisão de responsabilidades definida
- Data models compartilhados ou separados?
- Authentication/authorization entre sistemas
- Shared cache strategy (Redis)
- Database migrations coordenadas
Por que resolver isso agora?
- Confusão sobre quando usar Django vs FastAPI
- Duplicação de código entre frameworks
- Autenticação não é compartilhada
- Migrations podem conflitar
- Falta clareza arquitetural
Impacto de não resolver
- Código duplicado (models, business logic)
- Bugs por inconsistência entre Django e FastAPI
- Autenticação diferente em cada sistema
- Confusão do time sobre qual usar
- Manutenção duplicada
Documentação relacionada:
- docs/architecture/overview.md - Arquitetura atual
- docs/architecture/database-schema.md - Schema compartilhado
- docs/infrastructure/database/overview.md - PostgreSQL setup
- docs/development/coding-standards.md - Standards Python
- docs/testing/backend-testing.md - Testing Django/FastAPI
Proposta de Solução
Django para admin e FastAPI para APIs high-performance, com shared database e auth.
Divisão de Responsabilidades
Django (Monolith): - ✅ Django Admin interface - ✅ Autenticação de usuários (login, logout, registration) - ✅ User management (CRUD) - ✅ Permissions e grupos - ✅ Background tasks (Celery) - ✅ Dashboards internos - ❌ APIs públicas high-performance - ❌ Webhooks - ❌ Real-time features
FastAPI (Microservices): - ✅ APIs públicas REST (high-performance) - ✅ Webhooks de serviços externos - ✅ Integrações com third-party APIs - ✅ WebSocket endpoints (se necessário) - ✅ APIs high-traffic - ❌ Admin interface - ❌ User authentication (delega para Django) - ❌ Complex business logic (usa Django via API)
Database Strategy
Shared PostgreSQL Database:
PostgreSQL (RDS)
├── Django tables (managed by Django ORM)
│ ├── auth_user
│ ├── auth_group
│ ├── auth_permission
│ └── django_* (migrations, sessions, etc.)
└── Business tables (managed by Django migrations)
├── users (extended user info)
├── products
├── orders
└── ... (FastAPI lê/escreve via SQLAlchemy)
Migration strategy: - Django migrations são source of truth - Alembic sincroniza com schema Django (autogenerate) - Django roda migrations primeiro - FastAPI usa SQLAlchemy models que espelham Django models
Django models:
# app/models.py (Django)
from django.db import models
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
"""Extended user model."""
phone = models.CharField(max_length=20, blank=True)
bio = models.TextField(blank=True)
avatar_url = models.URLField(blank=True)
class Meta:
db_table = 'users'
indexes = [
models.Index(fields=['email']),
]
class Product(models.Model):
"""Product model."""
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=10, decimal_places=2)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
db_table = 'products'
FastAPI models (SQLAlchemy):
# app/models.py (FastAPI)
from sqlalchemy import Column, Integer, String, DateTime, DECIMAL
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class User(Base):
"""User model (mirrors Django User)."""
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
email = Column(String(254), unique=True, nullable=False, index=True)
username = Column(String(150), unique=True, nullable=False)
phone = Column(String(20))
bio = Column(String)
avatar_url = Column(String)
# Note: Não gerenciamos password aqui (Django handle)
class Product(Base):
"""Product model (mirrors Django Product)."""
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
name = Column(String(200), nullable=False)
price = Column(DECIMAL(10, 2), nullable=False)
created_at = Column(DateTime, nullable=False)
Authentication Strategy
Django: Autenticação principal
# Django: criar usuário, login, logout
# app/views.py
from django.contrib.auth import authenticate, login
from rest_framework_simplejwt.tokens import RefreshToken
def login_view(request):
user = authenticate(username=request.POST['email'], password=request.POST['password'])
if user:
# Gerar JWT token
refresh = RefreshToken.for_user(user)
return JsonResponse({
'access_token': str(refresh.access_token),
'refresh_token': str(refresh),
'expires_in': 900 # 15 min
})
FastAPI: Validação de token JWT
# FastAPI: apenas valida token gerado pelo Django
from fastapi import Depends, HTTPException
from fastapi.security import HTTPBearer
import jwt
security = HTTPBearer()
async def get_current_user(token: str = Depends(security)):
"""Validate JWT token created by Django."""
try:
payload = jwt.decode(
token.credentials,
settings.JWT_SECRET, # Shared secret com Django
algorithms=["HS256"]
)
user_id = payload.get("user_id")
# Fetch user from database (shared)
async with get_db() as db:
user = await db.get(User, user_id)
if not user:
raise HTTPException(status_code=401)
return user
except jwt.InvalidTokenError:
raise HTTPException(status_code=401)
@router.get("/api/v1/users/me")
async def get_current_user_endpoint(current_user: User = Depends(get_current_user)):
"""Get current authenticated user."""
return current_user
Shared JWT configuration:
# settings.py (Django e FastAPI compartilham)
JWT_SECRET = os.getenv("JWT_SECRET") # Mesmo secret!
JWT_ALGORITHM = "HS256"
JWT_ACCESS_TOKEN_EXPIRE_MINUTES = 15
JWT_REFRESH_TOKEN_EXPIRE_DAYS = 7
Shared Cache (Redis)
Django:
# settings.py
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': os.getenv('REDIS_URL'),
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
# Usage
from django.core.cache import cache
cache.set('user:123', user_data, timeout=300)
FastAPI:
import redis.asyncio as redis
redis_client = redis.from_url(os.getenv('REDIS_URL'))
async def get_user_cached(user_id: int):
# Try cache
cached = await redis_client.get(f'user:{user_id}')
if cached:
return json.loads(cached)
# Fetch from DB
user = await db.get(User, user_id)
# Cache
await redis_client.set(
f'user:{user_id}',
json.dumps(user.dict()),
ex=300 # 5 min TTL
)
return user
API Communication
FastAPI chama Django (quando necessário):
# FastAPI → Django internal API
async def get_user_permissions(user_id: int) -> list[str]:
"""Get user permissions from Django."""
async with httpx.AsyncClient() as client:
response = await client.get(
f"{DJANGO_INTERNAL_URL}/api/internal/users/{user_id}/permissions",
headers={"X-Internal-Secret": INTERNAL_API_SECRET}
)
return response.json()["permissions"]
Django internal API:
# Django: endpoints internos (não expostos publicamente)
# app/internal_api/views.py
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
@api_view(['GET'])
@permission_classes([InternalAPIPermission]) # Custom: valida X-Internal-Secret
def get_user_permissions(request, user_id):
user = User.objects.get(id=user_id)
permissions = user.get_all_permissions()
return Response({"permissions": list(permissions)})
Project Structure
people-api/
├── django_app/ # Django project
│ ├── manage.py
│ ├── app/
│ │ ├── models.py # Django models (source of truth)
│ │ ├── admin.py # Django admin
│ │ ├── views.py # Django views
│ │ └── migrations/ # Django migrations
│ └── settings/
│ ├── base.py
│ ├── staging.py
│ └── production.py
│
├── fastapi_app/ # FastAPI project
│ ├── app/
│ │ ├── main.py # FastAPI app
│ │ ├── models.py # SQLAlchemy models (mirror Django)
│ │ ├── routers/ # API routers
│ │ └── services/ # Business logic
│ └── alembic/ # Alembic migrations
│
├── shared/ # Shared code
│ ├── config.py # Shared settings
│ ├── cache.py # Redis client
│ └── auth.py # JWT utilities
│
└── tests/
├── django_tests/
├── fastapi_tests/
└── integration/ # Tests que envolvem ambos
Deployment
Separados mas coordenados:
# Deploy Django
- Deploy Django (Elastic Beanstalk ou ECS)
- Run Django migrations
- Health check Django
# Deploy FastAPI
- Deploy FastAPI (Lambda via SAM)
- Run Alembic migrations (sync com Django)
- Health check FastAPI
# Smoke tests (ambos)
- Test Django admin: login, create user
- Test FastAPI endpoint: GET /api/v1/users (com token Django)
Alternativas Consideradas
Opção 1: Migração completa para Django
Prós: - Single framework - Menos complexidade - Django REST Framework para APIs
Contras: - Django REST Framework menos performático que FastAPI - Perder benefícios FastAPI (async, performance) - Refactor grande
Por que não escolhemos: FastAPI oferece performance superior para APIs.
Opção 2: Migração completa para FastAPI
Prós: - Single framework - Performance - Async native
Contras: - Perder Django Admin (excelente) - Recrear auth system (complexo) - FastAPI não tem admin built-in - Refactor grande
Por que não escolhemos: Django Admin é muito valioso.
Opção 3: Databases separadas
Prós: - Zero conflito - Scaling independente
Contras: - Data duplication - Sync complexo - Custo 2x
Por que não escolhemos: Shared database é mais simples para nosso tamanho.
Opção 4: Microservices completos
Prós: - Scaling independente - Deploy independente
Contras: - Overhead operacional - Complexity aumenta muito - Time pequeno
Por que não escolhemos: Overhead não justificado.
Análise de Impacto
Impacto Técnico
- Django: admin, auth, user management
- FastAPI: APIs públicas, performance
- Shared: database, cache, JWT secret
- Coordenação de migrations
Impacto em Negócio
- ✅ Best of both worlds (Django admin + FastAPI performance)
- ✅ Aproveitar strengths de cada framework
- ✅ Flexibilidade para evoluir
- ⚠️ Complexidade de manter dois frameworks
Riscos
Risco: Complexidade de integração
Mitigação: Documentação clara, training, padrões bem definidos.
Plano de Implementação
Fase 1: Documentação (Semana 1)
- Documentar divisão de responsabilidades
- Criar decision tree (quando usar Django vs FastAPI)
- Documentar auth strategy
Fase 2: Shared Auth (Semana 2-3)
- Implementar JWT shared
- FastAPI valida tokens Django
- Testar fluxo completo
Fase 3: Integration (Semana 4-5)
- Django internal API
- FastAPI chama Django quando necessário
- Shared cache (Redis)
Fase 4: Migration Coordination (Semana 6)
- Processo de migrations coordenadas
- Testing
- Documentar
Métricas de Sucesso
Após 1 mês: - ✅ Auth compartilhada funcional - ✅ Zero duplicação de models - ✅ Decision tree documentado
Após 3 meses: - ✅ Time confortável com ambos frameworks - ✅ Zero conflitos de migrations - ✅ Integration testada e estável