architecture - hashtags - Puertos y adaptadores/arquitectura hexagonal-aclaración de términos e implementación
hashtag de arquitectura para instagram (4)
Alguien en mi trabajo hizo una gran presentación interna sobre esta arquitectura. Al final, durante el turno de preguntas, otro colega preguntó:
¿No es esto solo una arquitectura en capas con un nombre diferente y dibujada de manera diferente?
Y, para ser honesto, eso es cierto en gran medida. Para muchas aplicaciones, una arquitectura hexagonal se estructurará de manera idéntica a una arquitectura en capas, con algunos detalles específicos:
- Existe una mayor disciplina en la definición de interfaces entre cada capa (los puertos), en lugar de llamar impl a impl.
- Hay un enfoque en "el núcleo" (lógica de negocios) como la capa más importante, y todas las demás capas (los adaptadores) se ven como algo subordinadas.
- El enfoque en la definición de interfaces desde la perspectiva del núcleo evita que el lenguaje de los adaptadores se filtre en el núcleo. Por ejemplo, si está poniendo su persistencia (por ejemplo, Hibernate) en un adaptador, entonces no debería tener ninguna clase de @Entity en su núcleo.
Se puede ver que, incluso cuando se hacen todas estas cosas, sigue siendo solo una arquitectura en capas, solo que los límites entre las capas son bastante estrictos y un enfoque en la capa central.
Entonces, para responder específicamente a la pregunta, puede comprender los puertos y los adaptadores reconociendo que los puertos son las interfaces que entran y salen del núcleo, y los adaptadores son solo las capas de implementación que no son el núcleo.
Después de leer diferentes fuentes sobre la arquitectura de Puertos y Adaptadores, incluido el artículo original de Alistair Cockburn, todavía no estoy seguro del significado definido de los términos "puerto" y "adaptador", especialmente cuando se trata de asignar estos conceptos a los artefactos de implementación.
Varias fuentes (por ejemplo, esta publicación ) implican que los puertos en este patrón de arquitectura son artefactos en el exterior, seguidos por los adaptadores en la capa intermedia que se traducen entre los puertos y la aplicación que está en el corazón.
Sin embargo, en el artículo original de Cockburn, los puertos aparecen tanto en el exterior como en el interior de la capa del adaptador dependiendo de la dirección de la comunicación:
- Comunicación entrante: " A medida que los eventos llegan del mundo exterior a un puerto, un adaptador de tecnología específica lo convierte en una llamada o mensaje de procedimiento utilizable y lo pasa a la aplicación " .
- Comunicación saliente: " Cuando la aplicación tiene algo que enviar, la envía a través de un puerto a un adaptador, lo que crea las señales adecuadas que necesita la tecnología de recepción (humana o automática) " .
En realidad, para mí, ni el enfoque "todo fuera" ni el enfoque "dentro y fuera" tienen sentido: vería los puertos como artefactos que siempre se colocan al lado de la aplicación, independientemente de la dirección de la comunicación. Imo, esto también sería coherente con las metáforas de puerto y adaptador : E. g. teniendo un dispositivo con un puerto serie, para conectar otro dispositivo sin un puerto serie a este, necesitaría un adaptador que adapte las comunicaciones entrantes y salientes desde el punto de vista de mi dispositivo.
Para la implementación de esta arquitectura, vería la definición de los puertos en lugar de una parte de mi aplicación donde vería que los diferentes adaptadores están "fuera" de mi aplicación. P.ej. una implementación de un solo puerto podría consistir en una facade
(a ser llamada por los adaptadores para la comunicación de entrada) y una interface
(a ser implementada por los adaptadores para la comunicación de salida).
¿Cuál es el significado correcto de los términos puerto y adaptador y cómo se pueden asignar estos conceptos a los artefactos de implementación?
ACTUALIZAR:
Encontré este artículo que se asemeja a mi comprensión. La pregunta sigue siendo si hay algún tipo de acuerdo común.
Creo que es un concepto bastante simple. Usted tiene el núcleo de la aplicación, que no depende de nada fuera de él. Por ejemplo, no depende de los marcos HTTP, los controladores de base de datos, los marcos de correo, etc. Este núcleo tiene una interfaz muy específica según el dominio de su problema, por lo que el código del núcleo de su aplicación solo debería cambiar si cambia su dominio del problema. .
Por ejemplo, tienes publicaciones en el blog y quieres agregar categorías a ellas. Por lo tanto, las categorías deben fluir a través de todo el sistema, desde la comunicación HTTP a las bases de datos por escrito y viceversa por lectura.
Ahora, qué pasa si desea reemplazar su base de datos MySQL, por ejemplo, con MongoDB, porque ¿por qué no? No debería afectar al núcleo, porque la aplicación sigue haciendo exactamente lo mismo: almacena sus publicaciones de blog y ofc. Sus categorías y las devuelve a demanda. Entonces, lo que necesita es solo un adaptador MondogDB en este caso, que puede usar en lugar de su adaptador MySQL.
¿Qué sucede si desea, por ejemplo, una API REST y no solo una página HTML simple? Todavía no afecta el núcleo de su aplicación, por lo que necesita otro adaptador para la comunicación REST.
Entonces, en mi opinión, sus adaptadores deben tener interfaces específicas definidas en el núcleo de su aplicación, y los puertos deben comunicarse con el núcleo de la aplicación usando estos adaptadores. Entonces, en mi opinión, los puertos no necesariamente existen como clases, solo los adaptadores y su interfaz. Este concepto tiene sentido, porque el núcleo de su aplicación no estará estrechamente acoplado a los puertos que desea usar, solo a las interfaces de adaptador que defina. (Por cierto, hay varias arquitecturas similares. Como arquitectura limpia, arquitectura de cebolla, que usan el mismo concepto con un vocabulario diferente).
Desde mi punto de vista, después de leer el artículo original y ver algunas charlas de Alistair Cockurn ("Alistair en el Hexágono"), creo que el enfoque correcto es lo que llamarías "todo adentro", es decir, en la comunicación tanto entrante como saliente. Los puertos están "dentro" de los adaptadores. Los adaptadores se encuentran entre los actores externos que interactúan con la aplicación y los puertos. Los puertos pertenecen a la aplicación.
Para la comunicación entrante, el actor (actor conductor) activa la comunicación mediante un adaptador de controlador. Este adaptador llama a un puerto de controlador de la aplicación, solicitando que la aplicación haga algo.
Para la comunicación saliente, la aplicación activa la comunicación con un actor impulsado al definir y llamar a un puerto controlado. Este puerto es un contrato (generalmente una interfaz) de lo que la aplicación necesita en términos del propósito. Este puerto es implementado por un adaptador, que se comunica con el actor externo.
Las dependencias deben ser:
Driver Actor -> Driver Adapter -> Hexagon <- Driven Adapter <- Driven Actor
Los puertos pertenecen al hexágono:
Los puertos del controlador son la API ofrecida por el hexágono a los adaptadores del controlador.
Los puertos impulsados son el SPI que necesita el hexágono, implementado por los adaptadores impulsados.
También luché mucho con esta frase que mencionas, que aparece en el artículo original:
"A medida que los eventos llegan del mundo exterior a un puerto, un adaptador específico de la tecnología lo convierte en una llamada o mensaje de procedimiento utilizable y lo pasa a la aplicación".
Es decir que los puertos del controlador están "fuera" de los adaptadores de controlador. Pero leyendo todo el artículo y viendo las conversaciones, creo que no es así. Lo que la frase llama "puerto", es simplemente la interacción entre el actor controlador externo y el adaptador. El "puerto" debe ser la interacción entre el adaptador y la aplicación ("... lo pasa a la aplicación").
inf3rno dio una buena respuesta que aclara la pregunta original, pero puede ser útil resaltar otros usos para puertos y adaptadores.
Según mi entendimiento, el puerto es una expresión de su interfaz .
El puerto:
- Define la exposición de la funcionalidad del núcleo (para puertos ''entrantes'')
- Define la vista del núcleo del mundo exterior (para puertos ''salientes'')
El adaptador:
- Se encuentra fuera del componente (hexágono)
- Se utiliza para garantizar que el transporte entre el puerto y el destino se realice de una manera que satisfaga el contrato con la interfaz del puerto.
- Es lo que reemplazas (usando la inyección de dependencia) para probar el hexágono
El puerto debe aceptar el adaptador y asegurarse de que el adaptador implementa la interfaz. Entonces simplemente debería llamar a los métodos / funciones apropiados en el adaptador.
El puerto debe incluirse en las pruebas de comunicación. En ese caso, lo que se ''imita'' son los núcleos de dos hexágonos vecinos (o un hexágono y un servicio) y prueba el conjunto puerto / adaptador / adaptador / puerto.
Para obtener más información, puede ver la charla sobre microservicios hexagonales que James Gardner y yo dimos en la reunión de microservicios Skillsmatter de Londres en julio de 2014 .