不正破壊層パターン


レガシーシステムとの統合は楽しい楽しいマイルストーンではない.不十分なドキュメンテーション、サポートの不足、厄介なインターフェースとほんの少しの休眠バグより多くは、あなたが統合プロセスで直面するかもしれない問題の部分集合です.しかし、多くの場合、統合は、技術的および/または政治的理由の絶対的な必要性です.レガシーモデルが通常設計されていないので、レガシーシステムとの統合はシステムのリスクを課します.したがって、あなたの新しい、よく設計されたモデルはレガシー統合によって非常に腐敗しそうです.

統合戦略


レガシーシステムで動作する必要がある開発者は4つの選択肢に直面しています.
1 -溝をレガシー:共通のユースケースを持つことは常に統合の正当化ではない.開発者は、このオプションを考慮して、レガシーシステムの値を精査し、統合のコストに対して重み付けする.
2 -レガシーに適合する:組織がレガシーシステムを交換する計画がないとき、そして、レガシーモデルの品質が十分に許容可能であるとき、それから、遺産モデルに熱心に従うことはすべての統合コストを除去するでしょう.十分に確立された支配的なレガシーシステムに周辺拡張が加えられている状況では、適合主義的アプローチを真剣に考慮しなければならない.この選択は非常にホイールを再発明し、時間とお金を無駄に解決するための私たちの抵抗できない注入のために過小評価されていないほとんどのビジネス価値を提供する解決策.
3 -反腐敗層:レガシーモデルによる腐敗からモデルを分離するために非常に守備的な戦略.
4 -遺産を取り替えること:最も好ましいオプション.これらの野心的な試みが望ましくない結果に影響されやすいので、開発者はレガシーシステムの大規模で複雑なモデル(特にこれらのモデルがシステムの中心的な領域に属しているとき)をフェードアウトすることを控えるべきです.
反不正層解決策を試みる前に、慎重にオプション1と2を考え直すべきです彼らがメンテナンスと支持を必要とする組織のコンテクストマップに加えられる新しい要素を構成するという事実に加えて、反腐敗層は非常に醜くて複雑になることができます.反汚職層パターンに接近する前に、2つの顕著なデザインパターンについて簡単に話をしましょう

ファサードデ


FA Specadeは複雑なシステムへのより簡単なインタフェースを提供するのに用いられるパターンです.FAソアードはインターフェイスシステムのモデルに固執し、独自の新しいモデルを導入することを控えるべきです.
次のインターフェイスを持つ旅行計画システムを考えてみましょう.
public interface ITripService
{
    void BookAirline();
    void ReserveHotelRoom();
    void ReserveTouristAttractionTickets();
}
簡単なインターフェースを提供することができます-必要に応じて-操作全体を実行するために以下のFA SPADE ADEを通して.
public interface ITripServiceFacade
{
    void PlanItinerary();
}

アダプタ



ウォールアダプター
アダプターは、クライアントが意味をなすことができるインタフェースを提供することによって、2つの互換性がない抽象化の間の通信を許すパターンです.アダプターはクライアントの言語を話すことができて、それを他のサブシステムに適応させます.
次のインターフェイスを使用してアプリケーションをヘアイルを考える
public interface ITransportationService
{
    void StartTrip(string country, Zipcode from, Zipcode to);
}
しかしながら、いくつかの国では、旅行がZipcodesの代わりにソースと目的地の正確なコーディネイトに基づいてfaredされなければならないという洞察が現れたとき、アダプターが提供されました:
public interface ITransportationServiceAdapter
{
    void StartTrip(Coordinates from, Coordinates to);
}
アダプターがクライアントの言語を話すことに注意してください、そして、Adapteeに対応する要求を始めるのに責任があります.

不正破壊層



