Por qué sigo usando un ORM

En el mundo del desarrollo de software, como en todas partes, las modas van y vienen. Cuando hace unos años (ya bastantes) surgieron los ORMs, con Hibernate para Java a la cabeza, se generó bastante discusión sobre si eran una solución adecuada al problema de la persistencia, con opiniones para todos los gustos y argumentos válidos tanto a favor como en contra.

Por una parte, estaban aquellos que consideraban que ceder el control del acceso a la base de datos a una librería daba lugar a soluciones subóptimas, con consultas SQL pesadas y patrones de acceso no recomendables.

Enfrente, había quienes llegaban a decir que no usar un ORM y escribir consultas las consultas a mano suponía robar a tus clientes porque implicaba dedicar tiempo a resolver un problema ya resuelto en lugar de centrarse en aportar valor.

Con el paso del tiempo, los ORMs ganaron la batalla y se convirtieron en mainstream. En el caso de .NET un factor clave para ello fue la aparición de Entity Framework, apadrinado por Microsoft, que hizo que la barrera de entrada a este tipo de herramientas fuese mucho más baja y hoy en día sea el estándar de facto para acceso a datos en .NET.

En paralelo, la necesidad de hacer aplicaciones con un rendimiento cada vez mayor fue haciendo que se volviesen a tener en cuenta los problemas asociados a las abstracciones introducidas por un ORM y se desarrollasen alternativas más ligeras, los MicroORMs, que permitían un acceso más explícito y controlado a la base de datos.

Si a eso le unimos el auge de las bases de datos NoSQL, especialmente de las bases de datos documentales del estilo de MondoDB o RavenDB, capaces de almacenar directamente grafos de objetos sin necesidad de hacer una traducción entre el modelo de objetos y el modelo de persistencia, hoy en día los ORMs vuelven a parecer una solución innecesaria, que sólo introduce complejidad y que penaliza el rendimiento de la aplicación.

Sin embargo, y pese a todos los argumentos en contra, a día de hoy sigo utilizando un ORM (NHibernate) en muchos de mis desarrollos. ¿Por qué?

¿Qué aporta un ORM?

A priori, un ORM se encarga de guardar y cargar información de una base de datos relacional de manera automática. Esto quiere decir que nos evita tener que escribir a mano las consultas SQL necesarias para persistir y recuperar nuestro modelo de objetos, y para muchos, este es el único motivo para utilizar un ORM.

Pero un ORM aporta otras cosas que, por mi forma de trabajar, son mucho más importantes que el evitar escribir sentencias SQL a mano.

Un ORM nos garantiza a través del identity map que siempre que cargamos una misma entidad en una sesión, ya sea a través de distintas consultas o navegando relaciones diferentes, obtengamos una referencia al mismo objeto.

Realizar manualmente esta gestión es complicado y podemos acabar teniendo en memoria dos instancias distintas para representar una misma entidad, cada una con valores diferentes, dando lugar a errores y obligándonos a tener mucho cuidado con la forma y lugar en que cargamos los datos.

Además, el ORM se encarga de gestionar la unidad de trabajo (Unit of Work) con la que podemos interactuar explícitamente (Save, Delete) pero también, y lo que es más importante, de forma implícita mediante la detección automática de cambios y la persistencia por alcance.

Esto nos permite trabajar cómodamente el modelo de objetos en memoria sin tener que llevar la cuenta manualmente de aquello que hemos modificado, ya que el ORM se encargará de detectarlo automáticamente y realizar las operaciones necesarias en la base de datos para persistir los cambios.

Un último punto que me resulta especialmente útil es poder centralizar en el ORM la definición del esquema de base de datos. Con ello conseguimos acercarnos al ideal de tener una única fuente de verdad de la que parte todo el conocimiento sobre nuestro modelo.

En lugar de mantener por separado un esquema de datos y por otro un modelo de objetos, el esquema de datos “emana” del modelo de objetos, facilitando que ambos se mantengan sincronizados.

Una ventaja adicional de esto es que es muy sencillo automatizar la creación y actualización de la base de datos, lo que es facilita mucho las cosas no sólo para el despliegue de la aplicación, sino también durante el desarrollo o los tests.

¿Cuál es el problema con los ORMs?

Todo lo anterior no es gratis y hay que ser consciente del precio que se está pagando por usar un ORM.

El primer problema, y al que más veces se hace referencia, es la pérdida de rendimiento, especialmente a la hora de cargar información. En realidad, muchas veces esta es una cuestión más de cómo se use el ORM que del ORM como tal. Todos los ORMs decentes que conozco permiten utilizar distintos sistemas de lectura, algunos de ellos tan optimizados cuyo rendimiento es indistinguible de lanza las consultas a mano.

