Buenas prácticas, tips y trucos sobre Python, Django, Django REST Framework, FastAPI y Flask. Enfoques TDD, SOLID y DDD para construir APIs backend sólidas.
Anthony.D
Desarrollador Python · Django · FastAPI · Flask · Freelance
Desarrollador Python · Django · FastAPI · Flask · Freelance
Buenas prácticas, tips y trucos sobre Python, Django, Django REST Framework, FastAPI y Flask. Enfoques TDD, SOLID y DDD para construir APIs backend sólidas.

El TDD provoca dos reacciones extremas. La primera: “ya escribo tests, entonces hago TDD”. La segunda: “escribir los tests antes solo invierte el esfuerzo sin ganar mucho”. Ambas pasan por alto lo que realmente es el TDD. No es una cuestión de cobertura ni una simple inversión de orden. Es una disciplina de diseño que obliga a explicitar una intención antes de escribir el código que la satisface. Este artículo abre una serie sobre TDD. Antes de comparar las escuelas (Chicago, Londres, ATDD doble bucle, TDD estricto), hay que asentar el tronco común: el ciclo Rojo-Verde-Refactor, la práctica de los baby steps y las propiedades FIRST que definen un test que merece ese nombre. Los artículos siguientes partirán de esta base para hacer los compromisos tangibles, con ejemplos concretos. ...

Los cuatro artículos anteriores de la serie pusieron las bases para que un sistema distribuido se mantenga coherente: Saga para orquestar workflows, Outbox para publicar eventos fiables, Inbox para consumirlos sin duplicados, Idempotency Keys para proteger la API. Queda una pregunta: ¿qué se hace con los eventos una vez publicados? La respuesta más frecuente es: se usan para construir vistas de lectura. Es exactamente lo que propone el patrón CQRS (Command Query Responsibility Segregation): separar el modelo de escritura del modelo de lectura, cuando ambos divergen lo suficiente como para que forzarlos en una misma estructura cueste más que duplicarlos. ...

Los dos artículos anteriores trataron la idempotencia del lado evento: el patrón Outbox garantiza que un mensaje se publica al menos una vez, y el patrón Inbox garantiza que solo se consume una vez. Queda un tercer lugar donde aparece el mismo problema, más arriba: la propia API HTTP. Cuando un cliente lanza un POST /api/payments y su conexión se cae antes de recibir la respuesta, no sabe si el pago se creó o no. Si reintenta, arriesga pagar dos veces. Si no reintenta, arriesga no pagar nada. El patrón Idempotency Key, popularizado por Stripe y adoptado desde por la mayoría de APIs de pago, resuelve ese dilema poniendo el control del retry en manos del cliente. ...

El artículo anterior sobre el Transactional Outbox estableció una garantía clara: todo evento escrito en base acabará siendo publicado. Esa garantía es intencionadamente at-least-once. Un consumidor puede recibir el mismo evento dos veces, tres veces, o más si la red se porta mal. El patrón Outbox nunca promete unicidad. La consecuencia es inmediata: si el consumidor aplica dos veces el efecto del mensaje, factura dos veces, envía dos correos, descuenta stock dos veces. La coherencia garantizada del lado productor se rompe del lado lector. ...

Cuando un servicio modifica su base y quiere avisar al resto del sistema enviando un evento a Kafka, RabbitMQ o SQS, el código ingenuo se parece a esto: escribir en base y luego publicar. Si la publicación falla tras el commit, el evento se pierde. Si la publicación tiene éxito pero el commit falla, el evento habla de un estado que no existe. Ambos casos son la definición del dual-write problem. ...

Una operación de negocio que toca varios servicios plantea una pregunta que SQL resuelve desde hace cincuenta años dentro de una sola base de datos: ¿qué pasa cuando un paso tiene éxito y el siguiente falla? Mientras todo vive en la misma base, BEGIN ... ROLLBACK basta. En cuanto llamas a un servicio externo, una API de terceros u otra base de datos, esa red de seguridad desaparece. El patrón Saga responde a esa pregunta. En lugar de intentar una transacción ACID imposible, divide la operación en pasos locales, cada uno acompañado de una transacción compensatoria que sabe deshacer su efecto. Si el paso 4 falla, las compensaciones de los pasos 1, 2 y 3 se reproducen en orden inverso. ...

itertools es un módulo de la biblioteca estándar que expone bloques de iteración combinables. Su interés no está en sustituir un bucle for por una función de nombre críptico, sino en manipular flujos de datos sin cargarlos nunca por completo en memoria. Cada función devuelve un iterador perezoso: nada se calcula hasta que se consume el resultado. Eso es lo que permite encadenar transformaciones sobre millones de elementos con una huella de memoria constante. ...

Un test que debe aislar una función de sus dependencias acaba a menudo apilando llamadas a patch(). Tres dependencias, tres with anidados. Cinco dependencias, una pirámide que empuja el código útil diez niveles de indentación. El test se vuelve ilegible aunque su intención sea simple: verificar un solo comportamiento en una frontera precisa. contextlib.ExitStack resuelve exactamente este problema. Es un gestor de contexto que agrega un número cualquiera de otros y los cierra todos de forma limpia al salir. Así es como lo uso para mantener un test centrado en su frontera, con un caso concreto sobre la autenticación. ...

Consumir una API externa parece inofensivo al principio. Haces un requests.get, recibes un diccionario, y lo usas tal cual en el resto del código. El problema empieza cuando esa misma estructura JSON termina diseminada en diez archivos, y la API renombra un campo o cambia price de float a string. Corregirlo se convierte en una búsqueda del tesoro. La capa anti-corrupción (Anti-Corruption Layer, o ACL) responde a este problema. Procedente del Domain-Driven Design, actúa como un traductor entre un sistema externo y tu lógica de negocio. Un solo punto de contacto, un solo lugar que modificar cuando la API cambia. ...

Los permisos en Django REST Framework funcionan, pero muestran sus límites en cuanto las reglas de acceso se vuelven moderadamente complejas. Varios roles, objetos pertenecientes a un usuario específico, acciones custom en un ViewSet: acabas con clases has_permission y has_object_permission que mezclan verificaciones heterogéneas, difíciles de leer y aún más difíciles de testear. rest_access_policy (paquete djangorestframework-access-policy) propone un enfoque diferente: declarar las reglas de acceso como statements, similar a las políticas IAM de AWS. El resultado es legible de un vistazo, testeable independientemente del ViewSet, y extensible sin reescribir toda la clase. ...