jueves, 28 de febrero de 2008

Estrategias para persistir objetos : ActiveRecord vs DataMapper

Persistencia : procedimiento que es ejecutado para lograr que los datos sobrevivan a la ejecución de un determinado programa.

Porque es necesario el uso de frameworks ORM (object relational mapping) para crear una capa de persistencia que acceda a los datos de nuestra aplicación?, si esta pregunta no tiene respuesta para ti, es aconsejable que vayas aquí.

Si en .Net tengo AdoNet 2.0, que me da la posibilidad de comunicarme con la BD, permitiendome recuperar los datos y copiarlos a un DataSet local, ligarlos a mis controles en la IU, y luego persistir los cambios, via SqlCommands ó DataAdapters que realizan las operaciones CRUD de Insert,Update, Select y Delete; no puedo generar una capa de persistencia con esta tecnología?.
Por supuesto que SI, y esta fue la apuesta de la gente de Microsoft hasta la version 2.0 de NET; seguir desarrollando software donde mis entidades de negocio son directamente las filas de las tablas de una BDR (Base de datos relacional). Para esto MS ha desarrollado una artillería de controles (datagrids, combobox, listviews, etc) drag and drop con wizards que facilitan enormemente la programación ligando mis entidades de negocio con mis IU (Win y WebForms). Lamentablemente este tipo de tecnología implica comenzar cada aplicación de software como si se tratase de la 1ra que realizamos, osea : casi nula reutilización de código aunque rapidez en el desarrollo y testeo.
Esto ha permitido que miles de personas puedan estar "capacitadas?" como programadores con un curso intensivo de 3 meses; los cuales sólo podrán seguir desarrollando con las tecnologías MS del tipo "WizarDragAndDropHappyProgramming" salvo que logren ver "más allá" y comiencen a investigar como funcionan los frameworks de desarrollo (Struts de Java, RubyOnRails, CakePhp) que triunfan en la actualidad y que implican mayor conocimiento de programación y arquitecturas de software, especialmente en aplicaciones Web donde ha triunfado largamente el uso del patrón de arquitectura MVC, no es casualidad que en la nueva versión 3.5 de NET se haya desarrollado un framework que aplique esta arquitectura .

Hablabamos de reutilización, la cual es imposible sin un diseño previo que tenga en cuenta los patrones de arquitectura, donde el 3 capas lógicas es el punto por donde comenzar; donde ubicar la persistencia?, pues en la capa de acceso a datos. Ahora si lo que queremos es trabajar con "objetos de negocio" (business objects ó BOs) en las otras 2 capas superiores y no con los DataSets como "entidades de negocio" que representan a las filas de una determinada tabla, deberemos usar algún framework ORM para que haga de intermediario entre los campos de las tablas relacionadas y nuestros objetos de negocios que responden a un Modelo del dominio.

Estos objetos de negocio deben representar el mayor esfuerzo de diseño en el desarrollo de un software (la M de Modelo en un MVC), si lo que deseamos es aplicar un enfoque DDD (Domain Driven Design).
DDD propone que el aspecto más complejo en los proyectos de software no es la implementación, sino el dominio del mundo real al cual el software le da servicio. Lograr que el objetivo principal del proyecto sea el modelo del negocio y mantener un modelo de software que refleje una profunda comprensión del mismo. Esta visión fue desarrollada por Eric Evans en su libro "Domain Driven Design".
Volviendo a la persistencia, podría NO utilizar ningún fwk y codificar mi propias clases ORM?, por supuesto, aunque si desarrollas con .NET ya existen varias posibilidades open source que son excelentes, entre las cuales son de destacar NHibernate y ActiveRecord de Castle; que aplican 2 patrones de persistencia distintos que son el motivo de este post.





Active Record

Propone tener los métodos que me brindan la persistencia en el mismo objeto de negocio, y normalmente las instrucciones del mapeo atributo del objeto-campo de la tabla en la definición del objeto de negocio. Por ej. en el ActiveRecord de Castle:


De esta forma podria crear un objeto Blog y persistirlo así:

Como vemos el uso del motor de persistencia es muy natural y sencillo de usar. El único problema que se plantea es que, desde el pto. de vista de la separación de responsabilidades de las capas; en este caso; estamos acoplando los BOs de la capa de negocios con la persistencia que debería estar en la capa de acceso a datos. Porque esto no es bueno?, porque perdemos independencia y será más costoso el cambio a otra tecnología de persistencia.

