Pular para conteúdo

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

Referências