Ir al contenido

La Jornada Dext: Evolucionando el Alto Rendimiento en el Backend Delphi

Dext v1 RC

El desarrollo de sistemas enterprise exige una evolución constante. En el Project Dext, nuestro objetivo nunca fue solo entregar un producto finalizado, sino construir una base sólida, moderna y de alto rendimiento para la comunidad Delphi. Hoy compartimos una evolución fundamental más: la etapa Release Candidate 1.0.

Esta actualización consolida meses de refinamiento en el motor del framework, trayendo recursos que antes parecían distantes del ecosistema Object Pascal.


✨ Features Principales: La Evolución Final antes de la v1.0

Sección titulada «✨ Features Principales: La Evolución Final antes de la v1.0»

Hemos simplificado drásticamente la exposición de datos y la ejecución de consultas complejas, llevando el ORM a un nivel de expresividad sin precedentes.

La funcionalidad de generar endpoints automáticos (“Database as API”) ha recibido una actualización significativa en su integración. Ahora, con MapDataApi, usted registra un CRUD completo y seguro directamente en el builder de la aplicación, con soporte nativo para inyección de dependencia y políticas de acceso.

// Una única línea para exponer su entidad con seguridad
App.Builder.MapDataApi<TProduct>('/api/v1/products',
DataApiOptions.RequireAuth.AllowReadWrite
);

Database as API

🔍 Dynamic Specification Mapping: El Cliente al Control

Sección titulada «🔍 Dynamic Specification Mapping: El Cliente al Control»

Integrado nativamente en Database as API, el filtrado vía QueryString permite que el cliente de su API construya consultas complejas sin que usted escriba una sola línea de lógica en el backend. Dext mapea sufijos inteligentes directamente al SQL.

Ejemplos de URLs poderosas:

  • GET /api/v1/products?price_gt=100&stock_lt=10 -> Filtra precio > 100 y stock < 10.
  • GET /api/v1/products?name_cont=Dext&_sort=id desc -> Busca nombres que contengan “Dext” con ordenación descendente.
  • GET /api/v1/products?category_in=1,2,5 -> Filtra por múltiples valores simultáneos.

Esto transforma su API en una herramienta flexible, reduciendo la necesidad de crear múltiples métodos de búsqueda para cada combinación de filtros.

🔗 Multi-Mapping ([Nested]): El “Dapper” de Delphi

Sección titulada «🔗 Multi-Mapping ([Nested]): El “Dapper” de Delphi»

Hemos implementado soporte nativo para Multi-Mapping inspirado en Dapper. Usando el atributo [Nested], Dext logra hidratar grafos de objetos complejos a partir de una única consulta SQL con joins.

Imagine cargar un TOrder que posee un TCustomer y este posee un TAddress. En Dext, esto se hace de forma recursiva y performante, mapeando la jerarquía de clases en un solo viaje a la base de datos.

var OrderProxy := Prototype.Entity<TOrder>;
var Orders := Db.Orders
.Include(OrderProxy.Customer) // Carga anticipada vía Proxy
.ThenInclude(OrderProxy.Customer.Address)
.ToList;

Multi-Mapping

🔒 Pessimistic Locking: Control Total de Concurrencia

Sección titulada «🔒 Pessimistic Locking: Control Total de Concurrencia»

El control de concurrencia ahora es nativo en la API fluente. Puede solicitar bloqueos a nivel de base de datos para transacciones críticas directamente en la consulta:

var Product := Db.Products
.Where(Prop('Id') = 1)
.Lock(TLockMode.Update) // Genera FOR UPDATE (PostgreSQL) o UPDLOCK (SQL Server)
.FirstOrDefault;

🛠️ Stored Procedures & Views: Ciudadanos de Primera Clase

Sección titulada «🛠️ Stored Procedures & Views: Ciudadanos de Primera Clase»

Sabemos que el mundo real exige el uso de lógicas complejas que residen en la base de datos. Dext ha simplificado drásticamente la interacción con Stored Procedures y Views.

  • Views: Mapee Views complejas exactamente como tablas, aprovechando todo el poder de la hidratación automática.
  • Stored Procedures: Mapeo declarativo usando atributos, con soporte para parámetros de entrada, salida y variados tipos de retorno.
type
[StoredProcedure('GetEmployeeSalary')]
TEmployeeSalaryDTO = class
private
FEmpId: Integer;
FSalary: Double;
FBonus: Double;
public
[DbParam(ptInput)]
property EmpId: Integer read FEmpId write FEmpId;
[DbParam(ptOutput)]
property Salary: Double read FSalary write FSalary;
[DbParam(ptOutput, 'p_bonus')]
property Bonus: Double read FBonus write FBonus;
end;

