Ir al contenido

Dext Fluent Query: Ingeniería de Software y la Fuerza de la Comunidad

Dext Fluent Query

El desarrollo de un framework moderno como Dext no ocurre en el vacío. Es el resultado de un diálogo constante entre una ingeniería rigurosa y las necesidades reales de quienes están en el “frente” desarrollando aplicaciones complejas.

Recientemente, la Issue #117 de nuestro repositorio oficial trajo a la luz discusiones valiosas sobre la expresividad y la seguridad de los Joins en Fluent Query. Lo que comenzó como un pedido de mejora evolucionó hacia un análisis profundo, resultando en la implementación de mejoras inmediatas y en la creación de la especificación S19, que traza el futuro del motor de consultas de Dext.

En este artículo, exploraremos cómo Fluent Query eleva la calidad del código Delphi, el papel crucial de nuestra comunidad en este proceso y cómo estamos transformando el feedback en especificaciones técnicas de alto nivel.


1. Código de Calidad: Legibilidad que se traduce en Mantenibilidad

Sección titulada «1. Código de Calidad: Legibilidad que se traduce en Mantenibilidad»

El código que escribimos se lee muchas más veces de las que se escribe. Compare una concatenación de strings SQL tradicional con el patrón Fluent de Dext:

El “Estilo Antiguo” (Frágil y dependiente de strings):

Sección titulada «El “Estilo Antiguo” (Frágil y dependiente de strings):»
Query.SQL.Text := 'SELECT * FROM USERS WHERE AGE >= :MIN_AGE ORDER BY NAME ASC';
Query.ParamByName('MIN_AGE').AsInteger := MinAge;
Query.Open;
var u := Prototype.Entity<TUser>;
var Adults := Context.Entities<TUser>
.AsNoTracking
.Where(u.Age >= 18)
.OrderBy(u.Name.Asc)
.Take(50)
.ToList;

¿Por qué esto es calidad superior?

  • Type Safety Real (Paz Mental): Al usar u.Age, pones al compilador de tu lado. Si mañana renombras el campo Age a BirthDate en la clase TUser, Delphi no te dejará compilar el proyecto hasta que todas las consultas sean corregidas. Mejor aún: si usas una herramienta de refactorización, actualizará todas las referencias en tus consultas automáticamente, algo imposible con strings SQL. Olvida los errores de “Field ‘AGE’ not found” que solo aparecen cuando el cliente está usando el sistema.
  • Intellisense y Productividad: No necesitas memorizar el esquema de la base de datos. Al escribir u., el IDE te muestra exactamente qué campos están disponibles, reduciendo la carga cognitiva y eliminando errores de escritura.
  • Intención Clara: El encadenamiento de métodos (.AsNoTracking, .Where, .OrderBy) transforma el código en una documentación viva. Cualquier desarrollador (incluyéndote a ti mismo dentro de 6 meses) entiende la intención de la consulta en segundos.
  • Protección contra SQL Injection: Como Fluent Query trabaja con expresiones tipadas, Dext maneja la parametrización de forma nativa y segura bajo el capó. Tú escribes código Pascal, y el framework se encarga de la seguridad del SQL.

Seguridad de Tipos


2. El Ecosistema de Pruebas: Mucho más allá de lo básico

Sección titulada «2. El Ecosistema de Pruebas: Mucho más allá de lo básico»

Uno de los mayores diferenciales de Dext es que no es solo un ORM; viene acompañado de un Framework de Pruebas completo (Sources\Testing), diseñado para que Fluent Query se pueda probar con absoluta facilidad.

Gracias a la arquitectura basada en interfaces de Dext, puedes “mockear” tu base de datos sin esfuerzo. Usando Dext.Mocks, puedes interceptar llamadas y simular retornos complejos:

var u := TUser.Prototype;
var MockUsers := TList<TUser>.Create;
// Llenar lista simulada...
Mock<IDextContext>.Setup
.Returns(MockUsers)
.When.Entities<TUser>;

2.2 Fluent Assertions: Código que se lee como inglés

Sección titulada «2.2 Fluent Assertions: Código que se lee como inglés»

Olvida los Assert.AreEqual genéricos. Con Dext.Assertions, tus verificaciones son expresivas y tipadas:

// Verificando resultados de la consulta con elegancia
Products.Should.HaveCount(3)
.AndAlso.Contain(SomeProduct)
.AndAlso.AllSatisfy(p.IsActive);

