BookinglyTech News
Software

El patrón Strategy para separar visibilidad por rol de los filtros de búsqueda

Paolo Urbano publica un repositorio con dos versiones del mismo buscador SQL: la que crece a base de if y la que compone visibilidad por rol y filtros en fragmentos independientes.

3 min de lecturaDev.to0 vistas

Cada aplicación de negocio tiene esa pantalla: una tabla de registros, unas cajas de filtro encima y un usuario que solo puede ver una parte de los datos. Paolo Urbano defiende en un artículo que esa consulta nunca fue una cadena de texto, sino lógica de aplicación con una frontera de seguridad dentro, y ha publicado el repositorio search-query-composition-demo con las dos versiones del mismo buscador sobre el mismo esquema, probadas contra un SQL Server real con Testcontainers.

El stack: Spring Boot 4.1.1, Spring Framework 7.0.9, Flyway 12.4.0, Testcontainers 2.0.5, el driver JDBC 13.4.0 de Microsoft, SQL Server 2025 sobre la imagen mcr.microsoft.com/mssql/server:2025-CU9-ubuntu-24.04 y Java 21. Sin JPA: Spring JDBC, NamedParameterJdbcTemplate y records. Para levantar las pruebas hace falta Docker.

El dominio es una plataforma documental con oficina nacional, oficinas regionales y oficinas locales, modeladas en org_unit con un parent_id. La excepción son las unidades charter, que dependen directamente de la nacional: un supervisor regional solo las ve si tiene una delegación explícita y fechada. Cinco roles deciden la visibilidad —oficial local, supervisor regional, administrador nacional, auditor (solo APPROVED o ARCHIVED) y delegado— y encima hay diez filtros opcionales, varios de los cuales necesitan joins.

Lo que falla cuando la consulta se escribe a base de if

La versión que crece sola mete todo en un método con un StringBuilder, un if por rol y otro por filtro. Funciona, pero las reglas no son invocables: que un supervisor vea las charter solo con delegación activa son cinco llamadas a append dentro de un else if, así que no hay nada que probar aislado ni que reutilizar cuando una consulta de conteo o una exportación necesiten la misma visibilidad. Los predicados comodín obligan además a compartir un único plan en caché para todas las combinaciones de filtros, compilado con los valores de la primera llamada, lo que empuja al optimizador hacia escaneos. SQL Server 2025 añade la optimización de planes con parámetros opcionales, que cachea variantes según el estado NULL, pero cómo se porta con diez predicados opcionales es algo que hay que medir, no dar por hecho.

Y hay un detalle que duele más que el rendimiento: el email del autor se selecciona para todos los roles y se pone a null en el row mapper. Sale de la base de datos para quien no debería verlo, con un if de por medio. A eso se suman los LEFT JOIN que multiplican filas y el DISTINCT que lo tapa en lugar de evitarlo, o un rs.getLong sobre un id nullable que devuelve 0 cuando el valor es NULL.

Separar los dos ejes

La versión compuesta reparte responsabilidades. Un VisibilityScope decide qué puede ver el usuario: uno por rol, exactamente uno por búsqueda, nunca opcional. Un contribuidor de filtro decide qué pidió: uno por filtro, y cada uno resuelve si aplica. Los dos añaden fragmentos a un constructor común y ninguno ve la consulta entera. El patrón es Strategy, usado dos veces, y deja la frontera de seguridad como algo que se puede señalar en una revisión de código.

El interés para quien mantenga buscadores con permisos no está en el patrón en sí, sino en que la visibilidad deja de ser texto concatenado y pasa a ser código con pruebas. Queda por ver si el enfoque se sostiene cuando los fragmentos empiezan a depender unos de otros, algo que el repositorio no cubre: el ejemplo mantiene cada uno aislado. Lo que sí incluye es la comparación directa entre las dos implementaciones y las rarezas del original, para que salten a la vista al leer el diff.