Esto garantiza que pueda utilizar recursos avanzados de bases de datos como SQL Server y PostgreSQL sin sacrificar el tipado fuerte y la elegancia de su código Pascal.


La refactorización del motor de Interception (Proxying) al Core trajo algo revolucionario: la capacidad de gestionar estados sin contaminar los campos (fields) de su clase. Es lo que llamamos Shadow Fields y Anonymous States.

Esto permite que tenga datos persistidos en la base de datos (como TenantId, AuditLog, o Claves Foráneas) que su código de dominio ni siquiera ve. La clase permanece como un POCO (Plain Old CLR Object) puro, mientras el ORM gestiona los metadatos “en las sombras”.

Ejemplo Práctico: Aislamiento de Tenant sin contaminar el Dominio Su clase de dominio no necesita saber que la Base de Datos exige un TenantId en cada fila:

[Table('Products')]
TProduct = class
public
[PK, AutoInc] property Id: Integer ...
property Name: string ...
// Observe: ¡NO existe el campo TenantId aquí!
end;

Mapeo Fluente (Shadow Property):

ModelBuilder.Entity<TProduct>
.ShadowProperty('TenantId') // ¡Propiedad fantasma!
.HasColumnName('tenant_id');

Lo que Dext hace en la Base de Datos:

SELECT Id, Name, tenant_id FROM Products WHERE tenant_id = :current_tenant

¿Cómo acceder al Shadow Field si es necesario? Puede leer o escribir estos valores vía API de Entry:

var TenantId := Db.Entry(Product).Member('TenantId').GetCurrentValue.AsInteger;

Esto trae a Delphi una funcionalidad similar a los Objetos Anónimos o Propiedades Dinámicas de frameworks modernos, permitiendo arquitecturas limpias donde la Base de Datos no dicta el diseño de sus clases.


🚀 Web & Performance: Streaming Zero-Allocation

Sección titulada «🚀 Web & Performance: Streaming Zero-Allocation»

El framework web ahora utiliza el TUtf8JsonWriter para el motor de Database as API.

  • Los datos se escriben en streaming directo desde la base de datos hacia el socket.
  • Zero-Allocation: Casi no hay asignaciones temporales de strings, lo que reduce drásticamente la presión en el Garbage Collector (o la fragmentación del Heap en Delphi) e aumenta el throughput masivamente.

Para demostrar el poder de Dext en escenarios del mundo real, creamos una nueva serie de ejemplos enfocados en Domain-Driven Design (DDD) simplificado y Pruebas Unitarias. Estos proyectos muestran cómo Dext se integra perfectamente en arquitecturas limpias y desacopladas.

Los nuevos ejemplos exploran la separación de responsabilidades, usando Dext para gestionar la persistencia sin que este dicte las reglas de negocio:

  • Web.SalesSystem & Web.FoodDelivery: Demuestran el uso de Services, Repositories y Domain Entities con validaciones ricas integradas al ciclo de vida del ORM.
  • Web.TicketSales: Enfocado en alta concurrencia y el uso de Pessimistic Locking para garantizar la integridad en la venta de boletos.
  • Web.HelpDesk: Un sistema completo de tickets que utiliza el motor de Database as API para agilizar el frontend, manteniendo lógicas personalizadas donde sea necesario.

Todos estos ejemplos vienen acompañados de suites de Pruebas Unitarias y de Integración. Mostramos cómo Dext facilita el “Mocking” y la creación de entornos aislados para pruebas, garantizando que su lógica de base de datos sea testeable y confiable.

  • MultiTenancy: Demostración técnica de aislamiento vía Schema (PostgreSQL) y vía Base de Datos con cambio dinámico de conexiones.
  • SmartPropsDemo: El uso avanzado de Prop<T> y Nullable<T> para persistencia transparente y control total de cambios (Dirty Tracking).
  • SQL Generator: Mejora en la generación de DDL, ignorando navigation properties en el CREATE TABLE.
  • TActivator: Priorización inteligente de constructores de clases derivadas.
  • Lazy Loading: Corrección de Access Violations en proxies de relaciones circulares.

El trabajo no se detiene. Ya iniciamos un refactor profundo en Dext.Collections. Nuestro objetivo es eliminar completamente la dependencia de los generics estándar de Delphi, atacando el “code bloat” (hinchazón del .exe) y elevando el rendimiento a un nuevo nivel de excelencia.

Dext Collections Mystery

La evolución es constante. Dext está apenas comenzando.