2.3 Snapshot Testing: Adiós al Boilerplate de Assertions

Sección titulada «2.3 Snapshot Testing: Adiós al Boilerplate de Assertions»

Para consultas que devuelven objetos complejos o grandes payloads JSON, Dext ofrece Snapshot Testing. En lugar de escribir 50 aserciones manuales, tomas una “foto” del resultado esperado:

// Compara el resultado de la consulta con un baseline JSON guardado en disco
MatchSnapshot(ResultList, 'ReporteVentas_Enero');

Dext utiliza un Runner moderno inspirado en xUnit/NUnit, permitiéndote escribir pruebas sin necesidad de herencias complejas, usando solo metadatos:

  • [Fixture], [Test], [Fact] para definir escenarios.
  • [TestCase(1, 2, 3)] para pruebas parametrizadas (Data-Driven).
  • [Category('Integration')] para filtrar ejecuciones en CI/CD.

Dext Testing

Ventaja para tu CI/CD: Como el ecosistema de pruebas de Dext es nativo e integrado, tus pruebas unitarias se ejecutan en milisegundos, garantizando que los cambios en el mapeo o en la lógica de filtros se validen instantáneamente.


3. TFluentQuery: Más que una Consulta, un Pipeline de Datos

Sección titulada «3. TFluentQuery: Más que una Consulta, un Pipeline de Datos»

Para entender el poder de Dext, es necesario mirar bajo el capó de TFluentQuery<T>. A diferencia de los enfoques tradicionales que ejecutan el SQL inmediatamente, Dext trabaja con Ejecución Diferida (Deferred Execution).

TFluentQuery<T> se implementó como un record. Esto significa:

  • Cero Asignación en el Heap: Crear una consulta no genera presión en el Memory Manager, ya que el estado se mantiene en el stack.
  • Ciclo de Vida Automático: Al ser un tipo valor (record), se libera automáticamente cuando sale de alcance.

3.2 Ejecución Diferida (Deferred Execution)

Sección titulada «3.2 Ejecución Diferida (Deferred Execution)»

La consulta es solo un “blueprint” (proyecto). Puedes componer tu lógica en múltiples etapas antes de disparar la ejecución real.

var u := TUser.Prototype;
var Query := Context.Entities<TUser>.AsNoTracking;
// Composición dinámica basada en reglas de negocio
if OnlyActive then
Query := Query.Where(u.IsActive = True);
if MinAge > 0 then
Query := Query.Where(u.Age >= MinAge);
// El SQL solo se genera y ejecuta AQUÍ:
var ResultList := Query.OrderBy(u.Name.Asc).ToList;

4. Simplicidad Compleja: La Ingeniería detrás de la DSL

Sección titulada «4. Simplicidad Compleja: La Ingeniería detrás de la DSL»

Para que la experiencia del desarrollador fuera de una simplicidad absoluta, la ingeniería interna de Dext tuvo que ser visceralmente compleja.

Dext es el resultado de una “pelea” saludable con los límites del lenguaje. No aceptamos el clásico “eso no se puede hacer en Delphi”. Para crear una DSL (Domain Specific Language) que fuera consistente en todo el framework — desde Fluent Query hasta Collections, desde Specification Pattern hasta Expressions — fue necesario orquestar una sinfonía técnica:

  • Generics & Extended RTTI: Para garantizar abstracciones poderosas sin perder el tipado.
  • Managed Records & Operator Overloading: Para crear la sintaxis fluida y “zero-allocation” que ves en las consultas.
  • Class Functions & Interfaces: Para una arquitectura de Inyección de Dependencia y Desacoplamiento de nivel enterprise.
  • Public Functions & Audacia: El uso creativo y riguroso de recursos introducidos desde Delphi 2009, llevados al límite a través de cientos de pruebas unitarias y una autocrítica constante.

Esta consistencia significa que, una vez que aprendes a usar Fluent Query, ya sabes cómo filtrar una colección en memoria o cómo definir una Specification para una API Web.

El Arte de la Simplicidad Compleja

Imagina el poder de definir una regla de filtro compleja en tu Frontend (o capa de servicio) y pasarla directamente al Backend, que la traduce en SQL optimizado de forma segura. Esto evita la creación de docenas de endpoints específicos para cada variación de consulta, manteniendo tu código limpio, tipado y escalable. Es un ecosistema único, donde la complejidad interna trabaja silenciosamente para garantizar tu productividad.