Otro problema recurrente es la complejidad adicional que introducen y que, en muchos casos, resulta innecesaria. A fin de cuentas, escribir un par de selects y de inserts no es tanto trabajo, y tener que incluir una librería tan pesada como es un ORM para ello no parece que merezca la pena.

Pero el problema real con los ORMs es que se usan en aplicaciones sin un modelo de objetos. Un ORM, por definición, está diseñado para resolver un problema concreto, el mapeo entre un modelo relacional y un modelo de objetos, y hay muchas aplicaciones que no tienen (y probablemente no necesitan) un modelo de objetos.

Si tu modelo de “objetos” es un fiel reflejo de tu esquema de base de datos, con objetos que prácticamente sólo contienen datos sin operaciones asociadas, donde cada clase se corresponde exactamente con una tabla y los tipos de datos de las propiedades de esas clases son calcados a los tipos de datos de las columnas de las tablas, utilizar un ORM seguramente no sea la mejor solución y puedas obtener resultados muchos mejores con otro tipo de herramientas.

Esto no es algo malo, hay muchas aplicaciones que realmente no necesitan nada más y eso no las hace peores, ni necesariamente más simples. Muchas veces la complejidad de una aplicación no reside en un modelo con operaciones complicadas sino en ofrecer, por ejemplo, una excelente experiencia de usuario o manejar un volumen de datos enorme.

¿Por qué sigo usando un ORM?

El motivo por que sigo usando un ORM es porque, actualmente, la manera en que diseño la mayoría de las aplicaciones con las que trabajo es utilizando un modelo de objetos, y eso hace que todas las cosas que aporta un ORM y que mencionaba antes sean importantes para mi forma de trabajar.

Utilizar un ORM me permite centrarme en diseñar mi modelo de objetos sin tener que pensar demasiado en la forma en que lo persistiré y organizar toda mi aplicación alrededor de ese modelo de objetos. Para mi eso es un valor importante.

En realidad, esto nos llevaría a escalar la pregunta, ¿por qué diseñar una aplicación alrededor de un modelo de objetos?, pero esa es una pregunta diferente que depende de muchos factores, entre ellos el tipo de aplicaciones a desarrollar, el equipo de desarrollo, etc.

En mi opinión, una de las razones por las cuales los ORMs están empezando a tener “mala fama” en el mundo de .NET es Entity Framework. La excelente documentación que tiene y su facilidad de uso se ha convertido en un arma de doble filo que ha hecho que se adopte en muchos proyectos en los cuales no es necesario utilizar un ORM. Si a eso le unimos las carencias que todavía tiene a la hora de mapear modelos complejos (ausencia de tipos personalizados, imposibilidad de mapear objetos inmutables, falta de herencia implícita, etc.), hace que se acabe viendo el ORM como un DataMapper avanzado, cuando un ORM es mucho más que eso.


