

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 反貪汙層模式
<a name="acl"></a>

## 意圖
<a name="acl-intent"></a>

反損毀層 (ACL) 模式可做為中介層，將網域模型語意從一個系統轉譯為另一個系統。它會先將上游邊界內容 (monolith) 的模型轉換為適合下游邊界內容 (microservice) 的模型，再使用上游團隊建立的通訊合約。當下游邊界內容包含核心子網域，或上游模型是無法修改的舊版系統時，此模式可能適用。當來電者的呼叫必須以透明方式重新導向至目標系統時，它還可以防止對來電者進行變更，從而降低轉換風險和業務中斷。

## 動機
<a name="acl-motivation"></a>

在遷移過程中，當單一應用程式遷移至微服務時，新遷移服務的網域模型語意可能會有變更。當需要整體內的功能來呼叫這些微服務時，呼叫應該路由到遷移的服務，而不需要對呼叫服務進行任何變更。ACL 模式允許整體以透明的方式呼叫微服務，方法是做為轉接器或外觀圖層，將呼叫轉換為較新的語意。

## 適用性
<a name="acl-applicability"></a>

考慮在下列情況下使用此模式：
+ 您現有的單體應用程式必須與已遷移至微服務的函數通訊，而遷移的服務網域模型和語意與原始功能不同。
+ 兩個系統具有不同的語意，需要交換資料，但修改一個系統以與其他系統相容並不實際。
+ 您想要使用快速且簡化的方法，將一個系統調整為另一個系統，並將影響降至最低。
+ 您的應用程式正在與外部系統通訊。

## 問題和考量
<a name="acl-issues"></a>
+ **團隊相依性：**當系統中的不同服務由不同的團隊擁有時，遷移服務中的新網域模型語意可能會導致呼叫系統中的變更。不過，團隊可能無法以協調的方式進行這些變更，因為他們可能有其他優先順序。ACL 會分離受話方，並轉譯呼叫以符合新服務的語意，因此不需要呼叫者在目前的系統中進行變更。
+ **操作開銷：**ACL 模式需要額外的精力來操作和維護。此工作包括整合 ACL 與監控和警示工具、發行程序，以及持續整合和持續交付 (CI/CD) 程序。
+ **單點故障：**ACL 中的任何故障都可能導致目標服務無法連線，造成應用程式問題。若要緩解此問題，您應該建置重試功能和斷路器。請參閱[使用退避](retry-backoff.md)和[斷路器](circuit-breaker.md)模式重試，以進一步了解這些選項。設定適當的提醒和記錄將改善平均解決時間 (MTTR)。
+ **技術負債：**作為遷移或現代化策略的一部分，請考慮 ACL 是暫時性還是臨時解決方案，還是長期解決方案。如果是臨時解決方案，您應該將 ACL 記錄為技術債務，並在遷移所有相依發起人之後解除委任。
+ **延遲：**由於將請求從一個界面轉換到另一個界面，額外的 layer 可能會引入延遲。建議您在將 ACL 部署到生產環境之前，在對回應時間敏感的應用程式中定義和測試效能容錯能力。
+ **擴展瓶頸：**在服務可以擴展到尖峰負載的高負載應用程式中，ACL 可能會成為瓶頸，並可能導致擴展問題。如果目標服務隨需擴展，您應該設計 ACL 來相應擴展。
+ **服務特定或共用實作：**您可以將 ACL 設計為共用物件，以將呼叫轉換和重新導向至多個服務或服務特定類別。當您判斷 ACL 的實作類型時，請將延遲、擴展和容錯能力納入考量。

## 實作
<a name="acl-implementation"></a>

您可以在單體應用程式中實作 ACL，做為要遷移之服務的特定類別，或做為獨立服務。在將所有相依服務遷移至微服務架構之後，必須停用 ACL。

### 高層級架構
<a name="acl-high-level-arch"></a>

在下列範例架構中，單一應用程式有三種服務：使用者服務、購物車服務和帳戶服務。購物車服務取決於使用者服務，而應用程式會使用單體關聯式資料庫。

![\[具有三個服務的單體應用程式。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/acl-1.png)


在下列架構中，使用者服務已遷移至新的微服務。購物車服務會呼叫使用者服務，但整體內不再提供實作。 當新遷移服務的界面位於單體應用程式內時，它也可能與其先前的界面不相符。

![\[將一個服務移出微服務的單體應用程式。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/acl-2.png)


如果購物車服務必須直接呼叫新遷移的使用者服務，這將需要變更購物車服務，並徹底測試整體應用程式。這可能會增加轉型風險和業務中斷。目標是將整體應用程式的現有功能變更降至最低。

在這種情況下，我們建議您在舊使用者服務和新遷移的使用者服務之間引入 ACL。ACL 可做為轉接器或外觀，將呼叫轉換為較新的界面。ACL 可在單體應用程式內實作為 類別 （例如， `UserServiceFacade`或 `UserServiceAdapter`)，其專屬於已遷移的服務。所有相依服務遷移至微服務架構後，必須停用反貪汙層。

![\[新增反貪汙層。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/acl-3.png)


### 使用 AWS 服務實作
<a name="acl-aws-services"></a>

下圖顯示如何使用 服務實作此 ACL 範例 AWS 。

![\[使用 AWS 服務實作 ACL 模式。\]](http://docs.aws.amazon.com/zh_tw/prescriptive-guidance/latest/cloud-design-patterns/images/acl-4.png)


使用者微服務會從 ASP.NET 整體應用程式遷移，並在 AWS 上部署為 [AWS Lambda](https://aws.amazon.com/lambda/)函數。Lambda 函數的呼叫會透過 [Amazon API Gateway](https://aws.amazon.com/api-gateway/) 路由。ACL 部署在整體中，以轉譯呼叫，以適應使用者微服務的語意。

當 在整體內部`Program.cs`呼叫使用者服務 (`UserInMonolith.cs`) 時，呼叫會路由到 ACL (`UserServiceACL.cs`)。ACL 會將呼叫轉譯為新的語意和界面，並透過 API Gateway 端點呼叫微服務。發起人 (`Program.cs`) 不知道使用者服務和 ACL 中發生的轉譯和路由。由於發起人不知道程式碼變更，因此業務中斷和轉型風險較低。

### 範本程式碼
<a name="acl-sample-code"></a>

下列程式碼片段提供原始服務和 實作的變更`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 儲存庫
<a name="acl-github-repo"></a>

****

如需此模式的範例架構的完整實作，請參閱 GitHub 儲存庫，網址為 https：//[https://github.com/aws-samples/anti-corruption-layer-pattern](https://github.com/aws-samples/anti-corruption-layer-pattern)。

## 相關內容
<a name="acl-resources"></a>
+ [Strangler 無花果模式](strangler-fig.md)
+ [斷路器模式](circuit-breaker.md)
+ [使用輪詢重試模試](retry-backoff.md)