5. Rendimiento Real y Arquitectura “Dialect-Aware”

Sección titulada «5. Rendimiento Real y Arquitectura “Dialect-Aware”»

No sirve de nada ser bonito si no es rápido. El pipeline de Fluent Query fue diseñado para un alto rendimiento:

  1. AsNoTracking: Reduce drásticamente el overhead de memoria en operaciones de lectura, ignorando el ciclo de vida de tracking cuando solo necesitas listar datos.
  2. SQL Caching: Dext identifica firmas de consultas repetidas y reutiliza el SQL generado, ahorrando valiosos ciclos de CPU.
  3. Calificación de Columnas (Issue #117): Recientemente mejoramos el generador de SQL para calificar automáticamente todas las columnas en escenarios de JOIN. Esto elimina el error clásico de ambiguous column name: Id que acecha a quienes hacen joins manuales.

6. Evolución Guiada por la Comunidad: La Issue #117

Sección titulada «6. Evolución Guiada por la Comunidad: La Issue #117»

Dext no se construye en una torre de marfil. La Issue #117 es un ejemplo perfecto de esto. Recibimos feedback sobre la necesidad de más claridad y ejemplos en los Joins SQL.

Lo que entregamos:

  • Mejora en el SQL Generator: Ahora más robusto contra ambigüedades.
  • Overloads Simplificados: Añadimos formas más fáciles de describir la condición de JOIN.
  • Documentación Reforzada: Ejemplos reales añadidos al Orm.EntityDemo y en nuestro “Book” oficial.

7. El Futuro: S19 y la Evolución de Fluent Query

Sección titulada «7. El Futuro: S19 y la Evolución de Fluent Query»

La evolución de Dext no se detiene en la V1. Durante el análisis técnico de la Issue #117, identificamos oportunidades para llevar la experiencia de consulta a un nuevo nivel. Así nació la especificación S19, que define el roadmap post-V1 para Fluent Query.

El objetivo de S19 es consolidar a Dext como el ORM más expresivo y performante del ecosistema Delphi. Entre las novedades planeadas, destacan:

  • DSL Totalmente Tipada para JOIN: Eliminación de strings en la cláusula ON, utilizando expresiones Delphi puras.
  • Claridad de Ejecución: APIs explícitas para distinguir Joins en base de datos de Joins en memoria (JoinInMemory), evitando materializaciones accidentales.
  • Proyecciones Ergonómicas: Nuevos patrones como SelectJoin<TResult> para mapear resultados complejos sin costo de reflection en tiempo de ejecución.
  • Diagnósticos y Observabilidad: Introducción de TagWith para rastreo de consultas y ToQueryString() para inspección inmediata del SQL generado y sus parámetros.

Para los arquitectos que deseen profundizar en los detalles técnicos y en las directrices de rendimiento de zero-allocation de la próxima fase, la especificación completa está disponible en: S19-FluentQuery-Join-Evolution.md


TFluentQuery<T> no es solo para CRUDs simples. Es la base para:

  • Informes Complejos: Proyecciones y agregaciones de alto rendimiento.
  • Lógica de Negocio Desacoplada: Repositorios que devuelven consultas, permitiendo que la capa de UI decida sobre la paginación o el ordenamiento.
  • APIs Modernas: Integración nativa con JSON y materialización asíncrona.

Al dominar Fluent Query, no solo estás escribiendo menos código; estás construindo aplicaciones Delphi preparadas para el futuro.


El Fluent Query de Dext trasciende el concepto de mero “Syntax Sugar”. Es una manifestación de ingeniería de software rigurosa, diseñada para inyectar seguridad de tipos, rendimiento brutal y testabilidad determinista en el corazón de las aplicaciones Delphi modernas.

Estamos moviendo Delphi de un pasado de “strings quebradizas” a un futuro donde la consulta a la base de datos es un ciudadano de primera clase, protegida por el compilador y abrazada por la comunidad.

Si tienes el coraje de abandonar la “forma de siempre” para adoptar una arquitectura que escala con confianza, Dext es tu próximo paso.

📚 Más información y Colaboración:

🚀 ¿Quieres colaborar? Consulta la Issue #117 y mira cómo tu participación puede transformar el ecosistema Delphi. El futuro de Dext lo construimos todos.


Publicado por: Equipo de Dext Framework Issue #117: Evolución Continua vía Feedback de la Comunidad