Facade & Delegate en nuestras arquitecturas de software

Comencemos por el patrón Facade. Imagina que tienes un problema el cual para solucionarlo requiere de diferentes conocimientos en distintos temas. Veamos el siguiente ejemplo: Tienes dificultades con la instalación eléctrica de tu hogar y hay interruptores que no funcionan correctamente junto con luces que parpadean, es tu responsabilidad darle solución a esas dificultades, pero requieres conocimientos especializados en electricidad, como identificar circuitos, resolver problemas de cableado y reemplazar componentes defectuosos. Ante esta situación, tenemos dos maneras de solucionarlo: en la primera, te conviertes en un especialista y aprendes todo lo que necesitas para solucionar la necesidad actual, y en la segunda, contratas a un electricista el cual ya tiene los conocimientos y herramientas para hacer las debidas reparaciones.
En este segundo escenario, el electricista sería nuestro facade. Por lo tanto, ahora tu labor es únicamente comunicarle al experto qué dificultades estás presentando y él te brindará a través de un simple llamado la solución para reparar tu hogar.

El patrón de facade aplicado en este caso se convierte en el electricista el cual abstrae la complejidad de la solución del problema y proporciona una interfaz simple para ser invocado. Podemos ver a facade como un concepto en el que se reubica la complejidad de la solución de un problema, brindando una abstracción simplificada para su uso.

Facade es, entonces, un patrón de diseño que busca abstraer la solución de un problema, ofreciendo una interfaz simplificada permitiendo a su vez la reutilización. Muchos ingenieros piensan e incluso afirman que facade es un patrón que reduce el acoplamiento, cuando lo correcto que se debería decir es que acomoda el acoplamiento, debido a que el acoplamiento para la solución del problema sigue existiendo, pero ahora se encuentra en la facade.

Veamos el siguiente ejemplo:

Antes y después de usar facade

Observando la imagen anterior, podemos apreciar en la imagen de la izquierda cómo cada una de las clases de ventas presenta un alto acoplamiento al tener que depender de diferentes clases y sus respectivas respuestas para completar la lógica de negocio que precisase para realizar la venta. En la imagen de la derecha, vemos el poder de la facade en acción, esta facade ahora tiene el acoplamiento hacia las clases que necesitase para completar la venta y ofrece a las clases de venta una abstracción a través de una interfaz simplificada para hacer uso de ella. En este escenario, facade también habilita la reutilización; sin embargo, a pesar de conseguirlo este no es su propósito principal.

Debe quedar claro que el acoplamiento que existe para realizar la venta NO desaparece del proyecto, dicho acoplamiento sigue existiendo pero ahora en la facade.

Facade en nuestra estructura del proyecto

Facade está para ofrecer una interfaz simplificada a una lógica de negocio y NO para ser parte de una estructura de paquetes dentro de la arquitectura del proyecto a nivel de vista de paquetes. 

Uso incorrecto de facade

La imagen anterior muestra un clásico uso incorrecto de facade donde la presentan como una capa dentro de la estructura del proyecto, cuando ya hemos explicado realmente cual es su propósito. Veamos una aproximación más correcta de su uso.

Una vista de nuestro proyecto usando correctamente facade

Las Facades hacen parte de la lógica de negocio y por lo tanto, deben estar dentro de la carpeta service podrías llegar a crear una carpeta para dejar todas tus facades dependiendo de la necesidad que tenga tu proyecto.

“Toda la arquitectura habla de diseño, pero no todo el diseño habla de arquitectura”

Cuando se habla de Facade entonces estamos hablando de diseño y no de arquitectura de software debemos recordar que La arquitectura de software habla de la forma que se le da al sistema, esa forma es la división del sistema en componentes, la organización (estructuctura) de esos componentes y la forma en que esos componentes se comunican entre sí.

SOLID y Facade