ローマ市を飲み込む大火事
反汚職層は、それが統合する必要がある他のモデルによって汚職からモデルを分離するために、FA包丁、アダプターと翻訳者のセットを結合します.2つのモデルの間で顧客サプライヤーの関係が存在する場合、または双方向でなければ、反汚損層は単方向であるかもしれません.
パターンを説明するために、最初に注文を出荷するのに必要なパッケージの数と寸法を推定することによって注文の配送コストを表示する電子商取引アプリケーションを考慮しましょう.次に、複雑なネットワーク横断アルゴリズムを使用して、配送のための最も安いルートを見つけることができます.これから、私たちは2つの異なった文脈を迅速に推論することができます:出荷と包装、それぞれ異なった能力と完全に独立したモデル.
その組織はすでに、長年にわたって使用されてきたレガシーパッケージサービスを所有していることが判明している.なぜなら、組織は電子商取引に向けて戦略を転換する前に、包装業界に特化してきたからである.建築家が密接にレガシーパッケージサービスを検討したように、彼らはサービスが非常に複雑であるため、遺産との統合が不可避であることを理解しました.レガシーモデルは非常に時代遅れであり、現在のビジネスモデルにもはや適合していない仮定をしたので、より密接な検討を行いました.したがって、レガシーパッケージングモデルへの出荷コンテキストを適合させることは、出荷側で高い腐敗を引き起こすでしょう.
残念なことに、パッケージングサービスはあまりに古くて、ドキュメント化されていなくて、システムの他の構成要素の向こう側にいくつかの特別な回避策を強制した多くのバグのために悪名高いです.この時点で、建築家は、出荷コンテキストをレガシーから出荷モデルを隔離している反腐敗層でパッケージコンテクストと統合するべきであると決めました.
モデルの弱点を強調するためにレガシー包装モデルを詳しく見てみましょう.
namespace Warehouse.Packaging 
{
    public class Container 
    { 
        //Irrelevant details
    }

    public class Package 
    { 
        public double LengthInInches { get; }
        public double WidthInInches { get; }
        public double HeightInInches { get; }
    }

    public class Item 
    {
        public double LengthInInches { get; }
        public double WidthInInches { get; }
        public double HeightInInches { get; }
    }

    public class Warehouse 
    {
        public string Code { get; }
        public string ZipCode { get; }
        public IEnumerable<Container> AvailableContainers { get; }
        //Further details of a messy model
    }

    public interface ILegacyPackagingService 
    {
        bool ValidateItemDimensions (IEnumerable<Item> items);
        IEnumerable<Package> PackageItems (IEnumerable<Item> items, Warehouse warehouse);
        IEnumerable<Package> OptimizePackaging (IEnumerable<Package> packages);
    }
}
上記のドメインモデルにはいくつかの問題があります.
1ILegacyPackagingService 検証、パッケージングと最適化が3つの別々のAPIとして提供される複雑なインターフェースを提供します-たぶん、これらの操作の全てが船積みの観点から単一の操作として見られるので、これは出荷モデルのニーズとよく合いません.
2Item and Package モデルは、すべての新しいモデルでメトリックシステムを採用する新しい組織全体のポリシーとの衝突を提示する測定の帝国システムを使用します.
3 -パッケージモジュールの倉庫モデルは非常に時代遅れであり、もはやどのようにビジネスモデル独自の倉庫を反映しています.
4PackageItems で定義されたAPIILegacyPackagingService パッケージロジックとの間の結合を紹介しますWarehouse モデル-パッケージが以前に組織の指定された倉庫の1つの中で起こることができる操作として見られた時から.しかしながら、現在の出荷モデルは、製品が外部のベンダーの貯蔵施設から直接出荷されるのを許容するので、そのような仮定を維持することができません.
明らかに、反汚職層はやるべき仕事がたくさんある.上記の問題のいずれかを船積みモデルに漏らすことを許すことは、レガシーシステムのすべての弱点を運ぶ破損したモデルをもたらすでしょう.第1の問題を緩和するために、反汚職層の中で、fa - ade - adeを導入します.
namespace Warehouse.Packaging.AntiCorruption.Shipping 
{
    public interface IPackagingServiceFacade 
    {
        IEnumerable<Warehouse.Packaging.Package> PackageItems (IEnumerable<Item> items, Warehouse warehouse);
    }

    public class PackagingServiceFacade : IPackagingServiceFacade 
    {
        private readonly ILegacyPackagingService _packagingService;

        public PackagingServiceFacade (ILegacyPackagingService packagingService) 
        {
            _packagingService = packagingService;
        }

