viernes, 8 de febrero de 2013

Arquitecturas para Domain-Driven Design - Parte III

Los dos conceptos que voy a explicar hoy son cruciales para el diseño de una arquitectura decente que cumpla el dominio.

En DDD los dos objetos de negocio principales son Entidades y Objetos de Valor.

Entidades 

La característica definitoria de una Entidad es que tiene una identidad, es única dentro del sistema, y ​​ninguna otra entidad, no importa cuán similar sea, es la misma entidad a menos que tenga la misma identidad.

La identidad puede ser representada de muchas maneras en una entidad. Podría ser un identificador numérico, podría ser un GUID o podría ser una clave natural formada por uno o más valores que, según el dominio, van a ser únicos en todo el ciclo de vida del sistema persistente.

Objetos de valor (VO) 

La característica definitoria de un objetos de valor es que no tiene identidad. La intención de un objeto de valor es representar algo por sus atributos únicos. Dos VO's pueden tener atributos idénticos, en cuyo caso son el mismo VO. Un VO debe ser inmutable y sus características no deben cambiar durante su ciclo de vida.

Aquí están las definiciones, ahora veamos como se traduce esto en la vida real del desarrollo de software.

La vida real 

Pongamos un supuesto, el sistema de información para una fabrica de coches.

Entidad 

Cada coche que se fabrica es único y tiene que seguir siendo único y él mismo siempre. No importa la similitud que tenga con otros coches o que sus características cambien. Su identidad viene definida en este caso por el número de bastidor. Un coche puede ser igual a otro en todo: marca, color, llantas, peso, número de puertas, etc. Pero cada uno es un coche diferente.

Si a un coche se le cambia el color o se le pone un motor más potente, sus características cambian, pero sigue siendo el mismo coche. Esto es una Entidad.

Objeto de valor 

En el sistema de fabricación de coches tenemos una serie de colores con los que pintamos los coches, Estos colores están representados en el sistema por un nombre y sus valores RGB. ("Celeste" [0, 170, 228])

En nuestro sistema nunca tendremos dos "Celeste" que representen dos colores distintos. Si dos VO's son distintos tienen que tener características distintas. Si dos colores tienen el mismo RGB son el mismo color.

Los VO's son inmutables debido a que no puedo cambiar el código RGB de "Celeste" y que los nuevos números sigan representando al color de nombre "Celeste". Sería simplemente otro color. Esto es un VO.

Ahora bien. Es nuestra responsabilidad como arquitectos de software y desarrolladores el modelar las reglas de dominio que deben cumplir las entidades y los VO's.

Podemos poner como analogía las reglas de la física del macroverso (remarco macroverso por si alguno se me pone vacilón con los quarks y la antimateria). Según estas leyes, una entidad no puede estar en dos lugares a la vez al mismo tiempo, ni dos entidades pueden ocupar el mismo espacio al mismo tiempo.

Para ilustrar esto de modelar las reglas del universo que estamos creando, tengo que explicar la teoría de la creación por sustracción.

Como las fotos de tu boda

La mejor analogía que se me ocurre es poner el ejemplo de las diapositivas. Las máquinas de diapositivas crean imágenes en una pantalla. Pero no crean esa imagen añadiendo colores a la pantalla, la crean sustrayendo colores a la luz blanca.

En el supuesto de que tengamos ya modeladas la clase "Coche" y la clase "Color", nos ponemos delante nuestro editor de código favorito y ¿Qué podemos hacer con estas entidades? Pues como pequeños demiurgos del sistema que somos, lo podemos hacer todo. Podemos hacer que un coche esté en dos almacenes diferentes a la vez. Podemos hacer que el mismo coche este pintado con dos colores a la vez en la misma franja temporal. Podemos hacer que dos coches diferentes estén en la misma plaza de aparcamiento en el mismo instante. Si esto pasase en la física del macroverso de vez en cuando, de una forma cuasi aleatoria, diríamos que el sistema de nuestro universo tiene un Bug. ;)

Cuando estamos sentados delante de un editor de código en blanco y vamos a desarrollar algo, no creamos un sistema de la nada, creamos un sistema sustrayendo de un todo, tenemos que poner reglas y limitaciones a las infinitas posibilidades que existen delante de una pantalla vacía. Estas reglas y limitaciones son parte del dominio de nuestro sistema.

Vamos con la chicha 

