Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.
Patrón de la capa anticorrupción
Intención
El patrón de capa anticorrupción (ACL) actúa como una capa de mediación que traduce la semántica del modelo del dominio de un sistema a otro. Traduce el modelo del contexto limitado ascendente (monolito) a un modelo que se adapta al contexto limitado descendente (microservicio) antes de consumir el contrato de comunicación que establece el equipo ascendente. Este patrón podría aplicarse cuando el contexto limitado descendente contiene un subdominio central o el modelo ascendente es un sistema heredado que no se puede modificar. También reduce el riesgo de transformación y la interrupción de la actividad empresarial, ya que evita que los autores de la llamada cambien cuando sus llamadas se tienen que redirigir de manera transparente al sistema de destino.
Motivación
Durante el proceso de migración, cuando una aplicación monolítica se migra a los microservicios, podría haber cambios en la semántica del modelo del dominio del servicio recién migrado. Cuando las características del monolito sean necesarias para llamar a estos microservicios, las llamadas deben enrutarse al servicio migrado sin necesidad de hacer cambios en los servicios de llamadas. El patrón ACL permite al monolito llamar a los microservicios de manera transparente, ya que actúa como un adaptador o una capa de fachada que traduce las llamadas a la semántica más reciente.
Aplicabilidad
Considere la posibilidad de utilizar este patrón cuando se suceda lo siguiente:
-
La aplicación monolítica actual tiene que comunicarse con una función que se migró a un microservicio y el modelo y la semántica del dominio del servicio migrado difieren de la característica original.
-
Dos sistemas tienen una semántica distinta y deben intercambiar datos, pero no resulta práctico modificar un sistema para que sea compatible con el otro.
-
Se recomienda utilizar un enfoque rápido y simplificado para adaptar un sistema a otro con un impacto mínimo.
-
La aplicación se comunica con un sistema externo.
Problemas y consideraciones
-
Dependencias entre equipos. Cuando los servicios distintos de un sistema pertenecen a equipos distintos, la semántica nueva del modelo del dominio de los servicios migrados puede provocar cambios en los sistemas de llamadas. Sin embargo, es posible que los equipos no puedan hacer estos cambios de manera coordinada, porque es posible que tengan otras prioridades. La ACL desvincula a los destinatarios de las llamadas y traduce las llamadas para que coincidan con la semántica de los servicios nuevos, lo que evita que los autores de las llamadas tengan que hacer cambios en el sistema actual.
-
Sobrecarga operativa. El funcionamiento y el mantenimiento del patrón de ACL requieren un esfuerzo adicional. Este trabajo incluye la integración de la ACL con herramientas de supervisión y alerta, el proceso de publicación y los procesos de integración y entrega continuas (CI/CD).
-
Punto único de error. Los errores en la ACL pueden impedir la conexión con el servicio de destino y provocar problemas en las aplicaciones. Para mitigar este problema, debe incorporar funcionalidades de reintento y disyuntores. Consulte los patrones de reintento con retroceso y disyuntores para más información sobre estas opciones. La configuración de alertas y del registro adecuados mejorará el tiempo medio de la resolución (MTTR).
-
Deuda técnica. Como parte de su estrategia de migración o modernización, considere si la ACL será una solución transitoria o provisional, o una solución a largo plazo. Si se trata de una solución provisional, debe registrar la ACL como deuda técnica y retirarla una vez que se hayan migrado todos los autores de llamadas dependientes que llamaron.
-
Latencia. La capa adicional puede introducir latencia debido a la conversión de las solicitudes de una interfaz a otra. Le recomendamos definir y probar la tolerancia al rendimiento en las aplicaciones que son sensibles al tiempo de respuesta antes de implementar la ACL en los entornos de producción.
-
Cuello de botella de escalado. En aplicaciones con cargas elevadas, en las que los servicios pueden escalar hasta alcanzar la carga máxima, la ACL puede convertirse en un cuello de botella y provocar problemas de escalabilidad. Si el servicio de destino se escala según la demanda, debe diseñar la ACL para que escale de manera adecuada.
-
Implementación compartida o específica del servicio. Puede diseñar la ACL como un objeto compartido para convertir y redirigir las llamadas a varios servicios o clases de servicios específicos. Tenga en cuenta la latencia, el escalado y la tolerancia a los errores al determinar el tipo de implementación de la ACL.
Implementación
Puede implementar la ACL en la aplicación monolítica como una clase específica del servicio que se va a migrar o como un servicio independiente. La ACL debe retirarse una vez que todos los servicios dependientes se hayan migrado a la arquitectura de microservicios.
Arquitectura de alto nivel
En la arquitectura de ejemplo siguiente, una aplicación monolítica tiene tres servicios: servicio de usuario, servicio de carrito y servicio de cuentas. El servicio de carrito depende del servicio de usuario y la aplicación utiliza una base de datos relacional monolítica.
En la arquitectura siguiente, el servicio de usuario se migró a un microservicio nuevo. El servicio de carrito llama al servicio de usuario, pero la implementación deja de estar disponible en el monolito. También es probable que la interfaz del servicio recién migrado no coincida con la interfaz anterior, cuando estaba dentro de la aplicación monolítica.
Si el servicio de carrito tiene que llamar de manera directa al servicio de usuario recién migrado, será necesario hacer cambios en el servicio de carrito y hacer pruebas exhaustivas de la aplicación monolítica. Esto puede aumentar el riesgo de transformación y la interrupción del negocio. La meta debe ser minimizar los cambios en la funcionalidad existente de la aplicación monolítica.
En este caso, le recomendamos introducir una ACL entre el servicio de usuario anterior y el servicio de usuario recién migrado. La ACL funciona como un adaptador o una fachada que convierte las llamadas en la interfaz más nueva. La ACL se puede implementar dentro de la aplicación monolítica como una clase (por ejemplo, UserServiceFacade o UserServiceAdapter) específica del servicio que se migró. La capa anticorrupción debe retirarse una vez que todos los servicios dependientes se hayan migrado a la arquitectura de microservicios.
Implementación mediante los servicios de AWS
En el diagrama siguiente se muestra cómo implementar este ejemplo de la ACL mediante el uso de los servicios de AWS.
El microservicio de usuario se migra de la aplicación monolítica de ASP.NET y se implementa como una función de AWS Lambda
Cuando Program.cs llama al servicio de usuario (UserInMonolith.cs) dentro del monolito, la llamada se enruta a la ACL (UserServiceACL.cs). La ACL traduce la llamada a la nueva semántica e interfaz, y llama al microservicio a través del punto de conexión de API Gateway. El autor de la llamada (Program.cs) no conoce la traducción y el enrutamiento que tienen lugar en el servicio de usuario y en la ACL. Como el autor de la llamada no está al tanto de los cambios en el código, se producen menos interrupciones en la actividad comercial y se reduce el riesgo de transformación.
Código de muestra
El fragmento de código siguiente proporciona los cambios en el servicio original y la implementación de UserServiceACL.cs. Cuando se recibe una solicitud, el servicio de usuario original llama a la ACL. La ACL convierte el objeto del origen para que coincida con la interfaz del servicio recién migrado, llama al servicio y devuelve la respuesta al autor de la llamada.
public class UserInMonolith: IUserInMonolith { private readonly IACL _userServiceACL; public UserInMonolith(IACL userServiceACL) => (_userServiceACL) = (userServiceACL); public async Task<HttpStatusCode> UpdateAddress(UserDetails userDetails) { //Wrap the original object in the derived class var destUserDetails = new UserDetailsWrapped("user", userDetails); //Logic for updating address has been moved to a microservice return await _userServiceACL.CallMicroservice(destUserDetails); } } public class UserServiceACL: IACL { static HttpClient _client = new HttpClient(); private static string _apiGatewayDev = string.Empty; public UserServiceACL() { IConfiguration config = new ConfigurationBuilder().AddJsonFile(AppContext.BaseDirectory + "../../../config.json").Build(); _apiGatewayDev = config["APIGatewayURL:Dev"]; _client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); } public async Task<HttpStatusCode> CallMicroservice(ISourceObject details) { _apiGatewayDev += "/" + details.ServiceName; Console.WriteLine(_apiGatewayDev); var userDetails = details as UserDetails; var userMicroserviceModel = new UserMicroserviceModel(); userMicroserviceModel.UserId = userDetails.UserId; userMicroserviceModel.Address = userDetails.AddressLine1 + ", " + userDetails.AddressLine2; userMicroserviceModel.City = userDetails.City; userMicroserviceModel.State = userDetails.State; userMicroserviceModel.Country = userDetails.Country; if (Int32.TryParse(userDetails.ZipCode, out int zipCode)) { userMicroserviceModel.ZipCode = zipCode; Console.WriteLine("Updated zip code"); } else { Console.WriteLine("String could not be parsed."); return HttpStatusCode.BadRequest; } var jsonString = JsonSerializer.Serialize<UserMicroserviceModel>(userMicroserviceModel); var payload = JsonSerializer.Serialize(userMicroserviceModel); var content = new StringContent(payload, Encoding.UTF8, "application/json"); var response = await _client.PostAsync(_apiGatewayDev, content); return response.StatusCode; } }
Repositorio GitHub
Para obtener una implementación completa de la arquitectura de ejemplo para este patrón, consulte el repositorio de GitHub en https://github.com/aws-samples/anti-corruption-layer-pattern