web-dev-qa-db-ja.com

データベースが適切に設計されていないリレーショナルデータベース駆動型アプリケーションでより良いOOコードを作成する方法

私はJava Webアプリケーションを主に構成しています。これは、すべてのページに複数のテーブルとそれらのテーブルに適用されるフィルターが含まれる類似したページの束で構成されています。これらのテーブルのデータはSQLから取得されますデータベース。

私はmyBatisをORMとして使用しています。これは、私のデータベースでは設計が不十分であり、mybatisがよりデータベース指向のツールであるため、私の場合には最適ではない可能性があります。

データベースの設計が不十分なため、類似するものに対して異なるクエリを作成する必要があるため、これらのクエリは非常に異なる可能性があるため、多くの重複したコードを作成していることがわかりました。つまり、クエリを簡単にパラメトリック化することはできません。これは私のコードに伝播し、単純なループでテーブルの列に行を入力する代わりに、次のようなコードを使用します。

get [〜#〜] a [〜#〜]データ(p1、...、pi);

get [〜#〜] b [〜#〜]データ(p1、...、pi);

get [〜#〜] c [〜#〜]データ(p1、...、pi);

get [〜#〜] d [〜#〜]データ(p1、...、pi); ...

そして、これは、異なる列を持つ異なるテーブルがあるとすぐに爆発します。

また、ページのhtml要素へのオブジェクトのマッピングである「ウィケット」を使用しているという事実も複雑にしています。つまり、私のJavaコードは、データベースとフロントエンドの間のアダプタになり、いくつかのロジックが混在する多くの配線、ボイラープレートコードを作成します。

正しい解決策は、ORMマッパーをdbへのより均一なインターフェイスを提供するエクストラレイヤーでラップすることですか、それとも、私が作成しているこのスパゲッティコードを処理するためのより良い方法がありますか?

編集:データベースに関する詳細情報

データベースは主に通話情報を保持しています。貧弱な設計は、次のもので構成されています。

ドメインの知識とは何の関係もない、人工的なIDを主キーとして持つテーブル。

一意、トリガー、チェック、外部キーは一切ありません。

異なるレコードの異なる概念に一致する総称名のフィールド。

条件が異なる他のテーブルと交差することによってのみ分類できるレコード。

文字列として保存される数値または日付である必要がある列。

要約すると、いたるところに乱雑で怠惰なデザイン。

19
DPM

これらのタイプのシナリオが発生するため、オブジェクト指向は特に価値があり、複雑さをカプセル化できる抽象化を合理的に設計するためのツールを提供します。

ここでの真の質問は、どこにその複雑さをカプセル化していますか?です。

それでは、少し後戻りして、ここで言及している「複雑さ」について説明します。あなたの問題(私が理解しているように、私が間違っている場合は修正してください)は、データを完了するために必要なタスクに効果的に使用できるモデルではない永続モデルです。他のタスクでは効果的で使用可能ですが、 your タスクでは使用できません。

では、手段として適切なモデルを提示しないデータがある場合はどうすればよいでしょうか。

翻訳。 /できるのはそれだけです。その翻訳は、私が上記で言及した「複雑さ」です。モデルを翻訳することに同意したので、いくつかの要因を決定する必要があります。

両方向に翻訳する必要がありますか?次のように、両方向が同じように翻訳されますか?

(Tbl A、Tbl B)-> Obj X(読み取り)

Obj X->(Tbl A、Tbl B)(書き込み)

または、挿入/更新/削除アクティビティは別のタイプのオブジェクトを表し、データをObj Xとして読み取りますが、データはObj Yから挿入/更新されますか?これらの2つの方法のどちらを使用するか、または更新/挿入/削除が不可能な場合は、 where に翻訳を入れたい重要な要素です。


どこを翻訳しますか?

この回答で私が最初に述べたところに戻ります。 OOを使用すると、複雑さをカプセル化できます。ここで私が言及するのは、あなただけでなく、 must 必要に応じてその複雑さをカプセル化するという事実です。それがリークしてすべてのコードに浸透しないようにするためです。同時に、 完全な抽象化 を使用できないことを認識することが重要です。効果的で使いやすい。

再び今;あなたの問題は:あなたはこの複雑さをどこに置くのですか?まああなたは選択肢があります。

あなたはそれを in データベースでストアドプロシージャを使用して行うことができます。これはORMでうまく機能しないことが多いという欠点がありますが、常にそうであるとは限りません。ストアドプロシージャは、パフォーマンスなど、いくつかの利点をもたらします。ただし、ストアドプロシージャは多くのメンテナンスを必要とする可能性がありますが、特定のシナリオを分析して、メンテナンスが他の選択肢よりも多いか少ないかを判断するのはあなた次第です。私は個人的にストアドプロシージャに非常に熟練しているため、このように才能があるという事実はオーバーヘッドを削減します。あなたが知っていることに基づいて決定を行うことの価値を決して過小評価しないでください do あなたやあなたのチームは、最適なソリューションよりも優れたソリューションを作成して維持する方法を知っているため、次善のソリューションが正しいソリューションよりも最適な場合があります。

別のデータベース内オプションはビューです。データベースサーバーによっては、これらが非常に最適または準最適であるか、まったく効果がない場合もあります。欠点の1つは、インデックス作成オプションに応じてクエリ時間になる可能性があります。データベースで利用できます。データの変更(挿入/更新/削除)を行う必要がない場合は、ビューがさらに適切な選択肢になります。

リポジトリパターンを使用するための古いスタンバイを使用しているデータベースを通過します。これは非常に効果的である可能性のある実績のあるアプローチです。欠点にはボイラープレートが含まれる傾向がありますが、適切にファクタリングされたリポジトリではこれをある程度回避できます。これらが不幸な量のボイラープレートになった場合でも、リポジトリはシンプルなコードであり、理解と保守が簡単で、優れたAPIを提示する傾向があります。/abstraction。また、リポジトリは、データベース内オプションを使用すると失われる単体テスト可能性に優れている場合があります。

自動マッパーのようなツールがあります ORMを使用して、ormから使用可能なモデルへのデータベースモデル間の変換を実行できる可能性がありますが、これらのツールの一部は維持が難しい場合があります/魔法のように振る舞うことを理解する。ただし、最小限のオーバーヘッドコードを作成するため、よく理解されているとメンテナンスオーバーヘッドが少なくなります。

次に、データベースからどんどん踏み出していきますこれは、翻訳されない永続化モデルを処理するコードの量が増えることを意味しますが、これは本当に不愉快なことです。これらのシナリオでは、現在実行しているように見えるUIに翻訳レイヤーを配置する方法について説明します。これは一般に非常に悪い考えであり、時間とともにひどく衰退します。


では、話を始めましょうcrazy

Objectは、存在するすべての抽象化だけではありません。コンピュータサイエンスが研究されてきた長年にわたり、それ以前にも、数多くの抽象化が開発されてきました。数学の研究から。私たちが創造的になり始めるなら、研究されてきた利用可能な既知の抽象化について話し始めましょう。

アクターモデルがあります。これは、すべての作業を他のコードに効果的に委任する他のコードにメッセージを送信するだけなので、興味深いアプローチです。すべてのコードから複雑さをカプセル化するのに効果的です。これは、俳優に「私はObj XをYに送信する必要があります」というメッセージを送信し、場所Yで応答を待っているレセプタクルがあり、Obj Xを処理している限り、機能します。指示するメッセージを送信することもできます。 「Obj Xと計算Y、Zを実行する必要があります」そして、待つ必要すらありません。翻訳はそのメッセージパスの反対側で行われ、結果の読み取りが必要ない場合は先に進むことができます。これは、あなたの目的のためにアクターモデルを少し乱用する可能性がありますが、それはすべて依存します。アクターモデルの主な目的は、非同期と同時実行を簡単かつ適切に処理することです。ただし、これらは両方とも、ここで参照する複雑さ(またはその問題のあらゆる形式の複雑さ)をカプセル化する境界として機能する抽象化にすぎません。

別のカプセル化境界はプロセス境界です。これらは、複雑さを非常に効果的に分離するために使用できます。 SOAP、RESTを使用して、または独自のプロトコルが本当に必要な場合(非推奨)、通信が単純なHTTPであるWebサービスとして変換コードを作成できます。 STOMPは、完全に新しいプロトコルではありません。または、選択したプロトコルを使用して非常に迅速に通信するために、システムローカルの公開メモリパイプを備えた通常のデーモンサービスを使用します。これには実際にはかなり良い利点があります:

  • 古いバージョンと新しいバージョンのサポートを同時に翻訳する複数のプロセスを実行して、翻訳サービスを更新してオブジェクトモデルV2を公開し、後で個別に新しいコードを使用して使用するコードを更新することができます。モデル。
  • パフォーマンスのためにプロセスをコアに固定するなどの興味深いことを行うことができます。また、このアプローチでは、そのデータにアクセスするためのセキュリティ特権で実行される唯一のプロセスにすることで、セキュリティの安全性を確保できます。
  • 固定されたままになるプロセス境界について話すと、非常に強力な境界が得られます。これは、抽象化のリークを最小限に抑え、長期にわたって抽象化のリークを最小限に抑えるためです。プロセススコープを共有しないため、契約により使用シナリオの固定セットが保証されます。
  • 非同期/非ブロック更新の機能がシンプルになりました。

欠点は、一般的に必要とされるよりも明らかにメンテナンスが多く、通信オーバーヘッドがパフォーマンスとメンテナンスに影響を与えることです。


複雑さをカプセル化するさまざまな方法があり、その複雑さをシステム内のこれまでにない奇妙で好奇心が強い場所に配置することができます。より高次の関数の形式(多くの場合、戦略パターンまたはオブジェクトパターンのさまざまな他の奇妙な形式を使用して偽造されます)を使用すると、いくつかの非常に興味深いことができます。

そうです、モナドの話を始めましょう。必要な独立した翻訳を行う小さな特定の関数の非常に独立した方法でこの翻訳レイヤーを作成できますが、これらの翻訳関数はすべて見えないように隠します外部コードからはほとんどアクセスできません。これには、外部コードに影響を与えずに簡単に変更できるようにするため、依存を減らすという利点があります。次に、ニースOOモデルタイプオブジェクトのいずれかで機能する、より高次の関数(匿名関数、ラムダ関数、戦略オブジェクト、ただしそれらを構造化する必要があります)を受け入れるクラスを作成します。次に、これらの関数を受け入れる基になるコードに、適切な変換メソッドを使用してリテラルを実行させます。

これにより、すべての変換が境界の反対側に存在するだけでなく、すべてのコードから離れた境界が作成されます。 それはその側でのみ使用されます残りのコードは、その境界のエントリポイントがどこにあるかを除いて、それについて何も知ることができません。

ええ、それは本当にクレイジーな話ですが、誰が知っていますか。あなたはただその狂気であるかもしれません(真剣に、88%未満の狂気の評価でモナドを引き受けないでください。身体に怪我をする危険があります)。

53
Jimmy Hoffa

私のおすすめ:

次のデータベースビューを作成します。

  1. 列に意味のある名前を付ける
  2. 「条件の異なる他のテーブルとの交差」を実行して、その複雑さを隠すことができます。
  3. 文字列として保存されている数値または日付を、それぞれ数値および日付に変換します。
  4. いくつかの基準に従って、存在しない一意性を作成します。

アイデアは、悪いデザインの上に、より良いデザインをエミュレートするファサードを作成することです。

次に、ORMを実際のテーブルではなくそのファサードに関連付けます。

ただし、これは挿入を単純化しません。

4

既存のデータベーススキーマを使用すると、より適切に設計されたスキーマで抽象化される可能性のあるタスクのより具体的なコードとクエリを作成する方法を確認できますが、優れたオブジェクト指向コードを作成する能力を妨げるべきではありません。

  • SOLIDの原則 を思い出してください。
  • 簡単に単体テストできるコードを記述します(これは、多くの場合、SOLIDの原則に従っています)。
  • ビジネスロジックを表示ロジックから分離します。
  • Apache Wicketのドキュメント と例をお読みください。このフレームワークは、想像以上に定型コードを節約できるため、効果的に使用する方法を学びます。
  • データベースを処理する必要のあるロジックを、ビジネスロジックが処理できるクリーンなインターフェイスを提供する別のレイヤーに保持します。このようにして、あなた(または将来のメンテナ)がスキーマを改善する機会を得た場合、ビジネスロジックにあまり多くの変更を加えることなく、スキーマを改善できます。

完璧ではないデータベーススキーマを使用していることに気づくと、仕事を難しくするすべての方法を簡単に理解できますが、ある時点で、それらの不満を脇に置いて最大限に活用する必要があります。

それは、不完全なスキーマにもかかわらず、創造性を利用して、クリーンで再利用可能で、保守が容易なコードを書く機会と考えてください。

3
Mike Partridge

より優れたオブジェクト指向コードに関する最初の質問に答えるには、 SQLを話すオブジェクト を使用することをお勧めします。 ORMはオブジェクトを操作するため、本質的にオブジェクト指向の原則に反します。OOP内のオブジェクトは、その目的を達成するためのすべてのリソースを備えた自給自足のエンティティです。アプローチはあなたのコードをより単純にすることができます。

問題の領域、つまりドメインについて言えば、私は 集合根 を識別しようとします。これらは、ドメインの整合性の境界です。常に一貫性を保つ必要のある境界。アグリゲートはドメインイベントを介して通信します。システムが十分に大きい場合、おそらく開始する必要があります サブシステムで分割する (SOA、マイクロサービス、自己完結型システムなどと呼びます)

CQRSの使用も検討します-書き込み側と読み取り側の両方を大幅に簡略化できます。このトピックに関するUdi Dahanの article を必ずお読みください。

1
Zapadlo