Debate: Deben los Objetos de Negocio Incluir su Propia Lógica de Persistencia?

 Una  discusión recurrente se instaló en el Foro de Arquitectura en MSDN (que está en http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=228&SiteID=1)

Los objetos con lógica de dominio (o de negocio), deben o no contener sus propios métodos de persistencia?

El que abre el debate pregunta cuáles son los pros y contras de ambas alternativas: lógica de persistencia en el mismo objeto y fuera del mismo

Personalmente alguna vez me planteé esa duda muy fuertemente. Existencialmente. Hoy, con varias canas encima, varias batallas (por igual ganadas y perdidas) estoy en condiciones de compartir mis conclusiones. Que a la vez redondean debates que planteé en otros posts

Yo separo la lógica de persistencia de la clase que lleva la lógica de dominio modelado. Con eso gano en independencia: la lógica de dominio no está atada al mecanismo de persistencia

Por ejemplo, supongamos que en el servidor de producción, la base de datos donde se persisten los objetos está en un IBM 390 al que se accede no vía comandos tradicionales SQL, sino invocando servicios, posiblemente con un protocolo propietario (no el SOAP de los servicios web)

Levante la mano quién tiene ganas de meter toda esa lógica de comunicación (posiblemente a través de MS Host Integration Server o IBM CICS Transaction Gateway) visible desde el objeto de negocio

En estos casos en que la plataforma de persistencia es una infraestructura tan sofisticada y compleja de administrar, un recurso interesante en términos de productividad para el desarrollador es "falsear" la capa de persistencia en algo más manejable (la base de datos local SQL Server 2005 Express Edition, una planilla Excel o archivos XML), pero que se pueda desplegar en producción con el mecanismo de persistencia definitivo. Todo esto sólo cambiando la configuración de la aplicación, sin cambios en la lógica de la clase de negocio

Cómo lograrlo? Acá va mi propuesta de separación en capas

  • Una Capa de Dominio con al menos dos "especies" de componentes
    • Las Entidades de Dominio: esto es, el Cliente, la Factura, el Producto, la Orden de Compra…
    • Los Procesos de Dominio: o en otras palabras, los servicios a nivel de negocio, granularidad gruesa, que se exponen hacia afuera. Ejemplos? "Comprar Producto", "Facturar Servicios", etc. Estas clases están más alineadas con los casos de uso
    • Personalmente suelo poner la verdadera "lógica de negocio" en los Procesos de Dominio más que en las Entidades de Dominio. Las entidades son más bien "lleva y trae" de información entre las distintas capas. Pueden llegar a tener algo de conducta pero no de lo más "relevante". Me refiero a que si a mi entidad Cliente no le voy a permitir que le setéen la propiedad Apellido en blanco o null, lo probable es que esa lógica la ponga en el método set() de la propiedad Apellido, en la entidad Cliente. Pero olvidate que yo a esa entidad le ponga un método llamado Cliente.Facturate(). Seguramente lo que haré será tener un componente de Proceso de Dominio llamado Facturador que tenga un método Facturador.Facturar(Cliente cte)
    • Entidades de Dominio pasivas o semi pasivas, implementan la idea de un patrón de diseño conocido como Data Transfer Object (DTO) u Objeto de Transferencia de Datos
  • Una Capa de Acceso a Datos que alberga una categoría muy especial de componentes
    • Los Objetos de Acceso a Datos (Data Access Objects o DAOs), son responsables de comunicarse con el mecanismo de persistencia elegido (la base de datos, el CICS o el que corresponda), y devolverle, a la Capa de Dominio, Entidades de Dominio correctamente formadas
    • Para ello, es posible que tenga que corregir el desajuste por impedancia entre las Entidades de Dominio, orientadas a objetos, y las bases de datos relacionales. Esa tarea queda bajo el control del DAO (directa o indirectamente: el DAO a su vez la puede delegar)
    • Antes de seguir quiero aportar una referencia para los que quieran conocer más de implementación de la Capa de Acceso a Datos. Click acá (a los DAO se los llama Componentes de Lógica de Acceso a Datos en esa referencia, vale la pena hacer la salvedad)
  • En resumen, la Capa de Dominio y la de Acceso a Datos "conversan" mediante el intercambio de Entidades de Dominio. Pero lo importante es que esta conversación siempre es iniciada desde la Capa de Dominio. La Capa de Acceso a Datos no sabe a quién está sirviendo