El lector espabilado ya se habrá dado cuenta que esto está muy relacionado con "el pifostio de las referencias y los punteros". Al nivel de abstracción más bajo de nuestro sistema, tenemos objetos que apuntan a otros objetos, y con ellos podemos hacer lo que nos de la gana. Si tenemos un objeto "PlazadeAparcamiento" que apunta al coche que está ahora mismo ocupándola, podemos asignar un coche a esa plaza. Si asignamos otro coche a la misma plaza sin modelar ninguna regla, sería como si un coche "desapareciera" de la plaza y otro apareciera en su lugar de repente.

Hay que modelar un sistema que eleve el nivel de abstracción. A un nivel más alto no deberíamos tener que saber nada sobre punteros, instancias ni referencias a objetos. Deben ser los "cimientos" de la aplicación los que se encarguen de todo esto para que nosotros sólo nos tengamos que preocupar de "aparcar un coche en una plaza de aparcamiento". Si ésta se encuentra libre pues ya está hecho. Pero si está ocupada, dado que dos entidades no pueden ocupar el mismo espacio físico, el "aparca-coches" nos dirá que no puedo hacerlo, que la plaza está ocupada, o se encargará de mover el coche y ponerlo en otro lado para dejar la plaza libre.

Generación espontánea y teleportación 

Los coches no aparecen de la nada (PUF!!!). No se puede hacer un <new Coche()> por ahí, en cualquier lugar del código donde se necesite un coche nuevo. Los coches se fabrican, por lo que debemos tener una fábrica de coches. Esta fábrica podrá reconfigurarse para que fabrique coches con las características que le digamos. Si le cargamos un color a la fábrica, ésta creará coches pintados con ese color.

Otro ejemplo. Los clientes no se materializan en un negocio como teleportados por la Enterprise. Los clientes se dan de alta. Hay que proveer un modelo que se encargue de "fabricar" o "dar de alta" las entidades de nuestro sistema. Por supuesto, al nivel de abstracción más bajo, existen instrucciones para crear nuevas instancias de clases, pero estas instrucciones no salen de allí. No deberíamos ver un solo <new Entidad( )> fuera de su contexto (una fábrica, un proceso de alta, etc).

El gemelo malvado

Tenemos que limitar nuestro universo para que no existan dopplegängers. Que existan gemelos malvados en un universo nunca da buenos resultados. En un sistema se podría dar la situación en la que tenemos un coche asociado a un conductor, lo que se traduce en que una clase "Conductor" tenga un puntero a una instancia de un coche en memoria. Y ese coche está en un taller, por lo que la clase "Taller" también debe tener un puntero a esa misma instancia del coche en memoria. Si permitiésemos que lo que hay en el taller sea un dopplegänger (otra instancia de ese mismo coche en otra posición de memoria), cuando el taller pintase el coche de otro color, el coche del "Conductor" seguiría manteniendo su color antiguo, puesto que el color habría sido cambiado al gemelo malvado. Esto produciría una paradoja espacio-temporal que haría implosionar el pequeño universo que estamos modelando entre gritos de clientes, gestores con ceño fruncido, una horda de programadores corriendo por los pasillos como pollos descabezados y esa GPU tan molona que te acabas de comprar (a mi no me engañas, se que eres una graphic whore) echando humo.

¿Son iguales, son el mismo o es el gemelo malvado? 

En ciertas ocasiones es inevitable que, con la comunicación entre dos sistemas con una integración muy débil, se nos de el caso de encontrarnos con algún dopplegänger. Para estos caso (y para otros menesteres) debemos proporcionar mecanismos que nos permitan comparar entidades y poderlas identificar.

Hay 3 maneras de comparar entidades.

Se puede necesitar saber si dos entidades son iguales (que no la misma). Esto significa comparar si las características de un coche son las mismas que la de otro coche. Comparar el color, la cilindrada, la marca, el modelo, etc. Y si todo coincide es que esos dos coches, a pesar de ser distintos coches, son iguales.

También debemos proporcionar un mecanismo pasa descubrir cuándo dos coches son el mismo. En el caso del ejemplo con coches, nos bastaría con comparar los números de bastidor.

Y nos queda la última. La de determinar si dos referencias apuntan a la misma entidad. (dos punteros referencian la misma posición en memoria). Con ésta podemos desenmascarar a los gemelos malvados.

La combinación de estos tres mecanismos nos da la base para poder trabajar con las entidades y modelar sus leyes universales.

Cada entidad debe implementar internamente estos mecanismos de comparación, por lo que cuando trabajemos con las entidades ya no tenemos que saber nada de punteros, referencias o que características identifican a una entidad.

No hay comentarios:

Publicar un comentario