        public IEnumerable<Warehouse.Packaging.Package> PackageItems (IEnumerable<Item> items, Warehouse warehouse) 
        {
            if (_packagingService.ValidateItemDimensions (items)) 
            {
                var packages = _packagingService.PackageItems (items, warehouse);
                var optimizedPackages = _packagingService.OptimizePackaging (packages);
                return optimizedPackages;
            }

            return Enumerable.Empty<Package> ();
        }
    }
}
Fa - Made - adeはパッケージングサービスと同じモデルを使用し、モデル要素は追加されず、言語は維持されていることに注意してください.FAは、アードのみモデルに優しいインターフェイスを提供しています.ファサードの下に置かれるWarehouse.Packaging パッケージコンテキストを包含するモジュール/名前空間(反腐敗層は複数のモジュールにまたがることができます).
第2の問題を説明するために、反汚職層は2つの文脈で使用される異なったモデルの間でマップするために翻訳オブジェクトを定義するでしょう、以下の例では、簡単な方法でマッピング操作を説明するためにautomapperを使用しています
namespace Logistics.Shipping.AntiCorruption.Packaging
{
    public class PackagerProfile : Profile
    {
        private const double InchesCmConversionConst = 0.39370;

        public PackagerProfile()
        {
            CreateMap<Product, Item>()
                .ForMember(dst => dst.LengthInInches, opt => opt.MapFrom(src => src.LengthInCm * InchesCmConversionConst))
                .ForMember(dst => dst.WidthInInches, opt => opt.MapFrom(src => src.WidthInCm * InchesCmConversionConst))
                .ForMember(dst => dst.HeightInInches, opt => opt.MapFrom(src => src.HeightInCm * InchesCmConversionConst));

            CreateMap<Warehouse.Packaging.Package, Logistics.Shipping.Package>()
                .ForMember(dst => dst.LengthInCm, opt => opt.MapFrom(src => src.LengthInInches / InchesCmConversionConst))
                .ForMember(dst => dst.WidthInCm, opt => opt.MapFrom(src => src.WidthInInches / InchesCmConversionConst))
                .ForMember(dst => dst.HeightInCm, opt => opt.MapFrom(src => src.HeightInInches / InchesCmConversionConst));
        }
    }
}
翻訳者は前後互換性のないモデルをマップするために使用することができます.
第3および第4の問題を扱うために、私たちは、不正サービスを提供していますWarehouse モデルと代わりにContainer パッケージオブジェクトを表す値オブジェクト
namespace Logistics.Shipping.AntiCorruption.Packaging
{
    public interface IPackagingService 
    {
        IEnumerable<Logistics.Shipping.Package> PackageItems (IEnumerable<Product> items, IEnumerable<Container> containers);
    }

    public class PackagingServiceAdapter : IPackagingService 
    {
        private readonly IPackagingServiceFacade _packagingServiceFacade;
        private readonly IMapper _mapper;

        public PackagingServiceAdapter (IPackagingServiceFacade packagingServiceFacade, IMapper mapper) 
        {
            _packagingServiceFacade = packagingServiceFacade;
            _mapper = mapper;
        }

        public IEnumerable<Logistics.Shipping.Package> PackageItems (IEnumerable<Product> products, IEnumerable<Container> containers) 
        {
            Warehouse warehouse; //Logic to initialize a transient dummy warehouse
            var items = _mapper.Map<IEnumerable<Item>> (products); //Using the translator to map the Product model to an Item
            var packages = _packagingServiceFacade.PackageItems (items, warehouse); //Calling the Façade 
            return _mapper.Map<IEnumerable<Logistics.Shipping.Package>> (packages); //Mapping the Packager's result to the Package model defined in the Shipping context
        }
    }
}
結果はレガシーパッケージサービスと統合されています.
namespace Logistics.Shipping 
{
    public class Zone 
    { 
    }

    public class Product 
    {
        public double LengthInCm { get; }
        public double WidthInCm { get; }
        public double HeightInCm { get; }
    }

    public class Package 
    {
        public double LengthInCm { get; }
        public double WidthInCm { get; }
        public double HeightInCm { get; }
    }

    public class Courier 
    {
        private IPackagingService _packagingService;

        public double GetQuote (IEnumerable<Product> items, Zone source, Zone destination) 
        {
            var packagingCapabilities = _capabilitiesService.GetPackagingCapabilities (source);
            var packages = _packagingService.PackageItems (items, packagingCapabilities.Containers);
            //Shipping specific logic goes here
        }
    }
}
ドメインモデルのラフマップは次の通りです.

防汚層に関する詳細は簡潔に省略した.

結論



万里の長城
反腐敗層を開発することは多くの分析と開発作業を必要とする時間を要するプロセスです.建築家はすぐにこの解決策にジャンプしないでください、そして、代わりに他の単純な選択肢を考慮してはいけません、しかし、他の選択がないとき、敵対的な侵入から要塞を囲んでいる壁と同じように、反腐敗層は、あなたのモデルを外側の影響から保護して、あなたが孤立して働くのを許します.