Entidades
Objetos por valor
Agregados y Raíces
Para un entendimiento mas profundo de estos elementos recomiendo los capítulos 5 y 6 del libro Domain-Driven Design de Eric Evans.
Entidades
De acuerdo con la descripción de Evans:
“Muchos objetos no son fundamentalmente definidos por sus atributos, si no por un elemento que les proporcione identidad”
En el diseño tradicional orientado a objetos, se podría iniciar modelando a partir de verbos y sustantivos. En el modelado DDD, preferentemente nos enfocamos en términos provenientes del lenguaje común que exhiben elementos de identidad.
Por ejemplo, vamos a considerar el concepto Persona. Si tenemos dos objetos Persona, con el mismo nombre, acaso son la misma persona? Juan Pérez de Ciudad de México y Juan Pérez de Guadalajara no van a esta de acuerdo. Que pasa cuando conocemos a alguien con nombre igual a algún famoso? ambos tienen diferentes identidad. Entonces que distingue a dos personas si su nombre no es un atributo distintivo?, acaso su dirección? o numero de CURP?
En estos ejemplo, una Persona es identificada por algo mas que sus atributos, como el nombre, su dirección, su teléfono, etc. Una Persona tiene una identidad única que se manifiesta en diferentes formas a través de diferentes sistemas. Cada sistema tiene sus propios atributos sobre los cuales tiene interés, pero la Persona es siempre la misma entidad (no la misma clase, eso es algo diferente).
Para determinar si un objeto es una entidad, podemos hacernos la siguiente pregunta: si dos instancias de un objeto con valores diferentes en sus atributos, tienen el mismo valor en su atributo de identidad, indica que son la misma entidad?
Si la respuesta es si, y nos interesa su identidad, entonces es una entidad. Se pueden modelar las entidades en forma de clases en C#, agregando una identidad sustituta (Surrogate key) (la cual puede ser un entero o un GUID). Adicionalmente la clase modelada debe de contener información acerca que que significa tener la misma identidad, esto por ejemplo realizar un “override” al método Equal para que evalúe únicamente nuestra identidad y no los demás atributos para determinar si dos objetos son iguales.
Objetos por valor
Evans menciona:
“Muchos objetos no cuentan con una identidad. Estos objetos generalmente describen una característica de algo”
Cuando no nos interesa acerca de la identidad de un objeto, es recomendable considerar el que este objeto lo diseñemos como un objeto por valor.
Por ejemplo, si tenemos un sistema el cual modelo cubetas de pintura, el objeto Color seria un buen candidato para ser un objeto por valor. A nosotros nos interesaría poder identificar un objeto CubetaPintura de otro, ya que al irlos utilizando la pintura de cada objeto eventualmente se acabara.
Pero cuando revisamos el Color de una CubetaPintura especifica, Color no tiene una identidad propia. Si tengo dos objetos Color exactamente con los mismos valores de pigmentación, simplemente consideramos que ambos son el mismo objeto.
Cuando diseñamos objetos por valor, es importante mantenerlos alejados de la dinámica de los ciclos de vida de las entidades, por lo tanto es recomendable asignarles la característica de inmutables y remover cualquier concepto de identidad. Adicionalmente, - si estamos programando den .NET - hay que realizar un “override” al método Equal para que realice la comparación sobre los valores de sus atributos.
Al configurar un objeto por valor como inmutable, muchas operaciones se simplifican, por ejemplo no tenemos un puñado de propiedades de escritura-lectura, todos los atributos los asignamos por medio de un constructor y reafirmamos la equidad de los atributos.
Los objetos por valor, como cualquier otro patrón, pueden ser sobre utilizados, si buscamos oportunidades para ello. Este tipo de objetos deben de representar conceptos en nuestro lenguaje normal, y un experto en dominio deberá de poder reconocerlos en nuestro modelo.
Agregados y Raíces
En la vida real, muchos conceptos tienen relación entre ellos. Si se tiene varias cuentas bancarias, y esas cuentas tienen un propietario. Cada cuenta esta manejada por un banco, y cada banco tiene un grupo de cuentas. Si deseamos representar todos estos conceptos como clases, ¿cual debería ser la relación entre ellas?.
¿Deberíamos representar cada relación identificable posible en nuestro modelo de objetos? ¿En donde marcamos nuestra linea sobre crear o no una referencia? Si contamos con una referencia entre dos objetos, ¿Como debemos manejar su persistencia? ¿Actualizaciones en cascada?. Supongamos que Empleado tiene una referencia a Gerente directamente. Si modificamos Empleado.Gerente.Nombre, y guardamos empleado, ¿Acaso el nombre del Gerente se modificara en la base de datos?
El modelado de objetos es una tarea compleja. Los invariantes necesitan ser reforzados no solo por la entidad, si no por todas las entidades referenciadas también.
Agregados nos permite definir una linea frontera alrededor de una o mas entidades. Un agregado refuerza invarinates para todas la entidades en cada operación soportada. Un agregado cuenta con una identidad raíz, la cual es el único miembro del agregado que al que se permite que un objeto fuera del agregado mantenga una referencia a el.
La reglas, de Evans, que debemos reforzar son:
- La entidad raíz cuenta con una identidad global y es la única responsable de verificar sus invariantes.
- Entidades dentro de los limites tienen una identidad local, única solo dentro del agregado
- Nada fuera del agregado puede mantener una referencia a algo dentro del agregado que no sea la entidad raíz. La entidad raíz si puede pasar referencias a las entidades internas a otros objetos, pero solo para su uso transitorio (por ejemplo usarlas solo dentro de un solo método)
- Solo los agregados raíz pueden obtenerse directamente de consultas a la base de datos. Objetos dentro de un agregado pueden mantener referencias a otros agregados raíz.
- La operación de eliminación debe de eliminar todo lo que este dentro de los limites del agregado al mismo tiempo
- Cuando se modifica un objeto dentro de los limites del agregado y posteriormente se guarda, todos los invariantes del agregado se deben de satisfacer.
¡Son demasiadas reglas!. Todas ellas se originan de la idea de crear limites o fronteras en nuestros agregados. Estas frontera simplifican nuestro modelo, y nos obliga a considerar cuidadosamente nuestras relaciones de objetos, bajo una reglas bien definidas.
El mantener asociaciones bidireccionales es muy difícil sin considerar nuestro modelo de persistencia, así que modelando nuestras relaciones alrededor de casos de uso del mundo real, podemos simplificar enormemente nuestro modelo.
No todas las relaciones necesitan ser representadas a través de asociaciones. En la relación Empleado-Gerente, podemos eliminar la propiedad Gerente de Empleado, y para obtener el Gerente al que se reporta un empleado, llamamos un método a nuestro repositorio de empleado (EmpleadoRepository). Debido a que Empleado es un agregado raíz, esta bien el que empleado mantenga una referencia a su Gerente.
Modelado y simplificación
Una cita del libro del Evans:
“La traducción nubla la comunicación y la vuelve anémica”
Para evitar la traducción, representaremos conceptos del mundo real en nuestro modelo conceptual, y nuestro modelo conceptual lo representaremos a través de entidades y objetos por valor (y servicios). Para simplificar nuestros modelos, es importante usar agregados raíz y reforzar invariantes en cada operación. En todos los casos, debemos de poder representar nuestro modelo conceptual en nuestro código, y este debe de tener sentido para nuestro experto del dominio, ya que ellos va a ver la representación de nuestro lenguaje cotidiano.
Cuando nuestro modelo conceptual esta correctamente expresado en nuestro código, encontraremos que no únicamente la refactorizacion técnica se facilita, si no también las mejoras a nuestro modelo. Entidades y objetos por valor son solo una parte del mundo DDD, pero son un concepto importante sobre el cual otras ideas están construidas.
Referencias
No hay comentarios.:
Publicar un comentario