jueves, 5 de noviembre de 2009

DDD - Parte 3: Refactorizando Hacia una Visión más Profunda #1


Domain-Driven Design (DDD) dice que el verdadero desafío en el desarrollo de una aplicación empresarial es encontrar un modelo incisivo, que capture hasta los conceptos más sutiles del dominio y los vuelque en un diseño práctico. Refactorizar es una práctica que conduce a ese modelo. Refactorizar es rediseñar de forma tal que no se altere la funcionalidad del sistema. Para esto puede ayudar un buen conjunto de pruebas automatizadas que nos permitan experimentar con el código y el modelo con relativa tranquilidad.

Evans habla de tres niveles de refactorización:
  • Nivel 1: cambios mecánicos que hacen más fácil de leer el código
  • Nivel 2: cambios que permiten aplicar patrones de diseño
  • Nivel 3: cambios que permiten obtener un modelo más profundo
Un modelo profundo (deep model) proporciona una lúcida expresión de las incumbencias principales de un dominio y de su conocimiento más importante, a la vez que aparta los aspectos superficiales.

Progreso

El retorno de inversión de la refactorización no es lineal. Las revelaciones más importantes surgen abruptamente y así un modelo cada vez más profundo va emergiendo gradualmente tras jornadas discontinuas de refactorización. Cada refinamiento del código y del modelo aclara el panorama del equipo de trabajo. Este tipo de progreso (breakthrough) no es una técnica, es un evento.


A menudo, cuando la perspectiva de un progreso se presenta resulta aterrador. El calendario siempre es apretado y decisivo. Pero hay que recordar que estos progresos pueden implicar importantes adelantos en el desarrollo del proyecto. Evans nos recomienda que no nos paralicemos tratando de lograr un progreso. Usualmente los progresos se presentan solos después de varias modestas refactorizaciones. Concentrémonos en masticar el conocimiento y en cultivar un lenguaje ubicuo robusto, como se habló en el post dedicado a la primera parte del libro.

Haciendo Explicitos los Conceptos Implícitos

Muchas transformaciones del modelo de dominio ocurren cuando los desarrolladores reconocen un concepto que acaba de insinuarse en una en una discusión o que estaba implícito en el diseño y encuentran la forma de representarlo de forma explícita con uno o más objetos o relaciones. Por eso, los desarrolladores deben estar atentos a este tipo de insinuaciones y algunas veces deben salir a buscarlas de forma proactiva. Muchos de estos descubrimientos surgen de:
  • Prestar atención al lenguaje del equipo
  • Eliminar torpezas en el diseño y aparentes contradicciones en las declaraciones de los expertos
  • Explorar la literatura del dominio
  • Hacer mucha y mucha experimentación
Cuando el usuario o el experto de dominio usan palabras que no están en el lenguaje ubicuo o en el diseño, estamos frente a un signo de advertencia bien grande. Es una advertencia doblemente fuerte cuando no sólo los usuarios sino también los desarrolladores usan este vocabulario ajeno al modelo. Es cuando tenemos que sentarnos a refactorizar.


Los conceptos que necesitamos no siempre están flotando en la superficie, ni emergiendo en las conversaciones o los documentos. Tendremos que cavar, incluso excavar, incluso inventar. El lugar donde cavar es en la parte más débil de nuestro diseño, el lugar donde los procedimientos son cosas complicadas, retorcidas y difíciles de explicar, el lugar donde cada nuevo requerimiento parece agregar más complejidad.

A veces, para descubrir estos conceptos ocultos contamos con un experto de dominio colaborador, a veces no. A veces contamos con bibliografía donde los conceptos están bien definidos y detallados, a veces no. Muchos dominios no tienen modelos tan refinados como los de contabilidad o finanzas, pero en otros casos podemos dar con pensadores en el campo, quienes quizás se tomaron el trabajo de organizar y abstraer las prácticas comunes del negocio, o artículos especializados, white papers, blogs, etcétera.

En DDD la experimentación es una de las prácticas más importantes. La experimentación es la forma de saber si algo funciona o no. En el camino a un modelo profundo hay mucha prueba y error, mucha obstinación y muchas lecciones aprendidas.

