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


12

リポジトリにORMを使用している場合、データベースからすでに十分に抽象化されていると思います。

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

多くのORMで機能するレイヤーを作成するのは本当に必要なのでしょうか、それとも単純に頭がかかりますか?

編集

より詳細に説明するために:

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

3
追加のレイヤーには正当化が必要です。そうでない場合、YAGNIに違反します。言い換えれば、あなたがそれを必要としいると信じているは、それを証明する負担を負っています
gnat

2
操作の必要なサブセットのみを公開するドメインレイヤーが必要であることを理解できます-ORMは表面領域が少し広すぎる傾向があります(たとえば、他の包含エンティティによって指示されていないエンティティへの更新を許可したくない場合)。このような抽象化層があると、これに役立ちます。
Oded

4
最初のレイヤーも変更したい場合に備えて、おそらくORMの上の抽象化の最初のレイヤーの抽象化の2番目のレイヤーが必要になります。
デビッドピーターマン

1
@David冗長性を追加している間に、すべてのif(boolean)をif(boolean == true)に変更し、さらに同じものを逆流したい場合はif(boolean == true == true ...)など
ブライアン

回答:


12

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

社内のフレームワークはほとんど常に災害です。将来のニーズを見越して構築することは、ほぼ確実な災害です。成功したフレームワークは成功したプロジェクトから抽出されたものであり、想像上のニーズを満たすために事前に構築されたものではありません。


12

ORMは、データレイヤーをRDBMSから独立させるための抽象化を提供しますが、ビジネスレイヤーをデータレイヤーから「アンティ」するには不十分な場合があります。具体的には、RDBMSテーブルにマップするオブジェクトがビジネスレイヤーに直接「リーク」することを許可しないでください。

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

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

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


.NETがクエリオブジェクトをプラットフォームにベイクすることでこの問題を解決できたことを嬉しく思います。Hibernateの.NETポートでもサポートされています。
マイケルブラウン

2
@MikeBrown Yeah、および.NETも、LINQテクノロジーを使用して、独自の2つの競合するORMテクノロジーを提供しました!
-dasblinkenlight

@dasblinkenlight質問を更新して、追加情報を提供しました。
パトリックデジャルダン

最近、私はビジネス層がマップのようなインターフェースに基づいたデータ層に依存し、状態を保存するというアプローチを採用しました。私は現在、これらのインターフェースが状態維持の懸念を十分に表し(プログラミングの機能的なスタイルに触発された)、かなり薄い実装を介して選択したORMからうまく分離できると信じています。
ベルチン

2

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

ところで、O / RMを切り替えるだけでなく、単体テストを考えてください。UnitOfWorkの実装は3つあります。1つはEF、1つはNH(実際には、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を使用して結果をさらにフィルター、ソート、グループ化、さらには投影することができ、それでもサーバー側で行われているすべての利点を得ることができます。


1
しかし、ここでの質問は、上の層が有用であり、すべてのデータアクセスのゲートキーパーである必要があるかどうかを判断することです。
ブライアン

作業単位/リポジトリの実装に関する私のブログ投稿を指すことができればいいのですが。OPからの正確な懸念について説明します。
マイケルブラウン

レイヤーに名前を付けたからといって、それが必要だとか、便利だとかいうわけではありません。
ケビンクライン

OPによると、彼はデータアクセスとビジネスレイヤーの間に余分なマッピングを持っています。私にとって、私のビジネスオブジェクトとエンティティオブジェクトは同じです。EFとNHは驚くべきマッピングAPIを提供しているため、データマッピングが問題になることはほとんどありません。
マイケルブラウン

任意の式を効率的なORM呼び出しにどのように変換しますか?すべてを取得して、フィルターに一致しない行を破棄することはできません。
ケビンクライン
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.