本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。
反腐败层模式
意图
反腐败层 (ACL) 模式充当中介层,将域模型语义从一个系统转换为另一个系统。在使用上游团队建立的通信合约之前,它将上游有界上下文(monolith)的模型转换为适合下游有界上下文(微服务)的模型。当下游边界上下文包含核心子域,或者上游模型是不可修改的遗留系统时,这种模式可能适用。当呼叫者的呼叫必须透明地重定向到目标系统时,它还可以防止呼叫者发生变化,从而降低转型风险和业务中断。
动机
在迁移过程中,当单体应用程序迁移到微服务时,新迁移的服务的域模型语义可能会发生变化。当需要使用整体架构中的功能来调用这些微服务时,应将呼叫路由到迁移的服务,而无需对呼叫服务进行任何更改。ACL 模式允许单体通过充当适配器或将调用转换为更新的语义的外观层来透明地调用微服务。
适用性
在以下情况下,可以考虑使用此模式:
-
您现有的单体应用程序必须与已迁移到微服务的函数进行通信,并且迁移的服务域模型和语义与原始功能不同。
-
两个系统的语义不同,需要交换数据,但是修改一个系统使其与另一个系统兼容是不切实际的。
-
您想使用一种快速简化的方法来使一个系统适应另一个系统,同时将影响降至最低。
-
您的应用程序正在与外部系统通信。
问题和注意事项
-
团队依赖关系:当系统中的不同服务由不同的团队拥有时,迁移服务中的新域模型语义可能会导致调用系统的变化。但是,团队可能无法以协调的方式进行这些更改,因为他们可能还有其他优先事项。ACL 解耦被调用方并转换呼叫以匹配新服务的语义,从而无需呼叫者在当前系统中进行更改。
-
操作开销:ACL 模式需要额外的工作来操作和维护。这项工作包括将 ACL 与监控和警报工具、发布流程以及持续集成和持续交付 (CI/CD) 流程集成。
-
单点故障:ACL 中的任何故障都可能使目标服务无法访问,从而导致应用程序问题。为了缓解这个问题,你应该内置重试功能和断路器。要了解有关这些选项的更多信息,请参阅使用退避和断路器模式的重试。设置适当的警报和日志将缩短平均解决时间 (MTTR)。
-
技术债务:作为迁移或现代化战略的一部分,请考虑 ACL 是临时解决方案还是临时解决方案,还是长期解决方案。如果是临时解决方案,则应将 ACL 记录为技术债务,并在所有依赖呼叫者迁移完毕后将其停用。
-
延迟:由于请求从一个接口转换为另一个接口,额外的层可能会带来延迟。我们建议您在将 ACL 部署到生产环境之前,先定义和测试对响应时间敏感的应用程序的性能容差。
-
扩展瓶颈:在服务可以扩展到峰值负载的高负载应用程序中,ACL 可能会成为瓶颈,并可能导致扩展问题。如果目标服务按需扩展,则应设计 ACL 以相应地进行扩展。
-
特定于服务或共享的实现:您可以将 ACL 设计为共享对象,以将呼叫转换和重定向到多个服务或特定于服务的类别。在确定 ACL 的实现类型时,请考虑延迟、扩展和容错能力。
实施
您可以在单体应用程序中将 ACL 作为特定于要迁移的服务的类或独立的服务来实现。在所有依赖服务都迁移到微服务架构后,必须停用 ACL。
架构简析
在以下示例架构中,单体应用程序有三项服务:用户服务、购物车服务和账户服务。购物车服务依赖于用户服务,应用程序使用单体关系数据库。

在以下架构中,用户服务已迁移到新的微服务。购物车服务调用用户服务,但在整体架构中不再提供该实现。 当新迁移的服务位于单体应用程序内部时,其接口也可能与其之前的接口不匹配。

如果购物车服务必须直接调用新迁移的用户服务,则需要更改购物车服务并对整体应用程序进行全面测试。这可能会增加转型风险和业务中断。目标应该是尽量减少对单体应用程序现有功能的更改。
在这种情况下,我们建议您在旧的用户服务和新迁移的用户服务之间引入 ACL。ACL 可用作适配器或外观,将呼叫转换为较新的接口。ACL 可以在整体应用程序中作为特定于已迁移服务的类(例如UserServiceFacade
或UserServiceAdapter
)来实现。在所有依赖服务都迁移到微服务架构后,必须停用反腐败层。

使用 AWS 服务实施
下图显示了如何使用 AWS 服务实现此 ACL 示例。

用户微服务从 ASP.NET 单体应用程序中迁移出来,并作为函数AWS Lambda
在整体架构内Program.cs
调用用户服务 (UserInMonolith.cs
) 时,呼叫将路由到 ACL ()。UserServiceACL.cs
ACL 将调用转换为新的语义和接口,并通过 API Gateway 端点调用微服务。调用方 (Program.cs
) 不知道用户服务和 ACL 中发生的转换和路由。由于调用方不知道代码的变化,因此业务中断减少了,转型风险也降低了。
代码示例
以下代码片段提供了对原始服务的更改以及的实现。UserServiceACL.cs
收到请求后,原始用户服务会调用 ACL。ACL 会转换源对象以匹配新迁移的服务的接口,调用该服务,并将响应返回给调用方。
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; } }
GitHub 存储库
有关此模式示例架构的完整实现,请参见 GitHub 存储库,网址为https://github.com/aws-samples/anti-corruption-layer-pattern