2017-10-01 4 views
0

私はActive Recordと単方向APIを使って設計されたビジネスエンティティを持つビジネスレイヤを持っています。単方向APIでDIコンテナを渡すのは避けてください。

  • コードを複雑にすることなく、DALから構築されたオブジェクトにid値を渡すなど、実行時の値をどのように処理する必要がありますか?これはパラメータのオーバーライドで行うことができますか?製品は、容器をラップルートでどのように他のビジネスエンティティを作成し、私もダウンした容器を渡しておりません場合は、依存関係を渡します(アンチパターン/サービスロケータのそれをより作る)

  • とアプリケーションファサードとして機能し、残りのBALへのエントリポイントとして機能します。私が解決しようとしている作品は、ここでProduct.FindCustomerCustomer.FindDocument

    public class Product 
    { 
        private IUnityContainer container; 
    
        public void RegisterType<T>() ... 
        public void RegisterType<TFrom, TTo>() ... 
    
        public Customer FindCustomer(string customerNumber) 
        { 
         var id = context.Customers 
             .Where(p => p.CustomerNumber == customerNumber) 
             .Select(p => p.Id) 
             .Single(); 
    
         var customer = container.Resolve<Customer>(...); // param override? 
    
         customer.Load(); 
    
         return customer; 
        } 
    } 
    
    public class Customer : BusinessEntity<Data.Customer, Guid> 
    { 
        private readonly IDocumentFileProvider provider; 
    
        public Customer(IDataContext context, IDocumentFileProvider provider) : base(context) 
        { 
         this.provider = provider; 
        } 
    
        public Customer(IDataContext context, IDocumentFileProvider provider, Guid id) : base(context, id) 
        { 
         this.provider = provider; 
        } 
    
        public Document FindDocument(string code) 
        { 
         var id = context.Documents 
             .Where(p => p.CustomerNumber == customerNumber) 
             .Select(p => p.Id) 
             .Single() 
    
         var document = new Document(context, provider, id); // Here is the issue 
    
         document.Load(); 
    
         return document; 
        } 
    } 
    
    public class Document : BusinessEntity<Data.Document, Guid> 
    { 
        public Document(IDataContext context, IDocumentFileProvider provider) : base(context) 
        { 
         this.provider = provider; 
        } 
    
        public Document(IDataContext context, IDocumentFileProvider provider, Guid id) : base(context, id) 
        { 
         this.provider = provider; 
        } 
    
        public IDocumentFile GetFile() 
        { 
         return provider.GetFile(); 
        } 
    } 
    

    である簡単に他のクラスです。

    public abstract class ActiveRecord<TEntity, TKey> 
    { 
        protected ActiveRecord(IDataContext context) 
        { 
        } 
    
        public virtual void Load() ... 
        public virtual void Save() ... 
        public virtual void Delete() ... 
    } 
    
    public abstract class BusinessEntity<TEntity, TKey> : ActiveRecord<TEntity, TKey> 
    { 
        protected BusinessEntity(IDataContext context) : base(context) 
        { 
        } 
    
        protected BusinessEntity(IDataContext context, TKey id) : this(context) 
        { 
        } 
    
        ... 
    } 
    

    階層が非常に深くなり、より短い例することができます

    var customer = product.FindCustomer("123"); 
    var account = customer.FindAccount("321"); 
    var document = account.FindDocument("some_code"); 
    var file  = document.GetFile(); 
    

    私の目標の一つがAにある)ドメイン、およびBをモデル化するには)理解することは非常に簡単にAPIを提供しています。現在のところ、BALはService Locatorを使用していますが、これを適切なIoC/DIとコンテナに置き換えることを試しています。

    APIが深く、より多くの依存関係が必要な場合、すべての上位クラスのコンストラクタはかなり長くなる可能性があり、もはや結束するように見えなくなる可能性があります。

  • 答えて

    0

    半減測定を使用してDIをほとんどのアプリケーション設計に絞り込むことができますが、残念なことに、すべてのアプリケーション設計が特にDIに優しいわけではありません。 「スマートエンティティ」の作成はAPI設計に関しては魔法のように思えますが、実際にはSRPに違反している(ロードと保存はスライス方法にかかわらず別々の責任です)。

    1. DIのより助長しているデザインを検索し、DIのより助長しているデザインを探すあなたのAPI
    2. ために、そのオブジェクトモデルを使用し、ファサードオブジェクトを作成します。

      あなたは基本的に4つのオプションを持っていますあなたのAPIのためのモデル

    3. 使用プロパティの注入は使用サービスロケータをあなたの依存関係をロードして、コンストラクタ
    4. 以上のエンドユーザ制御を与えるために

    DIと一緒にCSLAを使用しようとしたときに同様の壁に突き当たり、多くの試みの後で最終的にはより良い設計アプローチを見つける必要があったのがCSLAだと最終的に判断しました。

    私は、オプション3を使用しようとしました。この場合、DIコンテナの周りにファサードラッパーを作成し、そのBuildUp()メソッドを静的アクセサを介して公開します。これにより、サービスロケータとしてのコンテナの使用が防止されます。

    [Dependency] 
    public ISomeDependency SomeDepenency { get; set; } 
    
    public Customer() 
    { 
        Ioc.BuildUp(this); 
    } 
    

    一部DIコンテナは(ので、あなたのビジネスモデルは、コンテナを参照する必要はありません)流暢な構成の代わりに属性を使用してプロパティを注入することができますが、これはDIの構成が非常に複雑にすることができます。別のオプションは、独自の属性を作成することです。

    オプション1と2は似ています。あなたは基本的にすべての責任を自分のクラスに入れ、 "エンティティ"をダムのデータコンテナに分けます。これに対してうまくいくアプローチは、Command Query Segregationを使用することです。

    public class FindCustomer : IDataQuery<Customer> 
    { 
        public string CustomerNumber { get; set; } 
    } 
    
    public class FindCustomerHandler : IQueryHandler<FindCustomer, Customer> 
    { 
        private readonly DbContext context; 
    
        public FindCustomerHandler(DbContext context) 
        { 
         if (context == null) 
          throw new ArgumentNullException("context"); 
         this.context = context; 
        } 
    
        public Customer Handle(GetCustomer query) 
        { 
         return (from customer in context.Customers 
           where customer.CustomerNumber == query.CustomerNumber 
           select new Customer 
           { 
            Id = customer.Id, 
            Name = customer.Name, 
            Addresses = customer.Addresses.Select(a => 
             new Address 
             { 
              Id = a.Id, 
              Line1 = a.Line1, 
              Line2 = a.Line2, 
              Line3 = a.Line3 
             }) 
             .OrderBy(x => x.Id) 
           }).FirstOrDefault(); 
        } 
    } 
    

    オプション1を使用して、エンドユーザがFindCustomerのインスタンスを作成し、queryProcessor.Handle(findCustomer)を呼び出すことになる(queryProcessorが注入されます)。

    オプション2を使用すると、ラッパーAPIを作成する必要があります。 fluent builderアプローチ(詳細はhere)を使用して論理的なデフォルトの依存関係を提供できますが、エンドユーザーは独自の方法を呼び出すことができます。

    var customer = new CustomerBuilder().Build(); // defaults 
    
    var customer = new CustomerBuilder(c => 
        c.WithSomeDependency(new SomeDependency()).Build(); // overridden dependency 
    

    残念ながら、これで主要な問題は、DbContextのような依存関係は特別な処理を必要とするので、オブジェクトの寿命の制御は、アップDIコンテナにもはやないことです。

    これの別の変形は、各エンティティを、他の(疎結合)APIオブジェクトを使用して独自のDIコンテナを内部的に構築する謙虚なオブジェクトにすることです。これはDIで使用するのが難しい従来のフレームワーク(Webフォームなど)に推奨されるアプローチです。

    最後に、すべてのAPIオブジェクトが依存関係を解決するために使用する静的サービスロケータが作成されています。これは最高の目標ですが、最後の手段と考えるべきものです。最大の問題は、クラスが必要とする依存関係をすばやく簡単に理解できなくなることです。したがって、依存関係がエンドユーザーに何であるかを示すドキュメントを作成(および更新)するか、エンドユーザーがソースコードを突き止めて見つけ出す必要があります。サービス・ロケータを使用できるかどうかは、ターゲット・オーディエンスと、デフォルトを超えて依存性をカスタマイズできる必要がある頻度に依存します。カスタム依存関係が青い月のものの中で1回であればうまくいくかもしれませんが、ユーザーベースの25%がカスタム依存関係を追加する必要がある場合、サービスロケータはおそらく適切なアプローチではありません。

    保守性が主な目標である場合、オプション1が明確な勝者です。しかし、この特定のAPIデザインと結婚している場合は、他のオプションの1つを選択し、そのようなAPIのサポートに必要な特別なメンテナンスを必要とします。

    参考文献:

    関連する問題