Bueno, y a decir verdad, la Capa de Dominio tampoco debería saber "exactamente" a quién le está hablando. Hace un rato comentaba el caso de las aplicaciones que en Producción acceden a back-ends complejos de montar o configurar en etapas de desarrollo

No sería fantástico, en esos casos, poder tener un DAO con la lógica de acceso a la plataforma que se va a usar en producción, y a la vez un DAO paralelo para poder simular esos accesos en una infraestructura liviana en el entorno de Desarrollo? Cómo independizar a la Capa de Dominio del DAO correspondiente según el entorno?

Para lograr esto hay una práctica de programación, que complementada con una técnica producen resultados sorprendentes en términos de productividad: Programación contra Interfaces (Interface-based Programming) e Inyección de Dependencias (Dependency Injection). Revisémoslos

  • Programación contra Interfaces es un estilo de programación donde un componente de dominio A no va a referirse nunca a un DAO B al invocarle un método, sino a la interfaz que este DAO expone (llamemosla IB). Ahora, claro, parece liviano todo pero… cómo recibe, el componente A, en tiempo de ejecución la referencia al B que está implementando la interfaz IB que es lo único que A conoce? Acá es donde entra en juego la
  • Inyección de Dependencias, mecanismo presente hoy en una amplia gama de así llamados "contenedores livianos" (lightweight containers) que mediante la asociación de etiquetas con clases concretas, por ejemplo en los archivos de configuración de la aplicación, permiten inyectarle un B específico a la clase A, sin que esta última vea más allá de la interfaz IB que B expone. Al pasar de un ambiente de Desarrollo al de Producción donde otra implementación de la interfaz será necesaria, se cambian los archivos de configuración pero no hay que recompilar nada
    A este tipo de contenedores, así como también a la Inyección de Dependencias, me referí en este post de Febrero pasado: click acá

Quiero cerrar volviendo a la pregunta original: lógica de persistencia dentro o fuera de los componentes de dominio? Mi respuesta: enfáticamente afuera. Si te gustó mi explicación pero querés darle una vuelta más te propongo que te leás este artículo publicado en MSDN Magazine hace unos años atrás

Esta entrada fue publicada en Software Architecture. Guarda el enlace permanente.

