Práctica · Bloque 3 · Unidades 3.1–3.2
Compón la aplicación sin ocultar su comportamiento
Un refactor no termina cuando aparecen carpetas o `Depends`. Termina cuando puedes demostrar qué contrato se conserva, qué proveedor se ejecuta, cuánto vive su resultado y cómo se libera incluso al fallar.
Cómo trabajar este bloque
Antes de modularizar: Calcula las rutas finales a partir de los prefijos y dibuja la dirección de imports antes de mover una línea.
Antes de proporcionar recursos: Dibuja el árbol desde la operación hasta cada proveedor y predice cuáles se ejecutan una vez por petición, una vez por proceso o alrededor de la respuesta.
Usa un workspace temporal. Conserva un baseline verde, mueve una sola frontera y repite exactamente la misma observación. Los paquetes no incluyen una solución modular, un árbol de dependencias resuelto ni tests ocultos que dicten carpetas.
Paso 5 · Apoyo focalizado
Extrae una sola frontera
Evidencia: El diff mueve código, pero status, body, modelos de salida, operationId y paths públicos permanecen estables.
EX-B3-01
Extraer el primer router sin cambiar el contrato
Una aplicación de expedientes ya tiene tests y tres operaciones públicas, pero main.py mezcla arranque, schemas, estado y rutas.
Pensar y responder en la web
Predice y guarda aquí tu razonamiento antes de abrir el material; después contrástalo en el workspace.
Pregunta central
¿Qué paths deben quedar en decoradores y prefijos para que el contrato final no cambie, y qué import debe apuntar hacia el punto de composición?
Frontera que debes preservar
Contrato inicial: GET /health, POST /expedientes y GET /expedientes/{expediente_id}. El include no aporta prefijo adicional.Para ordenar tu razonamiento
- ¿Qué conjunto método/path existe antes de mover código?
- ¿Qué suma de prefijos produce cada URL final?
- ¿Qué import crearía una vuelta hacia main.py?
Trabajo en el workspace
- Ejecuta los tests y registra las operaciones generadas por app.openapi().
- Completa el mapa de prefijos y el grafo de imports antes de mover código.
- Extrae schemas y un APIRouter de expedientes; conserva main.py como punto de composición.
- Ejecuta la misma regresión y explica cualquier diferencia antes de corregirla.
Entrega esperada
- Árbol final y grafo de imports dirigido.
- Diff acotado del refactor.
- Evidencia de pytest y fingerprint OpenAPI antes y después.
Escribe lo que piensas. Se guarda automáticamente en este navegador.
Sin respuesta guardada.
Pistas opcionales
Pista 1 · Conserva la suma
- Si el router tiene prefix=/expedientes, el decorador de colección puede usar una cadena vacía.
- El prefijo de include también participa aunque no esté en el archivo del router.
Pista 2 · Corta la vuelta
- El módulo de rutas exporta router; main.py lo importa e incluye.
- Un módulo inferior no necesita importar main.py para registrarse.
Profundización opcional
Extraer las operaciones de expedientes a APIRouter mediante un corte pequeño y demostrar compatibilidad HTTP y OpenAPI.
Contexto adicional
- No añadas endpoints, capas ni modelos durante el refactor.
- Conserva nombres públicos, status, bodies, response_model y operationId.
- Haz pasar la regresión después de extraer un solo router.
Decisiones abiertas
- Puedes usar imports absolutos o relativos, pero debes mantener una convención y explicar la dirección.
- Puedes dejar /health en main.py o justificar un router operacional; no crees ese router solo por simetría.
Cómo revisar tu respuesta
- Los tres pares método/path permanecen iguales.
- Status, body y response_model pasan la regresión existente.
- El router no importa la instancia app ni existe registro por efecto lateral.
- La explicación identifica una decisión que deliberadamente quedó fuera.
Evidencia, revisión y reinicio
- Predicción de rutas finales y grafo de imports antes del cambio.
- Salida de pytest y conjunto método/path de OpenAPI antes y después.
- Diff que mueve un grupo de operaciones sin añadir funcionalidad.
Revisión: con Codex, utilizando la evidencia y los criterios anteriores; no se publica una solución oficial.
Reinicio: Restaurar el módulo monolítico descargado y borrar los paquetes creados; no existe estado externo.
Material descargable opcional
- Mapa de refactor y contratoMonolito reducido, tests existentes y lienzo para predecir paths e imports.
Teoría que puedes consultar
Checkpoint independiente · EX-B3-S01-01
Repite la modularización en otro dominio
En un dominio distinto y sin una secuencia de archivos prescrita, modularizas la aplicación y justificas su grafo de imports.
EX-B3-S01
Flujo de solicitudes de cumplimiento
Recibes una API distinta a la del ejemplo: registra solicitudes de cumplimiento, permite consultarlas y tomar una decisión. Funciona y está probada, pero vive en un único módulo.
Abrir práctica extendida: base común y 1 parte
Base común
- El starter arranca y sus tests deben estar verdes antes de editar.
- El contrato público y los datos sintéticos son distintos al ejercicio guiado.
- Cada parte declara qué mecanismo puede añadirse; persistencia, autenticación, servicios y auto-registro siguen fuera.
- Los criterios dicen qué conservar, no qué carpetas debes crear.
Paquete de trabajo
- Starter de solicitudes de cumplimientoAplicación monolítica funcional, tests de regresión, brief y plantilla de evidencia; sin solución modular.
Entorno, evidencia y reinicio
Entorno: Python 3.10+ en un workspace temporal; FastAPI, Pydantic 2, pytest y httpx2 instalados por el paquete con uv.
- Commit o diff del refactor sin cambios funcionales.
- Salida completa de pytest antes y después.
- Fingerprint OpenAPI y diagrama de imports final.
Revisión: con Codex, utilizando los artefactos y criterios de cada parte; no se publica una solución oficial.
Reinicio: Descomprimir de nuevo el starter en otro directorio temporal; cada intento empieza con el monolito y sus tests verdes.
Parte 1 · Apoyo autónomo
EX-B3-S01-01 · Separar routers y schemas
Modularizar un contexto nuevo sin copiar el árbol del ejemplo y defender cada frontera mediante evidencia de regresión.
Qué debes hacer
- Sin abrir las pistas de EX-B3-01, predice las rutas finales y dibuja el grafo de imports que quieres conseguir.
- Ejecuta el baseline y guarda el fingerprint de operaciones y operationId.
- Separa composición, frontera HTTP y schemas con la estructura mínima que puedas justificar.
- Repite la regresión, inspecciona OpenAPI y documenta una separación que decidiste no hacer.
Entrega esperada
- Repositorio refactorizado sin funcionalidad nueva.
- before-after.md con comandos, resultados y diferencias observadas.
- Diagrama breve de imports y nota de decisión arquitectónica.
Criterios de aceptación
- Los seis tests suministrados pasan antes y después.
- Los cuatro pares método/path y sus operationId no cambian.
- No hay imports desde routers o schemas hacia main.py.
- La aplicación registra routers de forma explícita y un módulo omitido desaparece de OpenAPI de forma diagnosticable.
- La entrega explica por qué la estructura elegida es suficiente ahora y dónde se detuvo el refactor.
Evidencia para revisión
- Predicción y fingerprint previos al cambio.
- Diff, árbol y grafo de imports finales.
- Misma salida de pytest y mismo contrato público al terminar.
Reinicio de esta parte: Volver a descomprimir el starter; no reutilizar módulos del ejercicio guiado.
Teoría que puedes consultar
Transferencia: Ante otra aplicación monolítica, puedes decidir qué separar ahora, qué dejar junto y cómo demostrar compatibilidad.
Paso 6 · Retirada gradual del apoyo
Observa cada reloj por separado
Evidencia: OpenAPI conserva sus parámetros, la configuración falla pronto y el recurso se libera tanto en éxito como en error.
EX-B3-03
Construir y observar un árbol de dependencias
Dos ramas de una consulta de informes repiten headers y normalización de correlación. El contrato funciona, pero no permite observar ni reutilizar el requisito común.
Pensar y responder en la web
Predice y guarda aquí tu razonamiento antes de abrir el material; después contrástalo en el workspace.
Pregunta central
¿Qué nodo debe ser común a ambas ramas, qué valor entrega cada proveedor y qué evidencia distingue la caché de una ejecución accidental?
Contrato que el árbol no debe ocultar
Baseline: GET /reports exige X-Correlation-Id y X-Organization-Id, acepta page_size entre 1 y 100 y devuelve la misma representación pública.Para ordenar tu razonamiento
- ¿Qué header y query deben seguir apareciendo en OpenAPI?
- ¿Qué hoja comparten el contexto de consulta y el de auditoría?
- ¿Qué cambia al enviar una segunda petición?
Trabajo en el workspace
- Ejecuta el baseline, inspecciona OpenAPI y dibuja el árbol que esperas.
- Extrae una hoja de correlación y dos proveedores que la compartan; inyecta sus valores de forma explícita.
- Añade una traza controlada y un test que pruebe una ejecución de la hoja en una petición y otra ejecución en la siguiente.
- Repite la regresión y compara los parámetros públicos antes y después.
Entrega esperada
- Código refactorizado y tests añadidos.
- Árbol previsto frente a traza observada.
- Fingerprint de parámetros OpenAPI y salida de pytest antes/después.
Escribe lo que piensas. Se guarda automáticamente en este navegador.
Sin respuesta guardada.
Pistas opcionales
Pista 1 · Empieza por la hoja
- Las dos ramas necesitan el mismo valor de correlación ya validado.
- El resultado de una dependencia puede alimentar otra dependencia igual que alimenta una operación.
Pista 2 · Prueba dos escalas
- Una aserción dentro de una petición prueba reutilización.
- Otra petición debe probar que la caché anterior ya no existe.
Profundización opcional
Extraer un árbol pequeño con una subdependencia compartida y demostrar su caché por petición sin alterar el contrato HTTP u OpenAPI.
Contexto adicional
- Conserva status, body, nombres de parámetros y validaciones públicas.
- Usa Annotated para declarar valores concretos; no inyectes un diccionario contenedor.
- No uses use_cache=False: la capacidad objetivo es observar el comportamiento por defecto.
Decisiones abiertas
- Puedes ubicar los proveedores junto al router o en un módulo propio; justifica la dirección de imports.
- Puedes trazar con una lista, un contador o un fake, siempre que la prueba restaure su estado.
Cómo revisar tu respuesta
- El header X-Correlation-Id, X-Organization-Id y page_size conservan nombre, obligatoriedad y límites.
- La hoja compartida se ejecuta una vez dentro de una petición aunque dos ramas la necesiten.
- Una segunda petición produce una nueva ejecución; no existe caché entre peticiones.
- La operación recibe valores tipados y no busca nombres dentro de un contenedor genérico.
- Todos los tests de contrato permanecen verdes.
Evidencia, revisión y reinicio
- Árbol previsto y traza real de resolución para dos peticiones.
- OpenAPI y tests HTTP antes y después de extraer los proveedores.
- Test que demuestra una sola ejecución de la subdependencia compartida por petición.
Revisión: con Codex, utilizando la evidencia y los criterios anteriores; no se publica una solución oficial.
Reinicio: Volver a descomprimir ex-b3-03-dependency-tree.zip; el paquete no usa red, base de datos ni estado externo.
Material descargable opcional
- Starter del árbol de dependenciasAPI con entradas repetidas, tests verdes y plantilla de traza; sin proveedores extraídos.
Teoría que puedes consultar
EX-B3-04
Validar settings y sustituirlos en tests
Una API publica información operativa desde constantes codificadas. Los tests modifican globals y dependen del orden en que se ejecutan.
Pensar y responder en la web
Predice y guarda aquí tu razonamiento antes de abrir el material; después contrástalo en el workspace.
Pregunta central
¿Qué valores deben llegar desde el entorno, cuándo deben fallar y cómo puedes probar otro perfil sin editar la ruta ni conservar estado global?
Frontera de configuración
Baseline: GET /runtime devuelve app_name, environment y max_page_size desde constantes; audit_channel es obligatorio pero nunca se expone en la respuesta.Para ordenar tu razonamiento
- ¿Qué campo debe ser obligatorio y qué default es seguro?
- ¿Qué prefijo evita colisiones con variables ajenas?
- ¿Qué estado debes restaurar al terminar el test?
Trabajo en el workspace
- Clasifica cada valor como regla estable, configuración pública o configuración sensible.
- Crea Settings con prefijo explícito, validación y un proveedor cacheado por proceso.
- Sustituye el proveedor en un test y demuestra que el siguiente recupera el comportamiento normal.
- Añade una comprobación que falle al construir Settings si falta audit_channel y documenta cuándo se ejecuta esa validación.
Entrega esperada
- Módulo de configuración, integración y tests.
- Matriz de fuentes y precedencia observada.
- Salida del fallo esperado y evidencia de aislamiento entre tests.
Escribe lo que piensas. Se guarda automáticamente en este navegador.
Sin respuesta guardada.
Pistas opcionales
Pista 1 · Dos cachés
- FastAPI reutiliza un nodo dentro de una petición.
- functools.lru_cache conserva el resultado del proveedor entre llamadas del mismo proceso.
Pista 2 · Sustituye la función
- La clave del mapa de overrides es el callable original, no el tipo Settings.
- Limpia el mapa aunque la aserción falle.
Profundización opcional
Modelar configuración externa con pydantic-settings, distinguir caché de proceso y sustituir el proveedor sin contaminar otras pruebas.
Contexto adicional
- Usa BaseSettings desde pydantic_settings y SettingsConfigDict.
- El archivo .env es solo local y no puede entrar en el entregable; publica únicamente .env.example sin valores sensibles.
- No reconstruyas Settings en cada request y no uses monkeypatch sobre la ruta.
Decisiones abiertas
- Puedes validar settings al construir la app o mediante un smoke check de arranque; documenta el momento exacto del fallo.
Cómo revisar tu respuesta
- Los nombres de entorno usan un prefijo propio y los valores se validan con tipos y límites.
- audit_channel es obligatorio, no aparece en respuestas o logs y su ausencia falla de forma explícita.
- El proveedor no relee el entorno en cada petición y la entrega distingue caché de proceso de caché de Depends.
- El test reemplaza get_settings mediante dependency_overrides y restaura el mapa en finally o una fixture equivalente.
- No se publica .env ni un secreto real; .env.example solo contiene nombres o valores ficticios.
Evidencia, revisión y reinicio
- Matriz de fuentes y valores elegidos para local, test y producción simulada.
- Fallo de validación con configuración obligatoria ausente.
- Test con dependency_overrides restaurado después del intento.
Revisión: con Codex, utilizando la evidencia y los criterios anteriores; no se publica una solución oficial.
Reinicio: Volver a descomprimir ex-b3-04-settings.zip y eliminar solo el .env local creado para el intento.
Material descargable opcional
- Starter de settings por entornoAPI con constantes, tests verdes, matriz vacía y .env.example seguro; sin modelo Settings.
Teoría que puedes consultar
EX-B3-09
Demostrar el ciclo de vida de un recurso
Una ruta abre y cierra manualmente un audit sink. Otro endpoint omite el cierre al lanzar una excepción y la prueba solo comprueba el status.
Pensar y responder en la web
Predice y guarda aquí tu razonamiento antes de abrir el material; después contrástalo en el workspace.
Pregunta central
¿Qué eventos deben aparecer antes, durante y después de la operación, y qué aserción demuestra que el cleanup no depende del camino feliz?
Secuencia que debes demostrar
Traza objetivo por petición: open → use → close. El caso /audit/fail conserva su error y aun así termina en close.Para ordenar tu razonamiento
- ¿Qué ocurre antes de yield?
- ¿Qué valor recibe la operación?
- ¿Qué excepción no debe desaparecer durante el cleanup?
Trabajo en el workspace
- Predice la traza de éxito y de fallo antes de ejecutar.
- Reproduce el recurso abierto después del error y conserva la evidencia.
- Introduce una dependencia con yield que libere en finally e inyéctala en ambas operaciones.
- Prueba éxito, fallo, aislamiento entre peticiones y orden de la traza.
Entrega esperada
- Código corregido y tests de lifecycle.
- Cronología prevista y observada para ambos caminos.
- Explicación del scope usado y de una alternativa descartada.
Escribe lo que piensas. Se guarda automáticamente en este navegador.
Sin respuesta guardada.
Pistas opcionales
Pista única · Protege la salida
- Crea el recurso antes de yield.
- Coloca close en finally y deja que la excepción siga su curso.
Profundización opcional
Mover adquisición y liberación a una dependencia con yield y demostrar el orden exacto en éxito y fallo.
Contexto adicional
- El recurso es un fake local; no añadas base de datos, red ni transacciones.
- Usa try/finally y no conviertas el error deliberado en una respuesta exitosa.
- Mantén la política por defecto de scope y explica cuándo se ejecuta el cierre.
Decisiones abiertas
- Puedes registrar la traza en el fake o inyectar un recorder separado, pero su estado debe aislarse entre tests.
Cómo revisar tu respuesta
- Cada petición abre un recurso nuevo y lo cierra exactamente una vez.
- La traza de éxito y la de fallo terminan en close.
- El error deliberado conserva su status y no es absorbido por la dependencia.
- La operación usa el valor cedido y no ejecuta cleanup manual.
- La prueba observa el estado después de completar el ciclo request/response.
Evidencia, revisión y reinicio
- Predicción temporal para éxito y excepción.
- Trazas open/use/close observadas por petición.
- Tests que prueban cleanup después de 200 y después de error sin tragarse la excepción.
Revisión: con Codex, utilizando la evidencia y los criterios anteriores; no se publica una solución oficial.
Reinicio: Volver a descomprimir ex-b3-09-yield.zip; el audit sink es un fake en memoria y no abre archivos ni conexiones.
Material descargable opcional
- Starter del ciclo de vidaAudit sink simulado, ruta con cleanup defectuoso, tests baseline y plantilla de cronología.
Teoría que puedes consultar
Checkpoint independiente · EX-B3-S01-02
Integra el árbol sin copiar los ensayos
En la API modular de cumplimiento, sustituyes repetición y globals por proveedores explícitos y pruebas sus límites sin un árbol prescrito.
EX-B3-S01
Flujo de solicitudes de cumplimiento
Recibes una API distinta a la del ejemplo: registra solicitudes de cumplimiento, permite consultarlas y tomar una decisión. Funciona y está probada, pero vive en un único módulo.
Abrir práctica extendida: base común y 1 parte
Base común
- El starter arranca y sus tests deben estar verdes antes de editar.
- El contrato público y los datos sintéticos son distintos al ejercicio guiado.
- Cada parte declara qué mecanismo puede añadirse; persistencia, autenticación, servicios y auto-registro siguen fuera.
- Los criterios dicen qué conservar, no qué carpetas debes crear.
Paquete de trabajo
- Checkpoint de proveedores y configuraciónAplicación modular con repetición y lifecycle manual, tests verdes y contrato estable; sin solución con Depends o Settings.
Entorno, evidencia y reinicio
Entorno: Python 3.10+ en un workspace temporal; FastAPI, Pydantic 2, pytest y httpx2 instalados por el paquete con uv.
- Commit o diff del refactor sin cambios funcionales.
- Salida completa de pytest antes y después.
- Fingerprint OpenAPI y diagrama de imports final.
Revisión: con Codex, utilizando los artefactos y criterios de cada parte; no se publica una solución oficial.
Reinicio: Descomprimir de nuevo el starter en otro directorio temporal; cada intento empieza con el monolito y sus tests verdes.
Parte 2 · Apoyo autónomo
EX-B3-S01-02 · Proporcionar contexto, settings y auditoría
Reemplazar repetición de frontera, constantes de entorno y cleanup manual por proveedores explícitos, conservando el contrato y demostrando cada alcance.
Qué debes hacer
- Documenta el árbol deseado y clasifica cada nodo como petición, proceso o recurso antes de editar.
- Conserva el contrato de headers, query, respuestas y OpenAPI mientras eliminas la repetición de contexto.
- Recibe configuración tipada desde fuentes externas y permite sustituir su proveedor en tests.
- Gestiona el audit sink mediante un ciclo con yield y prueba cierre en éxito y error.
- Entrega una matriz que conecte cada decisión con una prueba o traza observable.
Entrega esperada
- Repositorio refactorizado sin persistencia, seguridad ni funcionalidad nueva.
- Tests propios de caché por petición, settings/override y lifecycle.
- before-after.md con árbol, comandos, resultados, decisiones y límite de parada.
Criterios de aceptación
- Los ocho tests suministrados pasan antes y después y el fingerprint de operaciones y parámetros OpenAPI no cambia.
- La subdependencia común se ejecuta una vez por petición y vuelve a ejecutarse en otra petición.
- Settings usa pydantic-settings, prefijo explícito y un campo obligatorio que falla al faltar; ningún valor sensible se publica.
- El test sustituye el proveedor de settings y restaura overrides y cachés controladas.
- El audit sink se abre y cierra una vez en éxito y error mediante una dependencia con yield, sin ocultar la excepción.
- No hay recurso global mutable, service locator, use_cache=False sin necesidad ni imports de vuelta hacia main.py.
- La entrega explica por qué cada alcance es suficiente y dónde se detuvo la composición.
Evidencia para revisión
- Árbol y clasificación petición/proceso/recurso antes del cambio.
- Diff, tests añadidos y misma regresión HTTP/OpenAPI después del refactor.
- Trazas de caché y cleanup junto con un override de settings restaurado.
Reinicio de esta parte: Volver a descomprimir el checkpoint; eliminar el .env local del intento y no reutilizar código de los ensayos guiados.
Teoría que puedes consultar
Transferencia: Ante una sesión, un cliente HTTP o un usuario actual, puedes decidir qué debe vivir por petición, por proceso o fuera de Depends y cómo probarlo.