web-dev-qa-db-ja.com

ORMレイヤーの上に抽象レイヤーを作成する

私はあなたのリポジトリにORMを使用している場合、それはすでにデータベースから十分に抽象化されていると信じています。

ただし、私が現在作業している場合、後でORMを変更する場合に備えて、ORMを抽象化するレイヤーが必要だと誰かが信じています。

多くのORMで機能するレイヤーを作成することは本当に必要ですか、それとも単純に多くのオーバーヘッドがありますか?

編集する

詳細を説明するために:

  1. AutoMapperでマップされたPOCOクラスとエンティティクラスがあります。エンティティークラスは、リポジトリレイヤーによって使用されます。リポジトリレイヤーは、追加の抽象化レイヤーを使用してEntity Frameworkと通信します。
  2. ビジネス層は、Entity Frameworkに直接アクセスすることはできません。 ORMに対する追加の抽象化レイヤーがなくても、これはリポジトリレイヤーを使用するサービスレイヤーを使用する必要があります。どちらの場合も、ビジネス層はORMから完全に分離されています。
  3. 主な議論は、将来ORMを変更できるようにすることです。それは本当にリポジトリ層の内部にローカライズされているので、私にはすでに十分に分離されており、「高品質」のコードを作成するために追加の抽象化層が必要な理由はわかりません。
12

その方法は狂気です。 ORMを変更する必要が生じることはほとんどありません。また、ORMを変更することを決定した場合、マッピングを書き換えるコストは、独自のメタORMを開発および維持するためのコストのほんの一部です。 ORMを切り替えるために必要な作業の95%を実行するスクリプトをいくつか記述できると思います。

社内のフレームワークはほとんど常に災害です。将来のニーズを見越して1つを構築することは、ほぼ保証された災害です。成功するフレームワークは、架空のニーズを満たすために事前に構築されたのではなく、成功したプロジェクトから抽出されます。

12
kevin cline

ORMは、データレイヤーをRDBMSから独立させるための抽象化を提供しますが、ビジネスレイヤーをデータレイヤーから「切り離す」には不十分な場合があります。具体的には、RDBMSテーブルにマップするオブジェクトがビジネスレイヤーに直接「リーク」しないようにする必要があります。

少なくとも、ビジネスレイヤーは、データレイヤーのORM管理のテーブルマップオブジェクトが実装する可能性のあるインターフェイスにプログラムする必要があります。さらに、ORMのネイティブクエリ機能を非表示にするために、抽象クエリ構築のインターフェイスベースのレイヤーを作成する必要がある場合があります。主な目標は、特定のORMがデータレイヤーを超えてソリューションに組み込まれるのを回避することです。たとえば、ビジネスレイヤーにHQL(Hibernate Query Language)文字列を作成するのは魅力的です。ただし、この一見無害な決定では、ビジネスレイヤーをHibernateに結び付け、ビジネスレイヤーとデータアクセスレイヤーを一緒に釘付けにします。この状況をできるだけ回避する必要があります。

EDIT:この場合、リポジトリ内の追加のレイヤーは時間の無駄です。ポイント2に基づいて、ビジネスレイヤーはリポジトリから十分に隔離されています。追加の断熱材を提供すると、不必要な複雑さが生じ、追加の利点はありません。

リポジトリ内に追加の抽象化レイヤーを構築することの問題は、ORMの特定の「ブランド」が、それとの対話方法を決定することです。 ORMのように見えるが、自分の管理下にある薄いラッパーを構築する場合、基になるORMを置き換えるのは、追加のレイヤーがない場合と同じくらい困難です。一方、ORMのように見えないレイヤーを作成する場合は、オブジェクトリレーショナルマッピングテクノロジーの選択に疑問を投げかける必要があります。

12
dasblinkenlight

UnitOfWorkは通常、この抽象化を提供します。これは変更が必要な場所の1つであり、リポジトリはインターフェースを介してそれに依存します。 O/RMを変更する必要がある場合は、その上に新しいUoWを実装してください。 1つ完了しました。

ところで、それは単にO/RMを切り替えるだけではありません。単体テストについて考えてみてください。私は3つのUnitOfWork実装を持っています。1つはEF用、もう1つはNH用です(実際にはDID Oracleサポートを必要とするクライアントのプロジェクトの途中でO/RMを切り替える必要があるため)、および1つはInMemory永続性のためです。 。データベースを背後に配置する準備が整う前に、InMemory永続性は単体テストやラピッドプロトタイピングにも最適でした。

フレームワークは実装が簡単です。まず、一般的なIRepositoryインターフェイスがあります

public interface IRepository<T>
  where T:class
{
  IQueryable<T> FindBy(Expression<Func<T,Bool>>filter);
  IQueryable<T> GetAll();
  T FindSingle(Expression<Func<T,Bool>> filter);
  void Add(T item);
  void Remove(T item);

}

そして、IUnitOfWorkインターフェース

public interface IUnitOfWork
{
   IQueryable<T> GetSet<T>();
   void Save();
   void Add<T>(T item) where T:class;
   void Remove<T>(T item) where T:class;
}

次はベースリポジトリです(抽象にするかどうかの選択)

public abstract class RepositoryBase<T>:IRepository<T>
  where T:class
{
   protected readonly IUnitOfWork _uow;

   protected RepositoryBase(IUnitOfWork uow)
   { 
      _uow=uow;
   }

   public IQueryable<T> FindBy(Expression<Func<T,Bool>>filter)
   {
      return _uow.GetSet<T>().Where(filter);
   }

   public IQueryable<T> GetAll()
   {
      return _uow.GetSet<T>();
   }

   public T FindSingle(Expression<Func<T,Bool>> filter)
   {
      return _uow.GetSet<T>().SingleOrDefault(filter);
   }

   public void Add(T item)
   {
      return _uow.Add(item);
   }

   public void Remove(T item)
   {
      return _uow.Remove(item);
   }
}

IUnitOfWorkの実装は、EF、NH、およびIn Memoryの子供の遊びです。私がIQueryableを返す理由は同じ理由です、とAyendeは彼の投稿で述べました、クライアントはLINQを使用して結果をさらにフィルタリング、並べ替え、グループ化、さらには投影することができ、サーバーサイドで行われるすべての利点を引き続き利用できます。

2
Michael Brown