Artículo de invitado de Carlos Morera, software craftsman actualmente trabajando como desarrollador Android en Viagogo, Londres, y padre de Finger Gesture Launcher

En el anterior post, Introducción a Domain Driven Design (DDD) parte 1 discutimos uno de los conceptos más importantes en domain-driven design, el Ubiquitous language. También empezamos a describir algunos de los elementos básicos para modelar el dominio del software como son las Entities y los Value Objects. En este post vamos a definir qué son y qué tipos de services existen, y terminaremos describiendo como aislar la capa de dominio del resto del sistema.

Services

Los servicios representan operaciones, acciones o actividades que no pertenecen conceptualmente a ningún objeto de dominio concreto. Los servicios no tienen ni estado propio ni un significado más allá que la acción que los definen.

Al contrario que las entidades y los value objects, los servicios son definidos en términos de lo que pueden hacer por un cliente, y por tanto tienden a ser nombrados como verbos. Los verbos utilizados para nombrar a los servicios deben pertenecer al ubiquitous language, o ser introducidos en el en caso de que aún no lo sean. A la hora de implementarlos tanto sus parámetros como resultados deben ser objetos pertenecientes al dominio.

Un servicio debe de cumplir tres características principales:

  • La operación que lo define está relacionada con un concepto de dominio, pero no es natural modelarlo como una entidad o un value object.
  • Su interfaz se especifica usando otros elementos del modelo de dominio.
  • La operación no tiene estado, por lo que cualquier cliente podría usar cualquier instancia del servicio sin tener en cuenta las operaciones que se han realizado con anterioridad en esa instancia.

Podemos dividir los servicios en tres tipos diferentes según su relación con el núcleo del dominio.

Domain services

Son responsables del comportamiento más específico del dominio, es decir, realizan acciones que no dependen de la aplicación concreta que estemos desarrollando, sino que pertenecen a la parte más interna del dominio y que podrían tener sentido en otras aplicaciones pertenecientes al mismo dominio. Por ejemplo, crear un usuario, actualizar los detalles de un cliente, etc…

Application services

Son responsables del flujo principal de la aplicación, es decir, son los casos de uso de nuestra aplicación. Son la parte visible al exterior del dominio de nuestro sistema, por lo que son el punto de entrada-salida para interactuar con la funcionalidad interna del dominio. Su función es coordinar entidades, value objects, domain services e infrastructure services para llevar a cabo una acción. Por ejemplo, realizar un pago, añadir un producto al carrito de la compra, realizar una transferencia a otra cuenta, etc… (Si estás familiarizado con “Clean Architecture” los application services serían equivalentes a los “Interactors”)

Infrastructure services

Declaran comportamiento que no pertenece realmente al dominio de la aplicación pero que debemos ser capaces de realizar como parte de este. Por ejemplo, enviar un email de confirmación tras realizar un pago, loguear transacciones, etc…

Diferentes aplicaciones tienen diferentes niveles de complejidad en sus dominios y eso puede hacer que diferenciar entre domain y application services no sea siempre trivial. Como idea general podríamos considerar que, si después de recibir una orden el sistema este necesita realizar varios pasos, la coordinación de dichos pasos se realizaría en un application service. Si por otro lado, recibimos una orden simple que está relacionada con un concepto de dominio este comportamiento probablemente deba de ser modelado como un domain service.

Para aclarar la diferencia entre los diferentes servicios y sus responsabilidades pongamos el ejemplo de un application service que dados unos productos en el carrito de la compra realiza el pago. Vamos a nombrar nuestro application service como MakePaymentService.

MakePaymentService tendrá que validar el usuario, aplicar descuentos, verificar que tenemos los productos disponibles stock, realizar una llamada a un servicio de pago externo, notificar al servicio de paquetería para el envío, enviar un email de confirmación al usuario, etc…

La responsabilidad de MakePaymentService es la coordinación del flujo para realizar un pago. Por otro lado, para realizar la acción de validar el usuario utilizariamos un ValidateUserService que sería un domain service y que tendría la responsabilidad de validar que el usuario es válido, ya que a priori es una orden simple que está relacionada con un concepto de dominio. En cambio, para la acción de enviar un email de confirmación al usuario, utilizariamos SendEmailService que en este caso sería un infrastructure service, ya que es una acción que nuestro dominio debe ser capaz de realizar pero que no pertenece al mismo. Por tanto, sería especificado en nuestro dominio como una interface (en el caso de Java), que como veremos a continuación sería implementada dentro de otra capa de nuestro sistema (infrastructure).

Layered architecture

Un sistema software está compuesto por muchas partes, de las cuales, la parte que resuelve problemas para el dominio es una porción pequeña, aunque su importancia es desproporcionada a su tamaño.

Para ser capaces de trabajar con el dominio sin perdernos en otros detalles presentes en el software necesitamos desacoplar los objetos de dominio de otras funciones del sistema. Tendremos que aislar nuestro dominio del resto del sistema para asi evitar confundir conceptos pertenecientes al dominio con conceptos que solo están relacionado con la tecnología utilizada.

Podemos utilizar cualquiera de las muchas arquitecturas que existen para aislar las distintas partes del sistema, pero la opción que elijamos debería dividir nuestro sistema en al menos cuatro capas: presentación, aplicación, dominio e infraestructura.

Presentation

Capa responsable de mostrar la información al usuario e interpretar los eventos de entrada del usuario. Cabe destacar que el usuario puede ser un ser humano u otro sistema que se comunica con el nuestro.

Application

Capa que declara las funcionalidades que el software tiene que llevar a cabo y orquesta los objetos de dominio para resolver los distintos problemas. Esta capa no contiene reglas de negocio o conocimiento, solamente coordina y delega el trabajo a la colaboración de los objetos de dominio que se encuentran en la la siguiente capa.

Domain

Capa donde se encuentran los conceptos del dominio y las reglas de negocio. Es la capa más importante del sistema y es la que realmente aporta valor y resuelve los problemas para los que un determinado software es creado.

Infrastructure

Capa que provee las implementaciones que apoyan a las capas definidas anteriormente. Aquí se encapsulan la mayoría de las decisiones tecnológicas adoptadas para un sistema, por ejemplo: el envío del email de confirmación tras un pago, persistencia para el dominio, comunicación con otros sistemas, etc…

Conclusión

El proceso obtención de conocimiento y de construcción de software es complejo y tedioso. Es un proceso iterativo, en el cual iremos descubriendo nuevos conceptos y abstracciones que tendremos que plasmar en el código y en el lenguaje utilizado para comunicarnos.

En muchas ocasiones descubriremos como un conocimiento más profundo del dominio y su inclusión en la base código harán que partes del sistema que a priori parecían complejas o en las cuales teníamos dificultades para modelar o diseñar se vuelvan triviales. Dando paso a la creación de software expresivo, semántico, fácil de entender, manejar y modificar.

Referencias

Eric Evans – Domain-driven design