web-dev-qa-db-ja.com

クリーンアーキテクチャのリポジトリパターンは、依存関係の逆転の原則に違反していませんか?

私が読んだものから見て、クリーンなアーキテクチャは、永続性について何も知らないエンティティがいくつかあると想定していますが、それらが存在するのと同じ層に、エンティティの読み取り/更新方法に関する規約となることを目的としたインターフェイスがあるかもしれません/ deleted。

// project Core
public class CarModel
{
    public string Name { get; set; }

    public void SomeImportantBehavior {}
}

public interface ICarModelRepository 
{
    CarModel FindByName(string name);
    CarModel Add(CarModel carModel);
}

その間、別のレイヤーはインターフェースの実装を保持します:

// project Infrastructure or Persistence
public class CarModelRepository : ICarModelRepository
{
    public CarModel FindByName(string name)
    {
        return new CarModel { Name = name }; // the implementation is not really important here
    }

    public CarModel Add(CarModel carModel) {
        // somehow persist it here
        return carModel;
    }
}

ですから、質問があります。リポジトリの実装はDiPの原則に違反していないのですか?それは抽象化だけでなく、具体的な実装(この場合はCarModel)にも依存するので?

この別の例は here です。

追伸CarModelは動作を伴うドメインモデルです

5
arthur.borisow

DIPの中心的な考え方は、上位レベルのモジュールと下位レベルのモジュール間の直接的な依存関係を排除することです。これを実現するために、抽象的で安定したインターフェースがそれらの間に配置され、代わりに両方の層が依存します。

リポジトリー・パターンでは、インターフェースは通常、リポジトリーが担当するエンティティーを参照します。したがって、低レベルの実装(CarModelRepository)が高レベルのモジュールエンティティ(CarModel)に依存するようになります。

具体的なCarModelICarModelインターフェースに置き換えても、この問題は実際には解決しません。結局のところ、リポジトリを通じて取得するCarModelインスタンスは、さまざまなビジネスルールを実装しているため、ビジネスレイヤーに存在する必要があります。

ビジネス層とリポジトリ実装の間の分離を増やすことができます。たとえば、CarModelFactory(ビジネスレイヤーで実装)を実装するICarModelFactory(共有インターフェースの一部)をリポジトリに提供できます。または、リポジトリで実際のCarDataエンティティではなくCarModel値オブジェクトを処理し、ビジネスレイヤーでエンティティ自体をインスタンス化することもできます。

ほとんどの場合、これは実りがありません。 CarModel自体よりも安定している2つのモジュール間の抽象的なインターフェイスを見つけようとしても、ほとんどの場合無駄です。これは、基本的に、CarModelRepositoryが汎用ユーティリティクラスになることを意図していないためです。それは本質的に、特定のドメインモデルをデータベースに接着することです。

結果として、それをドメインモデル(またはデータベース)から分離しようとしても、実際に役立つものは何もありません。つまり、リポジトリはドメインモデルとデータベースドライバの間のアダプタと見なすことができるため、両方を認識する必要があります。

DIPを導入するときのMartinの主な焦点は、リポジトリパターンが実現する、高レベルモジュールを低レベルモジュールから分離することでした。

5
doubleYou

番号。

リポジトリを導入する前にコードを検討してください:

public class MyView
{
    public void button_onClick()
    {
        var sql = $"select form car where id = {this.txt_id}"
        //do sql reading stuff
        this.Car = new Car();
        this.Car.Id = dr["id"].ToString();
    }
}

以降

public class MyView
{
    private IRepository repo;
    public void button_onClick()
    {
        this.Car = this.repo.GetCar(this.txt_id);
    }
}

MyViewクラスにDiPを適用しました。

これをさらに進めて、リポジトリに適用することもできます。おそらく、複数のタイプの車がある場合、Carは抽象クラスです。

public CarRepo : IRepository
{
    public ICarFactory carFac

    public Car GetCar(string id)
    {
        //do sql stuff
        var car = carFac.Create(dr["carType"].ToString(), dr)
        return car
    }
}

いずれの場合も、問題のクラスから何らかの責任を取り除き、代わりにそれをインターフェイスへの依存関係として挿入します。

下位クラスの特定の実装に依存するそのクラスの代わりに、この場合はSQLロジック。低レベルクラスは、高レベルオブジェクトのニーズに応じて定義されるインターフェイスに依存しています。

Carを高レベルのオブジェクトと考える場合は、ここで原則を適用することもできます。明らかに、リポジトリオブジェクトを注入するポイントはほとんどありません。しかし、たとえば

public class Car
{
    private IPriceCalculator calc;
    public decimal Price()
    {
        //comment out old code!
        //return (new MyCalculator).CalcPrice(this.colour);
        return this.calc.CalcPrice(this.colour);
    }
}

これで、価格計算ロジックへの依存を逆にしました

1
Ewan