18 comentarios en “Por qué sigo usando un ORM

  1. Baldomero dijo:

    También utilizo NHibernate por las mismas razones que has expuesto y por una que no has indicado: me facilita mucho la lectura del código.

    El rendimiento es muy importante pero la mantenibilidad es clave y el objetivo debe ser buscar el equilibrio entre ambos.

  2. ¡Enhorabuena!
    Muy buen post. Estoy de acuerdo contigo en que una de las claves fundamentales es usar un ORM en las aplicaciones que presentan el problema que el ORM resuelve.
    Mala idea si pensamos que el ORM es “obligatorio” en nuestras aplicaciones.

  3. Cesar Verano dijo:

    Muy bueno el articulo, pero si tengo problemas para no pensar primero en los datos, podrias recomendarme algún texto que mw ayude a cambiar esta forma de pensar.

    Muchas gracias …

  4. Hola Juanma!

    Antes de nada agradecer de nuevo estos artículos tan interesantes. Crack!

    ¿No crees que esa mala fama de EF puede deberse al mal uso que le damos los desarrolladores? ¿Por desconocimiento real de su funcionamiento?

    Salu2!

  5. Hola César,

    Puedes leer esta serie de posts de Jimmy Bogard (http://lostechies.com/jimmybogard/2010/02/04/strengthening-your-domain-a-primer/), o investigar un poco sobre arquitectura hexagonal o ports-and-adapters (que también ponen el foco en el modelo).

    Con eso debería poder hacerte una idea de las posibles ventajas, aunque ten en cuenta que hay otras alternativas igualmente válidas y, en muchos casos, un enfoque centrado en los datos puede ser más adecuado.

    Un saludo,

    Juanma.

  6. Miguel Ángel,

    Como digo al final del post, yo creo que en gran parte la mala de Entity Framework viene del mal uso que se le da, viéndolo como si fuera el nuevo Data Access Application Block o algo parecido.

    De todas formas, si necesitas hacer un uso avanzado del ORM, a Entity Framework todavía le faltan cosas muy importantes (desde mi punto de vista), que es lo que le hace ganarse las críticas de otro perfil de usuarios, más acostumbrado a usar ORMs y con unas expectativas diferentes.

    Un saludo,

    Juanma.

  7. Gracias por hacer en voz alta este sano ejercicio de plantearte porque usas un ORM.

    Respecto al post un poco de debate para animar la charla ;). Me choca que inicialmente apuntas al auge de las bds NoSql y sus ventajas como uno de los orígenes de la “crisis” de los ORMs. Sin embargo un buen numero de los motivos que das para seguir usando un ORM creo que también son aplicables en un contexto con BD NoSQL con un programación mínima comparada con lo que un ORM hace para el mismo objetivo:

    * como un ORM, una bd Nosql te permite guardar y leer datos de tu modelo sin complicadas transformaciones como tu apuntas.
    * Un buen numero de bds Nosql igual que un ORM te permite centrarte en el modelo y centralizar como lo persistirás en otro lugar.
    * Un ORM te proporciona un UoW que para una bd SQL es complicado de implementar, sin embargo hacer un UoW para una bd NoSQL si la bd NoSQL que eliges te provee de un soporte mínimo para ello, es mas sencillo hacértelo tu mismo (claro q en el saco de NoSQL cabe de todo, por ejemplo Siaqodb lo provee y otras OO tb, de las orientadas a grafo o las documentales la verdad es que no estoy puesto para saber si proveen de lo necesario). Con la ventaja que esto tiene pues te permite meter mano en su funcionamiento y pone al alcance del desarrollador el entendimiento de esa caja negra que a menudo hace que se use erróneamente un ORM.

    Un abrazo y gracias por la calidad y regularidad de tu blog, un trabajo que visto con un poco de perspectiva es titánico! ;)

  8. Gracias Sergio.

    Técnicamente, un ORM mapea entre objetos y una base de datos relacional (Object/Relational Mapper), así que con una base de datos NoSQL (que no suele ser relacional) no tiene mucho sentido, por eso decía que el auge de estas bases de datos ha contribuido a la “crisis” de los ORMs.

    En cuanto a los puntos que comentas, estoy de acuerdo con ellos. De hecho hay bases de datos como RavenDB que tienen el concepto de UoW e Identity Map directamente integrados en su API (http://ravendb.net/docs/client-api/basic-operations/understanding-session-object).

    Un saludo,

    Juanma.

  9. Juan muy buen post, realmente lo hace a uno reflexionar sobre este tema tan importante y sobre le cual me gustaría realizar un par de comentarios.

    1. Me uno a tu posición de no usar un ORM para todo, un ORM no es la solución universal a los problemas de acceso a datos, es solo una opción a ser tenida en cuenta.
    2. EF tiene un gran problema, y es que te permite tener en par clicks todo “funcionando”, por lo que el desarrollador deja de preocuparse por su acceso a datos dejandole toda la responsabilidad al ORM, sin importar si este lo hace bien o lo hace mal.
    3. Por experiencia siempre recomiendo hacer un profiler de lo que tu ORM hace, así podemos identificar posibles problemas de rendimiento, gracias a la abstracción que genera un ORM, algunas veces hace cosas no muy buenas.
    4. EF ha mejorado mucho, recuerdo que cuando recien salio, de verdad era un terrible dolor de cabeza :)

    Saludos!

  10. Ah vaya que bueno, no sabia lo de RavenDb.
    Entonces digamos que partiendo de que tengo claro que quiero tener un modelo de objetos y persistirlo en bd. Más que decidir si uso ORM o no, la decisión será acerca de que bd me interesa mas por sus prestaciones, pq el pack bd SQL+ORM en ese caso parece indivisible (salvo excepciones q convenga saltarse ORM en una App, claro)

  11. pedrohurtado dijo:

    Vamos a ver, que yo creo que estamos perdiendo el norte:).

    Un ORM para un 3% de una app, para un UoW.

    Lo que pagamos no nos compensa y el problema es que seguimos con los mismos problemas que en los 90(Impedancia y no pequeña) y esto no es más que otra moda pasajera.

    No se como funciona una bb.dd, pues entonces un ORM que no tengo que hacer Join:).

    Pero si de verdad la conoces y no me digáis eso de abstracción o es que si cambias, porque en 20 años aún no he cambiado y todo esto de los ORM es para mi una perdida de tiempo de la cual obtengo pocos beneficios.

    Si a eso le agrego lo que me hace pasar EF y cosas como Actualizaciones, Delete e Insert después de un UoW no se hagan con Merge cuando funciona en todos los motores es para llorar.

    Con esto ya os digo cual es mi opinión de los ORM’s, una moda que se va a pasar antes de que alguien acabe una app seria y seria es hablar de 500 o 1000 entidades.

    Con lo cual Juanma si coincido contigo y es sencillo un ORM es para aplicaciones de juguete.

  12. GreenEyed dijo:

    Yo creo que la cosa patina un poco en el concepto de que están las aplicaciones “serias” (500 o más entidades) y el resto son “de juguete”.

    Lo que hay son problemas que hay que solucionar y apps que hacemos para solucionarlos. Y si requieren 500 o más entidades, pues se usan, y si requieren menos, pues se hacen menos. Y para cada caso, las personas inteligentes usan las herramientas adecuadas. Tan poco sentido tiene usar soluciones “escala amazon” para aplicaciones de un par de docenas de tablas, como al contrario.

    A mí, por suerte o por desgracia, me ha tocado lidiar tanto con aplicaciones grandes como con pequeñas y lo que sigo sin entender es ese desprecio por unas u otras, que he visto por ambos lados. Pero bueno, las opiniones, como los culos :).

  13. Hola Juanma,

    Yo creo que también existe un problema inherente del uso de un ORM y es que cuando pones un ORM en tu App hay gente que piensa que todo debe pasar por el ORM.

    ¿Qué sucede si una App q encaja perfectamente con ORM pero existe una operación donde se pretenden modificar 1000 entidades de golpe? Aquí hay que saber que no vale cargar las 1000 entidades, modificarlas y guardarlas, sino que muchas veces hay que saber la forma de saltarse el ORM en ciertos casos y “dejar para el ORM lo que es del ORM y a SQL lo que es de SQL”.

    Al final, el uso de los ORM es aparentemente tan sencillo que nos hace poner el piloto automático y hacerlo todo siguiendo el manual. Su sencillez no nos hace pensar en cómo esto se va a traducir a SQL y por tanto creamos consultas ineficientes, complejas, … La dificultad de saber que cierto artefacto de un ORM se va a traducir de una forma y esto va a provocar ciertos problemas de rendimiento es tan complejo que muchas veces los usamos sin pensar en sus consecuencias y por tanto nos metemos en unos líos…

    Para mi esto es lo más complejo de los ORM de los que soy un asiduo usuario por su sencillez y organización. Yo también estoy acostumbrado a trabajar sobre un modelo de clases que me lo aportan los ORMs.

    Saludos,

    Xavi Paper

  14. Hola Xavi,

    Coincido con tu visión. Un ORM no es “para todo”, ni siquiera en aplicaciones que lo usen.

    El problema, como tú dices, es que hay una tendencia a asumir que manejar un ORM permite “no saber” de SQL, pero la realidad es que un ORM, como cualquier abstracción, tiene agujeros y hace falta saber lo que hay por debajo y lo que está pasando.

    El que pretenda usar un ORM sin conocer SQL no va a conseguir buenos resultados, igual que el que pretenda hacer una aplicación web con ASP.NET MVC, Angular o lo que sea, va a tener problemas si no comprende el funcionamiento básico del protocolo HTTP y las implicaciones que tiene.

    Un saludo,

    Juanma.

  15. Pedro siempre consigues atrapar mi atención!

    Pedro escribió:
    “esto ya os digo cual es mi opinión de los ORM’s, una moda que se va a pasar antes de que alguien acabe una app seria y seria es hablar de 500 o 1000 entidades”

    Yo creo que el futuro de los ORM esta ligado a la evolución de las Bd NoSQL no al hecho de que sean o dejen de ser una “moda”.

    Quieres decir que el numero de tablas de una App determinan si es razonable o no usar un ORM? Y eso que con una de 1000 no se puede? No es un poco radical?
    Yo creo que si se puede, pero claro no de cualquier manera: modularizando verticalmente, teniendo múltiples contextos, teniendo entidades adaptadas a cada contexto y por supuesto no usando el ORM para todo sino que usando SQL y lo que la bd te ofrezca cuando te convenga … estamos de acuerdo?

    Pedro dijo:
    “no me digáis eso de abstracción o es que si cambias, en 20 años aún no he cambiado y todo esto de los ORM es para mi una perdida de tiempo de la cual obtengo pocos beneficios”

    Es verdad el tema de la abstracción para cambiar la bd fue una vendida de moto pq nadie cambia su bd esta “ventaja” de los ORM no fue tal pq las bds SQL mas extendidas ofrecían servicios similares por lo que hacer un cambio tan arriesgado nunca estaba justificado. Sin embargo relacionado con eso, ojo q la situación hoy no es la misma que años atrás: La irrupción de las bds NOSQL rompe con la homogeneidad de uso y de servicios que teníamos con las Bds SQL. El panorama NoSQL es aun cambiante y puede ser razonable querer meter tu propio UoW a modo de abstraccion entre tu App y la BD NOSQL q estes usando si aun no tienes claro que la bd que has elegido vaya a ser la definitiva.
    Aun asi salvo en fases tempranas de un proyecto es muy raro que cambies la BD.
    Algo diferente es que mas que cambiar de bd, necesites que tu código se este ejecutando en varias plataformas diferentes y ante la falta de una bd apta para todas las plataformas en esa situación la capa de abstracción no es una opción.

  16. Sergio León dijo:

    Hola,
    Yo creo que efectivamente no siempre un ORM será la mejor opción, casi nunca hay una mejor opción para todo, en eso estamos de acuerdo.
    En cuanto a las alternativas (y dejando a un lado las bd NoSQL) ¿Qué opciones hay? ¿Sólo Micro-orm? Personalmente sólo conozco Dapper y lo utilizo cuando quiero tener el control absoluto de la SQL que se va a tirar contra la bd (por temas de optimización de índices por ejemplo) o cuando L2E es tan enrevesado que me parece que una SQL en texto aportará más claridad… para todo lo demás, normalmente un ORM.
    A veces oigo que un ORM es pesado, pero no termino de entender este concepto (bueno, ese y otros muchos). Es sólo un paquete de Nuget y un par de referencias. Quizás en sistemas embebidos pueda ser pesado (donde la memoria y el espacio son vitales) , pero en el resto de casos no lo veo pesado (no más que un framework AR o similar).
    Yo creo que el gran problema de EF (porque no conozco NHibernate) es que es fácil ponerlo en marcha pero también es muy fácil no profundizar en él y cometer errores que, no son problema de EF sino de desconocimiento de la herramienta (SELECT N+1, el Identity Map que ha mencionado Juanma, etc.).
    Yo sinceramente para un CRUD (que en mi caso es más de la mitad de la aplicación, no sé si tristemente o no) no me planteo no usar un ORM. También es verdad que no tengo ningún problema en usar artefactos nativos de la bd (procedimientos almacenados, TVF, etc.). Son muchos años con el SQL tradicional para llegar ahora y echar todo ese conocimiento a la basura.
    Por último (y asumiendo que tiene relación con el post) lo que sí decidí hace algún tiempo es no usar el patrón repository ni UoW, soy de esos (y juro haberlo meditado todo lo bien que puedo) que cree que un repo es un DbSet y un UoW es DbContext. Abstracción de abstracción me impide utilizar en su plenitud EF y entonces sí empiezo a echar pestes sobre él a las primeras de cambio.
    Un saludo y mañana nos vemos! :)

  17. Silvio Moschen dijo:

    Muy buen post !!! Un ORM tiene una curva pronunciada para el aprendizaje pero, disminuye bastante la escritura de código, nos concentramos en los objetos (que es mas fácil a la hora de modelar) y nos evitamos estar embebiendo código SQL.

    En mi opinión, Hibernate es un claro vencedor, aporta una solución transparente, soporta un gran número de bases de datos, esta disponible para Java y para .NET, soporta lectura/escritura retrasada, muy buenos sistemas de Caché, se integra perfectamente con otros Frameworks como Spring y las cargas lazy (entre otras cositas) le otorgan un buen rendimiento, ademas, soporta SQL Nativo (si por alguna razón lo necesitamos), se pueden crear Entidades a partir de Vistas, Stored Procedures, etc.

    Otra ventaja son todas las opciones de consulta que provee, HQL, Linq, Criteria y… actualmente, ha abierto el abanico a las bases de datos NoSQL, tiene soporte actualmente para MongoDB.

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

*

Puedes usar las siguientes etiquetas y atributos HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>