refactor(panorama/cities): resolución N+1, indexación unaccent y eliminación de patrón Fat Controller - #69
Merged
Ochoa-Stack merged 7 commits intoAug 1, 2026
Conversation
Documenta el comportamiento N+1 actual de get_compare (confirmado: 15 queries exactas para 5 skills, 3 por skill). Este test se actualiza, no se elimina, cuando la Ronda 3 introduzca los métodos batch que reduzcan el conteo a 3 queries. Co-authored-by: Oscar Soriano <neko.dev@outlook.com> Co-authored-by: Aylin Chavira <aylinchavirachv@gmail.com> Co-authored-by: Alejandro Balderrama <alejandro64.bp@gmail.com>
…shots Agrega SkillRepository.get_by_ids, TrendSnapshotRepository.get_by_skill_ids y TrendSnapshotRepository.get_latest_by_skill_ids (via DISTINCT ON) para resolver DT-26. Sin consumidores todavía; panorama_bp.get_compare se migra en la siguiente ronda. Co-authored-by: Oscar Soriano <neko.dev@outlook.com> Co-authored-by: Aylin Chavira <aylinchavirachv@gmail.com> Co-authored-by: Alejandro Balderrama <alejandro64.bp@gmail.com>
Extrae la logica de get_compare a PanoramaService.get_compare_data, usando los metodos batch de la ronda anterior. Reduce el patron N+1 de 15 queries a 3 para 5 habilidades, verificado con test de caracterizacion. Comportamiento observable del endpoint sin cambios. Co-authored-by: Oscar Soriano <neko.dev@outlook.com> Co-authored-by: Aylin Chavira <aylinchavirachv@gmail.com> Co-authored-by: Alejandro Balderrama <alejandro64.bp@gmail.com>
Habilita la extension unaccent de PostgreSQL (via wrapper immutable_unaccent para permitir indexacion, dado que unaccent nativo es STABLE no IMMUTABLE) y agrega CityRepository.find_by_normalized_name, verificado en 1 query indexada. Prepara la base para DT-27: get_or_create_city y _normalize siguen intactos, se reconectan en la siguiente ronda. Co-authored-by: Oscar Soriano <neko.dev@outlook.com> Co-authored-by: Aylin Chavira <aylinchavirachv@gmail.com> Co-authored-by: Alejandro Balderrama <alejandro64.bp@gmail.com>
Resuelve DT-27. CityRepository queda reducido a persistencia pura (find_by_normalized_name, get_by_name, create heredado). CityService nuevo orquesta busqueda indexada + Nominatim + persistencia, capturando ConflictError para condiciones de carrera bajo la constraint unique de City.name. ingestion_service.py actualizado para consumir CityService. 11 tests fallan intencionalmente en este punto (esperado, corregidos en la siguiente ronda): 6 en test_city_repository.py probaban el metodo eliminado, 5 en test_ingestion_service.py mockeaban la ruta antigua. Co-authored-by: Oscar Soriano <neko.dev@outlook.com> Co-authored-by: Aylin Chavira <aylinchavirachv@gmail.com> Co-authored-by: Alejandro Balderrama <alejandro64.bp@gmail.com>
test_city_repository.py reducido a persistencia pura (get_by_name). test_city_service.py nuevo: migra los 6 tests de orquestacion con mocks corregidos al namespace correcto, mas un test nuevo de recuperacion ante ConflictError por condicion de carrera concurrente. test_ingestion_service.py: 5 mocks corregidos de CityRepository a CityService. Suite completa: 140 tests, cero regresiones. Co-authored-by: Oscar Soriano <neko.dev@outlook.com> Co-authored-by: Aylin Chavira <aylinchavirachv@gmail.com> Co-authored-by: Alejandro Balderrama <alejandro64.bp@gmail.com>
Ochoa-Stack
force-pushed
the
refactor/panorama-service-and-city-decoupling
branch
4 times, most recently
from
August 1, 2026 04:27
5d0ae52 to
f7733ae
Compare
…ints Extrae get_skills, get_catalogs, get_summary, get_top_skills, get_trends, get_geo y get_salaries a PanoramaService. panorama_bp.py ya no llama a ningun repositorio directamente en ningun endpoint. SkillRepository.get_by_id y TrendSnapshotRepository.get_top_skills permanecen sin modificar dado que tienen consumidores externos (profile_bp.py, alerts_service.py, profile_service.py), confirmado sin cambios via git status. Cierra el alcance completo de esta rama: DT-26, DT-27, dos hallazgos sin numerar (transaccion movida fuera de CityRepository, busqueda O(n) reemplazada por indice unaccent). Suite completa: 140 tests, cero regresiones acumuladas en las 7 rondas. Co-authored-by: Oscar Soriano <neko.dev@outlook.com> Co-authored-by: Aylin Chavira <aylinchavirachv@gmail.com> Co-authored-by: Alejandro Balderrama <alejandro64.bp@gmail.com>
Ochoa-Stack
force-pushed
the
refactor/panorama-service-and-city-decoupling
branch
from
August 1, 2026 16:29
f7733ae to
83e6c63
Compare
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
refactor/panorama-service-and-city-decoupling
Descripción
En este ciclo de reestructuración arquitectónica profunda, mitigamos problemas severos de rendimiento ocasionados por consultas iterativas a la base de datos, delegamos la orquestación de la geocodificación a la capa de servicios adecuada e implementamos índices funcionales para optimizar las búsquedas textuales. Culminamos el proceso despojando a los endpoints de la capa de red de cualquier responsabilidad de acceso directo a persistencia.
Detalles técnicos que integramos:
DISTINCT ON. Se migró la lógica haciaPanoramaService, reduciendo el impacto en base de datos a solo 3 consultas, sin modificar el contrato de respuesta hacia el cliente.unaccenten PostgreSQL. Para soportar su indexación (dado que la extensión nativa esSTABLEy noIMMUTABLE), implementamos un wrapper inmutable que permitió reemplazar la búsqueda lineal de nombres de ciudades O(n) por consultas respaldadas por índices reales en la base de datos.CityRepositorya un componente de persistencia pura, eliminando transacciones acopladas y llamadas a redes externas. La interacción con Nominatim y la resolución geográfica fue encapsulada dentro deCityService. Inyectamos tolerancia a fallos frente a condiciones de carrera concurrentes capturando de forma explícita el error de colisión de llave única (ConflictError).panorama_bp.py) delegaron la totalidad de su lógica de negocio y consultas directas haciaPanoramaService. El controlador ahora funge estrictamente como gestor del contrato HTTP (entradas y códigos de estado).Tipo de cambio
Cómo probar
cd backend), activamos el entorno virtual.unaccent):flask db upgradepytest -v/api/panorama/comparecomprobando que los resultados arrojados permanecen idénticos, a pesar de que el costo computacional subyacente bajó drásticamente.Checklist