4 respuestas a Debate: Deben los Objetos de Negocio Incluir su Propia Lógica de Persistencia?

  1. Maximiliano dijo:

    Diego…Muy intersante tanto este como otros artículos que has escrito. Sería intersante (al menos para mi)  que en algún momento publiques algún ejemplo que tenga que ver con implementaciones, lo más "reales" posibles, sobre el uso de este tipo de soluciones, inclusive con el uso de patrones .

  2. fjgs dijo:

    Hola Diego, mi nombre es Francisco Gonzalez y alguna vez trabajamos juntos en ALTEC. Bueno junto con saludarte y felicitarte acerca de este blog me gustaría tu opinión en algunos puntos del post que me dan vuelta en la cabeza de algun tiempo :
     
    1. ‘Verticalidad’ y scope de los DTO’s estas estructuras entre la capa de presentación (como bean en una jsp) y la capa de negocios (interfaces bussines delagates). Esto trae consigo cierto nivel de acoplamiento por lo menos en terminos de las implementaciones de las clases de dominio y de presentacion (incluso al momento del deploy). He pensado que esto se resuelve aplicando algun patrón adapter contra una interfaz de la clases de dominio.
     
    2. Que opinas acerca de la opinion de rod jhonson acerca de que clases de ‘entidad’ sin comportamiento al igual que DTO o VO sacrifican los principios de POO (del mítico J2EE Development without EJB)
     
    Gracias y te felicito por el sitio, siempre lo recomiendo.

  3. Diego dijo:

    Maxi, antes de que entremos en un debate acerca de qué es "real" para vos y qué para mí, te propongo que me tires el escenario real y te planteo una arquitectura como la plantearía yo (va a ser a grandes rasgos, macho, si no termino lo que me pidió mi jefe, me va a pegar un patadón sabés dónde no?)

  4. Diego dijo:

    Pancho, claro que me acuerdo de vos. Y no sé si vos también le preparás todas las mañanas el desayuno a tu mujer pero eras un buen loco lindo como yo (como Fer "Chanchi" Morelli)
    Al grano dijo Belgrano: que tus DTO sean lo más básicos posible, en cuanto a la data que hay que traficar entre capas, a lo largo del caso de uso, y no deberías tener acoplamiento excepto -claro!- cuando algo de eso que se trafica debe cambiar. Vos dijiste "beans". Que sean "beans" de lo más awe: por tiras cifras, 90% propiedades, 10% métodos (y ojalá menos!). Algo tan básico y enfocado a lo que se dicen las capas cuando conversan, que realmente no importe lo que ambas capas se "guardan" de decirle a la otra parte
    Algo que a mí me funcó y lo aprendí de los ejemplos del Spring de Johnson es usar como DTO el formulario que se completaría en un caso de uso. Suponete que es una orden de compra o cualquier cosa q se llena en varias etapas
    Yo usaría un DTO basado en los campos del form, más cualquier otra info relevante que sirva de apoyo en ambas capas a los campos del form (por ejemplo, no vas a estar yendo a buscar cada vez la tasa del IVA si la vas a necesitar aplicar cada vez que el cliente mete un nuevo ítem a la compra -sin embargo esa tasa no era un campo del form original). Conclusión, el form más cositas asociadas. Si necesitás ciertos datos del cliente a mano, le asociaría al form una versión liviana del cliente, lo más limitada posible a los datos del cliente necesarios en el form
    Y ojo, a lo mejor no están todos en la clase cliente original de la capa de dominio, sino que puede haber parte en esa clase cliente, y parte en alguna clase que el cliente tiene asociada: bien, para el DTO metería todo lo relacionado con el cliente en una sola estructura. Cuando digo "todo lo relacionado con el cliente", "todo" alcanza a lo que en ese form de ese caso de uso pueda necesitar… Y NADA MÁS (la experiencia es de esas mamás que pegan fuerte, pero por el bien de uno  😦
     
    Respecto a lo que dice Johnson, le encuentro razón, como siempre. Es como mi Dios de la Arquitectura de Software. Si yo soy evangelista, Johnson es Sai Baba, sin duda. Ahora bien. La duda no es si rompe o no los principios de OOP. En eso estamos de acuerdo: los rompe. La duda es, "entonces deberíamos evitarlo"? No tengo claro que Johnson diga "sí, hay que evitarlo" pero sí tengo claro que yo, si me conviene usar esos objetos aunque rompan el paradigma, los uso. Lo que tengo claro es que necesito reusabilidad, diseño fácil de entender y mantener, sea o no OOP. OOP es un medio posible (no el único) de alcanzar eso. En cuanto OOP se convierte en un fin en sí mismo, y nos olvidamos de para qué era que lo necesitábamos, estamos fritos. De todos modos tendría q releer a Johnson para ver qué había dicho exactamente. No quiero decir q él haya dicho lo contrario o no (además que le van a dar más pelota a él que escribió 50 libros, q a mí que el único libro que firmé fue el libro de partes de la escuela)
     
     
     
    Como cierre quiero sugerir un artículo que expone estos mismos conceptos, aplicándolos al terreno particular de los servicios. Pero se puede generalizar la idea para escenarios de intercambio de datos entre capas. El artículo se llama "Datos de Afuera vs. Datos de Adentro"

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s