背景
新しいコンポーネントを作成しているときに、ストレージレイヤーのSQL/NOSQLデータベース(MongoとMysql)を決定する途中です。今日の時点で、mysqlは私のユースケース(6〜7のドメインエンティティ、互いに密接に関連している)に完全に適合しているようです。それでも、将来的にnosql(mongo)に切り替えるのに十分なだけ抽象的にデータレイヤーとの統合を維持したいと考えています。
この抽象的なデータアクセスレイヤーを構築しようとしている間、RDBMSの提供に妥協しているように感じます(NOSQLは最初のクラスコンストラクトとして結合をサポートしていないため、結合およびその他の主要なRDBMS機能をこの抽象化。)
質問:
そもそもそのようなレベルの抽象化を構築しようとするのは行き過ぎですか? RDBMS製品に妥協することなく、このようなレベルの抽象化を構築することは可能ですか?可能であれば、推奨パターンは何ですか?
データベースから適切に切り離されたままでありながら、その機能を自由に使用できることを保証する最善の方法は、データベースの抽象化レイヤーを作成しないにすることです。 (まあ、明示的な要件nowがない限り、複数のデータベースをサポートする必要があります。それ以外の場合はYAGNIです。)
最悪のことは、「データベースにとらわれない」状態を維持しようとすることです。これにより、ほぼ自動的に、いくつかの「共通の特徴」タイプのインターフェースが得られます。通常は、些細なCRUD操作です。次に、ストレージバックエンドの特定の機能を使用できないか(完全に異なるパラダイムに言及することすらなく、DBが今日持っている素晴らしい機能を考えると愚かです)、または特定の機能またはクエリに常に新しいメソッドを導入する必要があります。さらに悪いことに、この抽象化を「爆発」させたくないので、新しい要件のためにメソッドを再利用することを余儀なくされ、それは不適切で苦痛になります。
別の方法はmodel your domainで、データベース固有の実装を提供します。私が遭遇した1つの例:顧客(銀行ドメイン)のすべてのクレジットカードを凍結する必要がありました。これは当初、複数の接続されたエンティティ(通常の1-1/1-n関係を持つデータオブジェクト)を持つORMで実装されました。アカウント、次にカードのクエリを発行し、カードにフラグを設定して、ORMに永続化を処理させる必要がありました。
これらすべての代わりに、メソッドCustomer.freezeCreditCards()
を導入しました。このメソッドは、データベースに直接「更新」ステートメントを発行しました。これは特にエキサイティングな操作ではありませんが、ビジネスメソッドを持っている場合ビジネスメソッドが意味のある場所(データの場所)にある場合、必要な最適化や追加機能を使用するのは簡単です。また、機能を抽象化/一般化する必要はありません。
なぜそんなに抽象化したいのですか?
異なるストレージテクノロジーが存在するまさにその理由は、それぞれが一般化することができない長所と短所のマトリックスを持っているためです-業界の最高のマインドや大企業の経済的リソースによってさえも。
特定のアプリケーションでストレージテクノロジーが十分であると思われる場合は、ストレージテクノロジーを1つ選択して実行します。
すべての制限となしの特別な利点を考慮して、一度に多くのストレージテクノロジのコードを設計および記述した場合、このプロセスの結論は、低速で効果的な機能がほとんどないアプリケーションになる可能性があります。
あるいは、少なくとも、設計段階が非常に遅く、実装が必要以上に長く複雑で、検証が容易ではなく、それ以上のメンテナンスと変更が非常に困難なアプリケーションになるでしょう。
十分に統合された1つの保存方法のコードを設計して作成するだけで十分な場合があります。保存方法を後で変更する必要がある場合は、別のアプリケーションを作成できます(元のアプリケーションを補足または置き換えるため)。
コードに柔軟性を持たせるための最善の方法は、コードを柔軟にすることです。
多くの人がDBを念頭に置いてコーディングする際に犯す間違いは、DBのマニュアルを詳しく調べて、DBが提供するすごい小さな機能をすべて学び、それらを使用することです。すぐにthat dbを使用する必要があります。
これは、CPUの場合とまったく同じ問題です。すべてのCPUには一連のオペコードがあります。いくつかはベーシックで人気があります。いくつかは気の利いた、異なるです。コードが正確にそのCPUでのみ動作するようになったため、問題が発生する原因はさまざまです。
これが理由の1つですJavaはJVMを持っています。これは基本的で人気のあるopコードのみを提供し、多くの異なるCPUでそれらを実行する方法を理解します。さらに重要なのは、ふわふわと違うものをさりげなく。
データベースにとらわれないようにしたい場合は、データベースでも同じことができます。アプリケーションのニーズに合わせて、データベース機能ではなく、DB抽象化の設計を推進しましょう。そして、コードが柔軟であることを保証する最善の方法はそれを柔軟にすることですので、不可知論と言う前に少なくとも2つのデータベースで動作するようにして抽象化をテストすることをお勧めします。
各データベースに個別のアダプターを提供できますが、使用しているDBに関する詳しい知識はそこで停止する必要があります。どちらのアダプターも、使用しているDBを非表示にするDB抽象化の下で機能する必要があります。
多くの作業のように聞こえるかもしれませんが、DBが行う可能性のあるすべてのことを引き受ける必要はありません。あなたはあなたのアプリが実際に必要とするものを引き受けなければなりません。 DBがなくても問題が解決しない場合もあります。
まず、アプリケーションが何をする必要があるかを検討します。要件に複数のソースからのデータの結合が含まれ、これがオプションの機能ではない場合、選択したテクノロジーに関係なく、何らかの方法でそのようなロジックを実装する必要があります。特に、これは特定のデータベースがあなたの仕事にあまり適していないことを意味するかもしれません、そしてそれについてあなたができることは多くありません。すべてのタスクに最適な単一のデータベースがあったとしても、市場には数十の異なるデータベースが存在しないでしょう。
必要なロジックの種類がわかったら、アプリケーション内にデータアクセスAPIを作成します。ドメインオブジェクトで動作するはずです。ドメインオブジェクトは、非常によく似ていても、データベースに格納するオブジェクトとは別のものでなければなりません。したがって、APIは、たとえば、Javaインターフェイス:
interface CustomerRepository {
Customer findCustomer(String id);
}
これで、実際のデータベースコードは、このインターフェイスを実装するクラスにあるはずです。次に例を示します。
class MySqlCustomerRepository implements CustomerRepository {
Customer findCustomer(String id) {
//do stuff using MySql and some DAO objects you will use with it
}
}
後で別のデータベースを使用することにした場合は、実装を次のように変更できます。
class MongoDbCustomerRepository implements CustomerRepository {
Customer findCustomer(String id) {
//do stuff using MongoDB and some DAO objects you will use with it
}
}
特定のデータベースで使用されるDAOオブジェクトと同様に、内部のコードはおそらく完全に異なります。たとえば、ユーザーの名前や住所などのデータを正規化された方法で別々のDBテーブルに格納し、MySqlを使用する場合はそれらのテーブル間の結合を使用してデータを結合し、非正規化データを使用して名前とともにアドレスを格納することができます。 MongoDBを使用する場合のその他のフィールド。同様に、リポジトリ内で使用されるDAOは異なります。ただし、最終的に返すドメインオブジェクトCustomerは同じになるため、CustomerRepository
を使用するアプリケーションコードは、使用する実装を意識する必要がありません。
これが抽象化の力です。一部のテストでは、実際のリポジトリを使用する代わりにリポジトリをモックすることができるため、アプリケーションのテストも簡単になります。
ただし、データベース実装の変更はそれほど簡単ではない場合があることに注意してください。可能です。私はそのようなことを自分で行いましたが、それは重大な決定であり、アクセスパターン、使用するクエリの種類、パフォーマンス要件などによっては、選択がかなり制限される場合があります(たとえば、異なるSQLデータベース間でのみ)または異なるKVストアDB間)。それでも、リポジトリの抽象化をお勧めします。これは、懸念事項(アプリビジネスロジックとデータストレージ)を明確に分離し、テストを容易にするためです。これにより、下のDB実装を変更しない場合でも、どちらも非常に役立ちます。