Algunas personas piensan que facade, al ‘disminuir’ el acoplamiento (cuestión que no es así como ya se explicó anteriormente), ayuda a cumplir los principios SOLID. Esto es falso, el alto o bajo acoplamiento no es un principio SOLID. Sin embargo, revisemos cómo se relaciona este patrón de diseño con SOLID:

  • Dependency inversion principle: Puedes trabajar con interfaces y hacer inyección de dependencias en la facade sin necesidad de hacer inversión de dependencia. DESCARTADO.
  • Interface segregation principle: Trabajar con interfaces específicas no tiene nada que ver con facade. DESCARTADO
  • Liskov substitution principle: Poder reemplazar objetos por sus subtipos sin alterar el correcto funcionamiento tampoco tiene que ver con facade. DESCARTADO
  • Open/closed principle: Habilitar el programa para su extensión y cerrado a modificación tampoco está relacionado facade (Este principio SOLID se puede encontrar más claramente en patrones de diseño de comportamiento). DESCARTADO
  • Single responsibility principle: Al mover la responsabilidad de una lógica a una facade, se permite que la clase sea más cohesiva, es decir, facade ayuda al principio de responsabilidad única. APROBADO.

¿Facade como patrón arquitectónico?

Al patrón de diseño facade lo podemos encontrar también como un patrón arquitectónico cuando se trabaja con soluciones de microservicios con el nombre de Pattern Gateway Aggregation, de este tema podremos ahondar en entradas posteriores si es de interés general.

Conclusiones:

  • Facade no baja el acoplamiento solo lo mueve a otro lugar (mejor).
  • Facade está relacionado en cierta forma con SOLID en el tema de Cohesión pero no con el Acoplamiento.
  • Es posible lograr reutilización con facade pero no es su propósito principal, puedes crear tu facade para ser utilizada por una sola clase y está correcto.
  • Facade no hace parte de la estructura de paquetes en n-layer.
  • Facade al mejorar la cohesión y reestructurar el acoplamiento facilita las pruebas unitarias y las pruebas de integración.

DELEGATE

El patrón delegate es un patrón de diseño en el cual un objeto, en lugar de realizar directamente una tarea en particular, delega la tarea a otro objeto. En otras palabras, un objeto delegado se encarga de realizar la tarea. 

Este concepto de “patrón de diseño” fue importante en los primeros momentos de la programación cuando se solía tener archivos extensos con toda la lógica en ellos, lo que resultaba en una baja mantenibilidad y flexibilidad. Si repasamos este “patrón de diseño” podemos apreciar el «valor» que aporta, listemololos:

  • Separación de responsabilidades: El patrón Delegate permite separar las responsabilidades entre objetos, promoviendo un diseño modular y fácil de mantener. El objeto delegante (delegating object) puede transferir una tarea específica a otro objeto delegado (delegate object), lo que ayuda a mantener el código más organizado y reduce la complejidad.
  • Flexibilidad y extensibilidad: El uso de delegates permite cambiar el comportamiento de un objeto delegante en tiempo de ejecución. Puedes cambiar fácilmente el objeto delegado al que se le delega una tarea sin modificar el código del objeto delegante.
  • Acoplamiento débil: El patrón Delegate ayuda a reducir el acoplamiento entre objetos. El objeto delegante no necesita conocer los detalles internos del objeto delegado, solo necesita conocer la interfaz común que deben implementar los objetos delegados.

Excelente todo lo anterior, ¿verdad?, sin embargo, en la actualidad con los lenguajes orientados a objetos, el uso de interfaces y la composición brindan el mismo valor que se menciono anteriormente. Esto nos lleva a la conclusión de que el patrón delegate no aporta ningún valor adicional a nuestro proyecto. De hecho esto se puede comprobar en que la gran mayoría de libros y documentos que hablan acerca de lo que son patrones, ya no mencionan delegate como un patrón.

Delegate en nuestra estructura del proyecto


Ahora, es probable que alguna vez hayas implementado Delegate como una estructura/capa de tu arquitectura del proyecto, como se muestra en la siguiente imagen:

Uso incorrecto de delegate como capa 

Lo anterior, es un error generalizado puesto que no aporta ningún valor a tu proyecto agregar esta capa de delegate que solo realice llamadas a la capa de servicio. Sin embargo, existen defensores que argumentan que su función es “delegar”. 

Veamos ahora una aproximación de nuestro proyecto sin esa capa de delegate:

Una vista de nuestro proyecto sin la capa delegate.

Con la anterior aproximación logramos los mismos beneficios que tendríamos al tener una capa de delegate (Separación de responsabilidades, flexibilidad y extensibilidad, acoplamiento débil). Sin embargo, evitamos la complejidad accidental que conlleva agregar algo innecesario. 

La composición por medio de interfaces es la clave.

¿Delegate como patrón arquitectónico?

