All checks were successful
DEPLOY_MULTI_BRACH/pipeline/head This commit looks good
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.2 KiB
4.2 KiB
Convenciones del Ecosistema V-Encore Lab
Ecosistema de microservicios
| Repo | Rol | Puerto |
|---|---|---|
django-core-base |
Hub orquestador principal | 8000 |
api_backoffice |
Consulta y gestión de BD | 8001 |
api_comunicaciones |
Emails, notificaciones, webhooks | 8002 |
api_documentacion |
Generación y gestión de documentos | 8003 |
web_interno |
Panel de gestión (React + Ant Design) | 3000 |
Stack Django
- Django 5.0 + DRF + SimpleJWT
- Apps bajo
app/. Prefijo/api/en todas las URLs salvoadmin/. - Patrón 3 capas: URL → View → Action
- SQL con
connection.cursor()y placeholders (%s). Nunca concatenar strings.
Patrón de vistas — 4 bloques obligatorios
from general.utilidades.acciones import LogService
class MiVista(APIView):
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def post(self, request):
path = '/mi-app/mi-endpoint/'
# Bloque 1 — Inicio Log
log_id = LogService.gestionar_log(self, request, path=path)
try:
# Bloque 2 — Data Cleaning
data = request.data
LogService.gestionar_log(self, request, log_id=log_id,
path=path, body_request=data, status_code=100)
params = {'campo': data.get('campo')}
except Exception as error:
response = {'error': str(error)}
LogService.gestionar_log(self, request, log_id=log_id,
path=path, body_response=response, status_code=400)
return JsonResponse(response, status=400)
try:
# Bloque 3 — Action Call
resultado = mi_accion(params)
# Bloque 4 — Cierre Log
LogService.gestionar_log(self, request, log_id=log_id,
path=path, body_response=resultado, status_code=200)
return JsonResponse(resultado, status=200)
except Exception as error:
response = {'error': str(error)}
LogService.gestionar_log(self, request, log_id=log_id,
path=path, body_response=response, status_code=500)
return JsonResponse(response, status=500)
LogService — reglas
- Siempre llamar como
LogService.gestionar_log(self, request, ...)— pasarselfde la vista. - Primera llamada (sin
log_id): crea el registro, devuelve ellog_id. - Llamadas siguientes: actualizar con
log_id=log_id. body_requestybody_responsese serializan conDjangoJSONEncoder— soportadatetime.date,Decimal, etc.status_codecomo entero. Usaris not Nonepara comprobar (0 es válido).
Base de datos
DB_HOSTdefinido → PostgreSQLDB_HOSTno definido → SQLite enapp/data/db.sqlite3- Migraciones siempre desde
app/:cd app && python manage.py migrate - SQL INSERT con psycopg2: usar
INSERT ... RETURNING id+cursor.fetchone(). Nuncacursor.lastrowid. - Booleans en psycopg2: pasar
True/False, no1/0.
Flujo de ramas
pre-dev → dev → master
pre-dev: desarrollo activo — aquí entran todos los cambiosdev: validación previa a producciónmaster: producción estable- Merges siempre con
--no-ff - NUNCA propagar en sentido inverso (master → dev o dev → pre-dev)
- Secuencia correcta:
git checkout pre-dev && git merge <mi-cambio> --no-ff git checkout dev && git merge pre-dev --no-ff && git push origin dev git checkout master && git merge dev --no-ff && git push origin master
Estructura de app Django
app/
├── api_config/ # settings.py, urls.py, wsgi.py
├── general/ # LogService, utils — NO tiene modelos propios
├── backend_admin/ # Modelo Log (audit_logs), admin panel
├── common/ # Modelos y utilidades compartidas entre apps
├── <feature_app>/ # Una app por dominio de negocio
├── data/ # SQLite (gitignored, solo .gitkeep versionado)
└── manage.py
Fixtures
loaddataignoraauto_now_add=True— incluir fechas explícitas en el JSON o falla con NOT NULL en PostgreSQL.