1

バックグラウンド

私自身の理解のために、私は単純なシナリオの階層化アーキテクチャを作成しようとしています。単純なドメインクラスCarがあります。  

public class Car {

  public string Make;
  public string Model;
  public int Year;

  public Accelerate() { }
  public Decelerate() { }
}

データベースから値を入力するCarの新しいインスタンスを作成したいと思います。  これは私が立ち往生しているところです。

永続層には、CarIDに基づいてデータベースからこれらの値を取得するという汚い作業を行う別のクラス(CarRepositoryと呼ばれる)が必要だと思います。

このCarRepository依存関係を実行時にCarドメインクラスに注入して、さまざまな実装(単体テストの偽物など)を可能にしたいと考えています。 

だから、私はこれをCarクラスに追加することを検討します:

public class Car() {

  private ICarRespository _carRepository;

  public Car(ICarRepository carRepository) {
    _carRespository = carRepository;
  }

  public void Find(int carId) {
    var car = _carRepository.FindById(carId);
    this.Make = car.Make;
    this.Model = car.Model;
    // etc.
  }

  // other properties and methods here
}

そして、ICarRepositoryを次のように定義します。

public interface ICarRepository {
  DtoCar FindById(int carId);
}

public class DtoCar {
  public string Make;
  public string Model;
  public int Year;
}

「貧乏人の依存性注入」を使用していて、プロジェクトタイプ(本番コードと単体テスト)に基づいて、集約ルートが適切な具象CarRespositoryに合格できるようにしていると仮定します。明らかに、ここではコンストラクタインジェクションを使用することを選択しました。

質問

(1)この解決策は正しくないと感じています。新しい車を作成するときは、その時点ですべてのプロパティを設定して初期化する必要があると感じています。ただし、Find()メソッドを呼び出した後でのみ、実際に有効な車になることができます。検索に有効なCarIDを渡さないとどうなりますか?そうすると、Carオブジェクトが有効なオブジェクトになることはありません。これは私を悩ませるべきではありませんか?もっと良い方法はありますか?

(2)コンストラクターインジェクションを使用してリポジトリの依存関係を渡します…CarIDを渡して、コンストラクターでここを検索することもできますか…次に、Find()メソッドを削除できますか?それで:

public Car(ICarRepository carRepository, int carId) {
  _carRespository = carRepository;
  var car = _carRepository.FindById(carId);

  this.Make = car.Make;
  this.Model = car.Model;
  // etc.
}

...こんな風になっているのを見たことがないので、使うのをためらっています。このようにしても大丈夫ですか?

(3)ここで欠けている創造的なパターンはありますか?これは単純なシナリオです…Factory、Singleton、Prototypeなどがここに当てはまるとは思いません(誤解される可能性があります)。それらは重くて専門的だと感じます。このシナリオ(データベースからの単純なドメインオブジェクト)は非常に基本的なように思われるため、何らかのガイダンスが必要です。

4

2 に答える 2

4

あなたの問題は、あなたが単一責任の原則に従わないことです。あなたはこれに特化したサービスに車の作成をオフロードする必要があります。

class CarService
{
    CarService(ICarRepository carRepo)
    { 
        _carRepo = carRepo;
    }

    Car GetById(int id)
    {
        var carDTO = _carRepo.GetById(id);       
        var car = Mapper.Map<CarDTO, Car>(carDTO);
        return car;
    }
}

*こちらがAutoMapperへのリンクです

**必要はありませんが、CarDTO必要に応じて抽象化レイヤーが追加されます。

これはファクトリのように機能しますが、実装はより理にかなっており、恐れていたほど重くはありません。コードは次のようになります。

var car = carService.GetById(id);

これですべての質問が解決するはずです。

于 2013-02-27T18:47:38.397 に答える
3

クラスの設計は、単一責任の原則Carに違反しています。つまり、そのドメインエンティティであり、データ永続化ロジックが含まれています。

Carそれで、ただのドメインエンティティになりましょう(IDプロパティが導入されました):

public class Car
{
    public Car(int id, string make, string model, int year)
    {
        ID = id;
        Make = make;
        Model = model;
        Year = year;
    }

    public int ID { get; private set; }
    public string Make { get; private set; }
    public string Model { get; private set; }
    public int Year { get; private set; }

    public void Accelerate() { }
    public void Decelerate() { }
}

リポジトリインターフェイスは、データ転送オブジェクトを返さないようにする必要があります。

public interface ICarRepository
{
    Car FindById(int carId); // No DTO here - DTO is the detail of implementation
}

次に、Carサービスのインターフェースを設計するだけです。サービス層はビジネスロジックを公開します。この単純なケースでは、サービスレイヤーは何の価値も追加しません。

public interface ICarService
{
    Car FindById(int carId);
}

public class CarService : ICarService
{
    private readonly ICarRepository _repository;

    public CarService(ICarRepository repository)
    {
        _repository = repository;
    }

    #region Implementation of ICarService

    public Car FindById(int carId)
    {
        var car = _repository.FindById(carId);
        // The additional business logic (if required) could be added here.
        return car;
    }

    #endregion
}

リポジトリの実際の実装は、Carそのコンストラクタを使用して作成します。

于 2013-02-27T18:57:24.403 に答える