Esquema estable
Las tablas y columnas se comparten y es posible que no sigan inmediatamente el nombre del dominio.
Estudio de caso arquitectónico · biblioteca de código abierto
Una tabla CUSTOMER expone CUST_NO y LEGAL_NM; el dominio necesita expresarse como Customer.Id y Customer.Name. FluentMap hace explícita esa traducción sin agregar atributos de persistencia a la entidad.
El escenario empresarial es ilustrativo. Demuestra contratos públicos de la biblioteca; no representa a un cliente, una implementación ni una mejora de rendimiento en producción.
Contexto y motivación
Los esquemas heredados contienen abreviaturas, convenciones históricas y nombres compartidos por sistemas que no pueden renombrarse en una sola entrega. Repetir esos nombres en el modelo .NET transfiere el costo de persistencia a las reglas de negocio, las API y las pruebas.
El objetivo no es ocultar SQL ni crear un ORM completo. La consulta y la conexión siguen bajo la responsabilidad de Dapper, mientras una configuración tipada y centralizada traduce los nombres físicos a propiedades que comunican intención.
Restricciones y atributos de calidad
Las tablas y columnas se comparten y es posible que no sigan inmediatamente el nombre del dominio.
Las entidades no reciben atributos que les permitan conocer el mecanismo de persistencia.
La línea 3.x conserva las principales API históricas de 2.x, con diferencias documentadas y nuevas funciones de suscripción.
La compatibilidad con Dapper, la certificación Core y la integración con Dommel son afirmaciones distintas.
Decisiones arquitectónicas
EntityMap<T> asocia expresiones de propiedad con columnas. El cambio de nombre de una propiedad pasa por el compilador y la configuración puede validarse al iniciar la aplicación.
Las convenciones reducen la repetición cuando el esquema sigue estándares. Los mapas explícitos siguen siendo adecuados para excepciones y tienen precedencia documentada.
Prioridad ↗La configuración global se construye antes de las consultas simultáneas y se trata como de solo lectura. FluentMapRuntime permite configuraciones aisladas en API QueryMapped*.
El paquete Dommel agrega metadatos y persistencia, pero mantiene el estado global en solucionadores y constructores. Los tiempos de ejecución aislados de FluentMap no aíslan a Dommel.
Integración Dommel ↗Los source generators registran mapas elegibles y materializadores para formas compatibles. No analizan SQL ni garantizan todas las formas de materialización; los casos no compatibles recurren a un fallback.
Source generators ↗Los analizadores detectan configuraciones observables en la compilación. El registro dinámico, la reflexión y el comportamiento dependiente del tiempo de ejecución quedan fuera de esta prueba.
Analizadores ↗Implementación del escenario
using Dapper;
using Dapper.FluentMap;
using Dapper.FluentMap.Mapping;
FluentMapper.Initialize(config => config.AddMap<CustomerMap>());
FluentMapper.Validate();
const string sql = """
SELECT CUST_NO, LEGAL_NM
FROM CUSTOMER
WHERE CUST_NO = @id
""";
Customer customer = connection.QuerySingle<Customer>(sql, new { id = 42 });
public sealed class Customer
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
}
public sealed class CustomerMap : EntityMap<Customer>
{
public CustomerMap()
{
Map(customer => customer.Id).ToColumn("CUST_NO");
Map(customer => customer.Name).ToColumn("LEGAL_NM");
}
}
El ejemplo utiliza la API documentada actual. La conexión, transacción, parametrización, consulta y migraciones siguen siendo responsabilidad de la aplicación y de Dapper.
El proveedor ADO.NET entrega a Dapper un lector con CUST_NO y LEGAL_NM. El mapa de tipos instalado por FluentMap resuelve estos nombres para Id y Name. Dapper crea y puebla Customer; FluentMap no rastrea la entidad ni cambia el SQL.
Alternativas y compensaciones
| Enfoque | Ejemplo | Ventaja | Costo y mejor contexto |
|---|---|---|---|
| Alias en SQL | SELECT CUST_NO AS Id, LEGAL_NM AS Name | Sin configuración adicional; intención visible en la consulta. | Repite alias en varias consultas. Es adecuado para una lectura puntual o para SQL controlado por una sola operación. |
| Persistencia DTO | CustomerRow.CUST_NO → Customer.Id | Límite y transformación totalmente explícitos; DTO puede reflejar uniones y proyecciones. | Más tipos y código de conversión. Bueno cuando la forma de persistencia difiere semánticamente del agregado. |
| FluentMap explícito | Map(x => x.Id).ToColumn("CUST_NO") | Reutiliza un mapa escrito y mantiene la entidad libre de atributos. | La configuración global requiere disciplina; no elimina SQL. Es bueno cuando las entidades reaparecen en múltiples consultas con nombres físicos estables. |
Evolución y mantenimiento
EntityMap<T>, FluentMapper.Initialize(...) y el puente del mapa tipo permanecen. Para la ruta histórica común, la migración tiende a consistir en actualizaciones, validación y pruebas de paquetes coordinadas.
El uso normal de Dapper.Query<T>() emplea el type map global de Dapper. Dommel también es global. El aislamiento de FluentMapRuntime se aplica a las API mapeadas que reciben el runtime o pertenecen a él.
SQLite, SQL Server, PostgreSQL, MySQL y MariaDB tienen evidencia real de Core y Dommel en la matriz actual. Oracle y Firebird tienen evidencia Core, pero no certificación Dommel.
Matriz de compatibilidad ↗El rango documentado actualmente es Dapper [2.1.79,3.0.0) y Dommel [3.5.3,4.0.0). Los canarios experimentales no amplían la matriz hasta que exista una actualización intencional y revisada.
Limitaciones y riesgos
La biblioteca no gestiona conexiones, transacciones, migraciones, seguimiento ni diseño de consultas. La configuración global tardía puede entrar en conflicto con consultas simultáneas; la inicialización debe ocurrir antes de su uso y ser tratada como inmutable.
La compatibilidad esperada por Dapper/ADO.NET no equivale a una certificación de FluentMap. La certificación de Core no implica la de Dommel. Tampoco se afirma compatibilidad completa con Native AOT: la ruta estricta generada tiene cobertura acotada y las formas incompatibles pueden usar fallback.
Los benchmarks usan BenchmarkDotNet y SQLite en escenarios definidos en el repositorio. La línea base versionada incluye "reportOnly": true; por lo tanto, sirve como evidencia exploratoria y detector de tendencias, no como promesa general de mejora de rendimiento entre entornos, providers o workloads.
Aprendizajes
El valor del mapa no está en ocultar la base de datos, sino en encontrar la traducción. Los alias, DTO y FluentMap siguen siendo opciones arquitectónicas; la frecuencia de reutilización, la diferencia semántica y la necesidad de aislamiento indican cuál cuesta menos.
En la evolución de la biblioteca, los recursos opt-in permiten ampliar la materialización, el diagnóstico y la integración sin obligar a los consumidores históricos a adoptarlo todo. Las matrices separadas y el lenguaje preciso impiden que la evidencia de las pruebas se generalice más allá de lo que realmente cubre.
Evidencias y referencias