¿Cómo modelar los conceptos menos obvios?
El paradigma de la orientación a objetos nos lleva a buscar y a inventar cierto tipo de conceptos. Son los famosos sustantivos y verbos que nos enseñan cuando somos neófitos en la materia, intentando en vano simplificar el proceso de relevamiento de un dominio. Pero hay otras categorías de conceptos también muy importantes:


1) Restricciones Explícitas

Algunas veces las restricciones (constraints) encuentran un hogar dentro de un objeto o un método cuya responsabilidad no está relacionada con la restricción de forma directa, pero cuando la lógica se torna más complicada, es conveniente aislar la regla en una porción de codigo aparte (sea método o clase), donde la restricción tenga espacio para crecer naturalmente. Si la regla es más complicada debe tener su clase propia y hasta su paquete.


Hay tres advertencias que podemos recibir de que una regla o restricción está contaminando la clase en donde se ubicó y debe mudarse a un lugar más espacioso:
  • Evaluar la restricción requiere datos que no tienen que ver con la definición del objeto
  • Reglas relacionadas aparecen en múltiples objetos, forzando duplicamiento o herencia de objetos que no deberían pertenecer a la misma familia
  • Un montón de diseño, requerimientos y conversaciones giran alrededor de la restricción, pero en la implementación la regla está oculta en un código procedural
DDD propone un patrón para encapsular reglas de negocio un poco más complicadas. Es el patrón Specification que ya mencioné en algún post pasado. En el libro hay una extensa y detallada explicación que no tiene desperdicio. También en el libro de Fowler, Patterns of Enterprise Application Architecture, que ya estuve comentando en los post de Patrones Enterprise Parte 1 y Parte 2, hay una explicación exhaustiva. La versión de Evans es un poco más sofisticada. Para definir los Specifications se inspira en los predicados del paradigma de programación lógica. Además, si se quedaron con ganas de seguir leyendo sobre el tema, hay un paper que Eric Evans y Martin Fowler escribieron juntos y que se puede descargar desde este link. (Algún día, si tengo tiempo, me gustaría escribir un post exclusivo sobre Specifications, pero por ahora voy a saltear el tema.)

2) Procesos como Objetos de Dominio
Reglas de negocio y procesos son dos grandes categorías de conceptos de modelo que no vienen a la mente cuando estamos programando en un lenguaje orientado a objetos. Los procesos pueden expresarse con el patrón Service (ver DDD - Parte 2: Los Componentes Fundamentales de un Modelo Guiado por el Dominio #1, donde hay un breve resumen sobre los Services). La clave para distinguir un proceso que debe ser expuesto de forma explícita en el modelo de otro que debe permanecer escondido entre Entidades y ValueObjects, es simple: ¿El proceso es algo de lo que el experto de dominio habla mucho, o sólo se trata de un aspecto técnico, de un proceso algorítmico del código?

Si bien los patrones Specification (para expresar reglas de negocio) y Service (para expresar procesos) son elegantes y poderosos, hay casos en los que no resultan suficientes y tenemos que adentrarnos en el mundo de los motores de reglas de negocios y de workflows.

Diseño Flexible

El propósito final del software es servir a los usuarios. Pero primero, ese mismo software debe servir a otros desarrolladores. Cuando un sistema con un comportamiento complejo carece de un buen diseño, se vuelve difícil de refactorizar y de manipular. El diseño flexible (supple design) es el complemento de un modelo profundo.

Simple no significa fácil. Para crear elementos que puedan ensamblarse en elaborados sistemas y mantenerse simples se necesita de un estilo de diseño moderadamente riguroso. Para abrazar el cambio, el diseño tiene que ser fácil de entender y, por ende, fácil de modificar. Las primeras versiones suelen ser rígidas, toscas; sólo a través de sucesivas refactorizaciones y sucesivos progresos, podremos alcanzar un diseño flexible.


No hay ciencia ni fórmula para lograr esto; sin embargo, Evans propone un conjunto de patrones que contribuyen a alcanzar el (o por lo menos a mantenerse en el camino hacia el) diseño flexible:
  • Intention-Revealing Interfaces
  • Side-Effect-Free Functions
  • Assertions
  • Conceptual Contours
  • Standalone Classes
  • Closure of Operations


En el próximo post nos dedicaremos a ver un resumen de cada uno de estos patrones y hablaremos un poco sobre Diseños Declarativos y lo que es la máxima expresión de DDD: Domain-Specific Language.