Esto es debido a que son nuestros objetos de negocio los que conocen como acceder a los datos persistidos mediante, en este caso, ActiveRecord de Castle; estos métodos de manejo de persistencia no deberían ser conocidos por nuestra capa de negocios, y suele decirse que estamos ensuciando nuestro código. Si a esto le sumamos las instrucciones de mapeo (ej: [PrimaryKey]), nuestras clases que crearán los BOs dejarán de ser POCO (plain old c# objects), y estarán influenciadas por cualquier cambio en la tecnología de acceso a datos subyacente.

Por último cabe aclarar que muchos de estos ActiveRecord, como por ejemplo el DooDads (cuyo código se crea desde MyGeneration), generan el modelo de clases a partir de las tablas y relaciones de la BDR ya creada, lo cual desde mi pto. de vista no es lo correcto, debido a que un modelo de clases es mucho más rico en posibilidad de asociaciones y navegabilidad que un esquema de tablas relacionadas.

Data Mapper

Propone que nuestros objetos de negocio sean POCO (ó POJO en Java), o sea que no tengan nada de código que los acople a una tecnología dada de persistencia, ya que no es su función manejarla. Los objetos de negocio solo deben preocuparse de cumplir con el modelo del dominio vía diseño del diagrama de clases con las asociaciones pertinentes.



Este es el caso de NHibernate, que propone manejar la persistencia a través de una clase administradora de la misma: Session. Esta clase es la que se encarga de hacer las operaciones CRUD a la BDR. Por ej. para persistir nuestro objeto de tipo Salon:

Para ver un tutorial sencillo y completo sobre NHibernate ver este post de Dario Quintana.

De esta forma todo lo que tenga que ver con el DataMapper (NHibernate) puede quedar aislado en su propia capa, fuera de la capa de negocio que posee nuestros BOs. Para poder realizar esta tarea en forma más transparente es necesaria la creación de clases DAO (data access objects) que implementen los métodos más comunes que realiza la capa de persistencia.
Por ej: (DAO del Salon)


Con los DAOs de cada BO trabajarìamos con el sig. còdigo en la implementaciòn de nuestros procesos:

Por supuesto que aprovechando Generics podriamos armar un HibernateDAOAbstracto que realice todas las operaciones comunes de persistencia y de esta forma nuestros DAO especìficos de cada BO heredarìan de èste. Para màs informaciòn al respecto ver el excelente ejemplo de Billy McCafferty.

En Resúmen...

Esta visto que con NHibernate debemos codificar màs, aunque logramos mayor independencia para poder cambiar los DAOs que se implementan con NHibernate por otro que se implemente con Linq Sql por ejemplo. Tambien es importante destacar que NHibernate es un framework con muchisimas funcionalidades (manejos de transacciòn, lazy loadings, Queries tipo HQL, Criterias, interceptores, etc), esto ha hecho que en el caso del ActiveRecord de Castle, se haya implementado usando NHibernate como API base ("no reinventar la rueda", no?).

Desde el punto de vista teórico de los 2 patrones (Martin Fowler. Patterns of enterprise application architecture ), el uso de un Data Mapper nos permite manejar toda la carga y almacenamiento entre el modelo de dominio y la base de datos, logrando que ambos varien independientemente. En el Active Record se mantiene una relacion de uno a uno entre clases y tablas, siendo más dificil la implementacion de modelos de negocio complejos.

Por último, el objetivo de este post no ha sido mostrar una tecnologìa de persistencia en particular, sino revisar los 2 patrones principales que se usan para implementarla y el porque de los beneficios de usarla en un diseño que se guie por un buen diagrama de clases del modelo de dominio de la aplicaciòn a desarrollar; en definitiva promover que los programadores dejemos de lado el uso directo de los registros de las BDR y usemos objetos de negocio en nuestros desarrollos, aprovechando una arquitectura que separa las responsabilidades y posibilite mayor reusabilidad y menor mantenimiento.

Me gustaria que este post sirva para concentrar comentarios sobre el tema de persistencia, y que puedan brindar aqui sus experiencias y dudas en su uso. Nos leemos..

5 comentarios:

Anónimo dijo...

hey hola me intereso mucho tu articulo ya que desde que conoci ruby on rails me di cuenta de que debemos trabajar con orm, he estado trabajando para crear un ORM nuevo y funcional. he tenido ya un intento fallido debido a que termino teniendo una sintaxis criptica y poco funcional pero ahora he tomado otro giro y me decidí por intentar buscar algo que se asemeje mas a la estructura de jquery y su chainability actualmente lo estoy desarrollando en PHP 4 pero no es problema ya que practicamente es independiente del lenguaje y podria pasarse a cualquier lenguje OO. me gustaria saber si se te ha ocurrido alguna idea sobre como deberia ser la interfaz, o almenos la forma de uso del ORM definitivo (la silver bullet jaja). saludos MUY BUEN BLOG

Anónimo dijo...

comunicame cualquier duda a picanteverde@msn.com

Anónimo dijo...

comunicame cualquier duda a picanteverde@msn.com

Jorge Ercoli dijo...

Raúl, realmente no me imagino armando un ORM sin un lenguaje OO.
Porque la idea es justamente armar un componente que contenga el modelo de dominio del negocio (los business objects); y luego un componente ORM que haga el mapeo entre estos BO y su/s tablas de BDR.
Para esto hay que solucionar innumerables cuestiones: identity map, lazy loading para los objs. asociados, mantenimiento de transacciones,...
Desde mi pto. de vista me convence el uso del patron "DataMapper" (ver Martin Fowler) que implementa una herramienta como Hibernate, por ser "No Intrusivo" en mi componennte de BOs, permitiendome "flexibilidad" en caso de decidir cambiar mi implementaciòn de persistencia de objetos por otra.
En el caso de RoR su ActiveRecord es muy bueno, aunque en este caso no hay opciones de cambio (inflexible) ya que el fwk. Rails se sustenta en su ORM; es un "toma todo el fwk. o déjalo".
Por último , la "silver bullet" es cualquier buen ORM que me permita independizarme lo mayor posible de mi Base de Datos Relacional.
Saludos.

elperucho dijo...

Saludos hermano,
La verda esta muy interezante tu articulo, yo actualmente utilizo el ORM - Cooperator Framework, me ha ido muy bien con el una vez que entiendes la idea del ORM, y sabes manejarlo, lo demas es solo desarrollar las interfas y las reglas del negocio, estas ultimas incluso dentro del ORM. Me gustaria aprender sobre NHIBERNATE, he visto que es muy utilizado y creo que tiene cosas o elementos que no realiza cooperator framework aun.

Saludos de nuevo y que buen articulo u blog tienes.