Al patrón de diseño delegate lo podemos encontrar también como un patrón arquitectónico cuando se trabaja con soluciones de microservicios con el nombre de Pattern Gateway Routing dado que nuestro gateway recibe una petición y la delega a un componente que se encarga de hacer el trabajo (delegado). Delegate como patrón arquitectónico aporta mucho valor al proporcionar un punto único para la comunicación de diferentes componentes del sistema, lo que traduce en reducir el acoplamiento del sistema. 


Conclusiones

  • Delegate no debería hacer parte de nuestra estructura de carpetas del proyecto.
  • La composición y uso de interfaces/polimorfismo suplen los beneficios que puede aportar el uso de Delegate.
  • El concepto de Delegate lo podemos encontrar también como patrón arquitectónico.
  • Delegate no es un agrupador de llamados a servicios para ser usado por un controlador.
  • La excusa de “su responsabilidad es delegar” es bastante carente de argumento dentro de los ingenieros de software que aún la defienden a capa y espada.

Escrito por SAS (Software Architecture Sabana): Sebastian Pardo, Sergio Gonzalez, Richard Lion, Daniel Saavedra.

3 comentarios en «Facade & Delegate en nuestras arquitecturas de software»

  1. Muy buen artículo, el facade es claro, me gusto el ejemplo y la implementacion.
    Con respecto al Delegate si me gustaría dejar unos puntos de vista, me gusta mucho trabajar con Delegate (Lo he trabajado en empresas y en proyectos personales) y si le veo bondades que se pueden rescatar, obviamente en el ejemplo que pones, no se logran ver, pero en proyectos profesionales y grandes puedes encontrar los siguientes beneficios:
    – Orquestacion de los service: En un escenario donde primero debes consumir un Microservicio y luego validar la respuesta y luego guardar la información en base de datos, en un service puede quedar muy largo el código y si se piensa en la solución de un service intermedio, estarías creando un delegate pero con otro nombre… y es ahí donde viene mi segundo punto.
    – Orden y legibilidad: Si estoy en un controller y voy para el service, no sabré exactamente si ya estoy en la capa de persistencia o capa de validación de negocio y más cuando un service llama a otro service y a otro service, se pierde uno en el código. Con un Delegate, el controller le pasa la petición al delegate (acá ya se que no estoy en la persistencia y puedo hacer las validaciones y cambios que desee) y el delegate le pasa la petición al service… y en el service ya se que estoy en la capa de persistencia con un repository o que voy a consumir un tercero.
    ¿Y las reglas de negocio?: En lo personal me gusta dejar las reglas de negocio en el delegate y dejar los métodos de los service lo más reutilizable posible (un método de un delegate puede o no ser reutilizable en diferentes controller, en mi experiencia casi nunca lo son). Por ejemplo en el delegate llamo a un service encargado de consumir un microservicio, obtengo la respuesta y hago los ifs, for y cambios en la capa delegate para posteriormente consumir otro service para bajarlo a base de datos, con eso tengo un service que consume servicios de terceros (sin if ni fors ni nada y son reutilizables) al igual que mi service de persistencia.

    La oportunidad de utilizar un delegate en los proyectos me ha dado escalabilidad en mis intercaces sin afectar flujos de negocios existentes, ya que casi siempre el método de un delegate es el comportamiento de un endpoint de mi aplicación y los métodos de los service los reutilizo en cualquier delegate.

    Responder
    • Jorge, mil gracias por tu comentario. Permíteme responderte.
      Orquestación de los service: en este caso si haces esto, ya no sería un delegate (que delega) sino estarías teniendo lógica de negocio (otro service) que trabaja invocando a otro service.
      Orden y legibilidad: No veo que puede llegar a tener de malo que un service llame a otro service siempre y cuando cada uno de ellos tenga responsabilidades claras (cohesión) definidas y verificando no llegar a tener alto acoplamiento.
      Si colocas reglas de negocio en los delegate, ya no son delegate.
      Y por último, el concepto de escalabilidad no es correcto, sería mejor extensibilidad. Dado que escabilidad habla de la capacidad de manejar un aumento de carga de trabajo (horarios pico) sin degradar su rendimiento

      Responder
  2. Entiendo que esto aplica para desarrollo de software principalmente.
    Se necesita conocimiento de términos técnicos para comprender mejor el esquema.
    Se aprecia bien conocimiento y organización en la presentación para que sea intuitiva.
    Felicitaciones

    Responder

Deja un comentario