ドメイン駆動型設計はアンチSQLパターンですか?


44

私はドメイン駆動設計(DDD)に飛び込んでいますが、さらに深く掘り下げていくと、得られないことがいくつかあります。私が理解しているように、主なポイントは、ドメインロジック(ビジネスロジック)をインフラストラクチャ(DB、ファイルシステムなど)から分離することです。

私が疑問に思っているのは、マテリアルリソース計算クエリのような非常に複雑なクエリがある場合、どうなりますか?この種のクエリでは、SQLが設計された種類の重いセット操作を操作します。ドメインレイヤー内でこれらの計算を行い、その中の多くのセットを操作することは、SQLテクノロジーを破棄するようなものです。

DDDパターンでは、ドメインレイヤーを変更せずにMongoDBにSQL Serverなどの同じ機能がないことを認識せずにインフラストラクチャを変更できるため、インフラストラクチャでこれらの計算を行うこともできません。

それはDDDパターンの落とし穴ですか?


34
SQLはリレーショナルセット代数を処理するように設計されていますが、ビジネスロジックの半分がリファクタリングやテストがさらに困難な少数のSQL関数に埋もれていることに気付くのは楽しい日ではありません。そのため、これをドメインレイヤーに移動して、友人と遊ぶことができるのは魅力的です。これはSQLテクノロジーのかなりの部分を捨てているのでしょうか?もちろん、SELECT / JOINのみを使用している場合、SQLは管理がはるかに簡単です。
Jared Goguen

30
@JaredGoguenしかし、これはあなたがSQLの専門家ではなく、技術のせいではない可能性があります
Leonardo Mangano

2
@JimmyJamesは、DDDが適切に実装されていれば、SQL ServerからMongoDBへの切り替えなど、最小限の労力でレイヤーを変更できることを伝えようとしました。しかし、SQLに複雑なクエリがある場合、技術的な違いのためにMongoDBに切り替えることができない可能性があります。当たり前のことを言ったと思う。
レオナルドマンガノ

7
... is like throwing away the SQL technology特定の技術何かを行えるからといって、それが最良の選択であるとは限りません。これは逸話的な証拠ですが、データベースにビジネスロジックを保存するために使用し、長期にわたる保守性の問題が原因でデータベースから移行している企業が非常に多くあります。単純化しすぎていますが、データベースはデータを保存するためのものであり、プログラミング言語はデータを変換するためのものです。アプリケーションを使用してデータを直接保存しようとするよりも、ビジネスロジックにDBを使用したくないでしょう。
コナーマンコーネ

8
SQL自体はDDDの優れた例です。関連データの整理に直面したとき、人々は最初にそれを行う言語を指定しました:SQL。実装はそれほど重要ではありません。DB管理者は、データベースを照会するためにC / C ++を知る必要はありません。同様に、イベントのスケジューリングのタスクに直面したとき、誰かがCRON構文(mhdmw)を思い付きました。これは、スケジューリングの問題の99%に適合する単純なドメインモデルです。DDDの中核は、クラスやテーブルなどを作成することではありません。問題を理解し、問題の領域で機能するシステムを
考え出すことです

回答:


39

最近では、読み取り(クエリ)が書き込み(コマンド)とは異なる方法で処理される場合があります。複雑なクエリを使用するシステムでは、クエリ自体がドメインモデルを通過することはほとんどありません(主に書き込みの一貫性を維持する責任があります)。

SQLであるSQLにレンダリングする必要があることは絶対に正しいことです。そのため、読み取りを中心に最適化されたデータモデルを設計し、そのデータモデルのクエリは通常、ドメインモデルを含まないコードパスを使用します(一部の入力検証の可能性を除き、クエリ内のパラメーターを確認します)合理的です)。


13
+1良い答えですが、この概念に適切な名前、Command-Query Segregationを付ける必要があります。
マイクはモニカを

6
@Mike読み取りと書き込みでまったく異なるモデルを持つことは、CQSではなくCQRSに似ています。
アンディ

3
「読み取りモデル」はドメインモデル(またはその一部)ではありませんか?私はCQRSの専門家ではありませんが、コマンドモデルは従来のドメインモデルとはまったく異なるが、読み取りモデルはそうではないと常に思っていました。それで、あなたはこれの例を与えることができますか?
Doc Brown

High Performance Markがタイプミスに注意を促していることに気付くまでに時間がかかりすぎました。
VoiceOfUnreason

@DocBrownは-ここであなたを明確にする私の試みである- > cascadefaliure.vocumsineratio.com/2019/04/...
VoiceOfUnreason

21

私が理解しているように、主なポイントは、ドメインロジック(ビジネスロジック)をインフラストラクチャ(DB、ファイルシステムなど)から分離することです。

これは誤解の基礎です。DDDの目的は、「これはSQLサーバーにあるためBLであってはならない」のような難しい線に沿って物事を分離することではなく、DDDの目的はドメインを分離し、間に障壁を作成することですドメインの内部を別のドメインの内部から完全に分離し、それらの間で共有外部を定義できるようにします。

「SQLであること」をBL / DLの障壁と考えないでください。それはそうではありません。代わりに、「これは内部ドメインの終わり」を障壁と考えてください。

各ドメインには、他のすべてドメインと連携できる外部向けAPIが必要です。データストレージレイヤーの場合、格納するデータオブジェクトの読み取り/書き込み(CRUD)アクションが必要です。これは、SQL自体が実際には障壁ではなくVIEWPROCEDUREコンポーネントが障壁であることを意味します。テーブルから直接読み取るべきではありません。それは、DDDが外部の消費者として心配するべきではないことを伝える実装の詳細です。

あなたの例を考えてみましょう:

私が疑問に思っているのは、マテリアルリソース計算クエリのような非常に複雑なクエリがある場合、どうなりますか?この種のクエリでは、SQLが設計された種類の重いセット操作を操作します。

これはまさにSQLにあるべきことであり、DDDの違反ではありません。それが私たちがDDDを作った理由です。SQLでのその計算では、それはBL / DLの一部になります。あなたがすることは、別のビュー/ストアドプロシージャ/ what-have-youを使用し、それが外部APIであるため、ビジネスロジックをデータ層から分離したままにすることです。実際、データレイヤーは別のDDDドメインレイヤーである必要があります。データレイヤーには、他のドメインレイヤーと連携するための独自の抽象化があります。

DDDパターンでは、ドメインレイヤーを変更せずにMongoDBにSQL Serverなどの同じ機能がないことを認識せずにインフラストラクチャを変更できるため、インフラストラクチャでこれらの計算を行うこともできません。

それは別の誤解です:実装の詳細は、他のドメインレイヤーを変更せずに内部的に変更できると言います。インフラストラクチャの一部だけを置き換えることができるというわけではありません。

繰り返しますが、DDDは明確に定義された外部APIで内部を隠すことです。これらのAPIが置かれている場所はまったく異なる質問であり、DDDはそれを定義していません。これらのAPIが存在することを単に定義し、決して変更しないでください

DDDは、MSSQLをMongoDBでアドホックに置き換えることができるようにセットアップされていません。これらは、まったく異なる2つのインフラストラクチャコンポーネントです。

代わりに、DDDが定義するものの類推を使用しましょう:ガスと電気自動車。両方の車両には、推進を作成するための2つの完全に異なる方法がありますが、同じAPIがあります:オン/オフ、スロットル/ブレーキ、および車両を推進するホイールです。DDDは、車のエンジン(ガスまたは電気)を交換できるはずだと言っています。車をオートバイに置き換えることができるとは言いませんが、それは事実上MSSQL→MongoDBです。


1
説明ありがとう。私にとって非常に難しいトピックであり、誰もが異なる視点を持っています。私が同意しない唯一のことは、MSSQL(car)とMongoDB(motorcycle)の比較です。私にとって正しい比較は、これらは同じ車の2つの異なるエンジンですが、それは単なる意見です。
レオナルドマンガノ

8
@LeonardoManganoあ、でもそうではない。MSSQLはリレーショナルデータベースであり、MongoDBはドキュメントデータベースです。はい、「データベース」は両方を説明していますが、それはそれに関する限りです。読み取り/書き込みの手法はまったく異なります。代わりにMongoDBの、あなたは、代替として、PostgreやMySQLを使用することができ、そしてそれは有効な比較になります。
410_

3
「テーブルから直接読むべきではない...」狂気。
jpmc26

「テーブルから直接読むべきではありません...」これは、データベースと連動するソフトウェアを10年間書いた後、独自に実装するようになったルールです。人気のデザインパターン。
ルシファーサム

@LuciferSamアイ 実装の詳細とドメインの境界の分離を管理するのがはるかに簡単になります。ドメイン内の1つの「オブジェクト」は5つのテーブルで表される場合があるため、ビューを使用してそのオブジェクトをカプセル化します。
410_

18

アプリケーションをホストする組織がデータベースレイヤーライセンスが高すぎると判断したプロジェクトに参加したことがある場合は、データベース/データストレージを簡単に移行できることに感謝します。すべてのことを考慮しますが、これは起こりますが、頻繁には起こりません

いわば、両方の長所を最大限に活用できます。データベースで複雑な機能を実行することを最適化することを検討する場合、インターフェイスを使用して、計算の代替実装を注入できます。問題は、複数の場所でロジックを維持する必要があることです。

建築パターンから逸脱する

パターンを純粋に実装すること、または特定の領域で逸脱することと対立することに気づいたら、決断する必要があります。パターンは、プロジェクトを整理するのに役立つことを行うテンプレート化された方法です。この時点で、次の評価に時間がかかります。

  • これは正しいパターンですか?(何度もありますが、時にはそれがちょうど悪いフィットです)
  • この1つの方法で逸脱する必要がありますか?
  • 私はこれまでどのくらい逸脱しましたか?

一部のアーキテクチャパターンは、アプリケーションの80〜90%に適していますが、残りのビットにはあまり適していません。規定のパターンからの時折の逸脱は、パフォーマンスまたはロジスティックの理由で役立ちます。

ただし、累積偏差がアプリケーションアーキテクチャの20%以上に相当する場合は、おそらく適切ではありません。

アーキテクチャを使い続けることを選択した場合は、自分自身に賛成して、規定された方法から逸脱した場所と理由を文書化してください。チームに新しい熱心なメンバーを獲得したら、パフォーマンスの測定値と正当化を含むそのドキュメントを指すことができます。これにより、「問題」を修正するためにリクエストが繰り返される可能性が低くなります。その文書化はまた、横行する逸脱の抑止力にもなります。


私は答えに「これは正しいパターンですか」のようなフレーズの使用を避けます。質問を書くときに人々を具体的にするのは十分に困難であり、あなた自身の承認によって「時には適合しない」ことがあり、それはいいえ、それは正しいパターンではないことを示唆しています。
ロバートハーヴェイ

@RobertHarvey、使用しているパターンがアプリケーションに適していないプロジェクトに参加していたため、特定の品質メトリックが失敗しました。確かにそれは当たり前のことではありませんが、そうなったときは、アーキテクチャを変更するか、コードをアプリに組み込む必要があります。不適切な適合をより早く判断できるほど、修正が容易になります。だからこそ、私は常にエッジケースを評価する際にその考えを取り入れています。最後の箇条書きに加えて、偏差の累積が見られるまで、フィットがどれほど悪いか気付かないことがあります。
ベリンロリッチ

7

SQLが得意とする集合操作ロジックは、DDDと問題なく統合できます。

たとえば、集計値、種類ごとの製品の合計数を知る必要があるとします。SQLで簡単に実行できますが、すべての製品をメモリにロードし、それらをすべて加算すると遅くなります。

新しいドメインオブジェクトを紹介するだけです。

ProductInventory
{
    ProductType
    TotalCount
    DateTimeTaken
}

と私のリポジトリ上のメソッド

ProductRepository
{
    List<ProductInventory> TakeInventory(DateTime asOfDate) {...}
}

確かに、私は今、特定の能力を持つDBに依存しています。しかし、私はまだ技術的に分離されており、ロジックが単純である限り、それは「ビジネスロジック」ではないと主張することができます


さて、今のところ思い出します。リポジトリもQueryパラメータとして取得することになっています。repository.find(query);。私は同じことを読みましたがSpecs. That opens a door to leave 、抽象化としてのQuery` QueryImplまたはインフラストラクチャレイヤーへの特定のクエリの実装です。
ライヴ

5
ああ、神様、私はそれをする人もいますが、それはひどいことだと思います。この種のことは、その道の一歩とみなすことができます。しかし、私はそれを慎重にとることができると思います。
ユアン

I know some people do that一部の人々はPivotalとそのフレームワークです。SpringFrameworkにはこれがたくさんあります:-)。とにかく、@ VoiceOfUnreasonが示唆したように、DDDの周りの鍵は文章の一貫性を保つことです。クエリのクエリまたはパラメーター化のみを目的とするドメインモデルを使用して設計を強制するかどうかはわかりません。これは、データ構造(poco、pojo、dto、行マッパーなど)を使用してドメイン外からアプローチできます。
ライヴ

これらの人々が正気に戻るのを助けるために、明らかに何らかの調査が必要です。しかし、私は銃にこだわっています。データ層の部分的な露出は、「ドメインオブジェクト」であるかどうかが主観的である、より良いアプリケーションを客観的に作成する場合に許容されます
Ewan

1
@LeonardoManganoは、アプリケーションと実装に依存します。認識すべき主なことは、ドメインを再解釈して実行可能にすることです。
ユアン

3

このジレンマを解決する可能な方法の1つは、SQLをアセンブリ言語のように考えることです。SQLを直接記述することはめったにありませんが、パフォーマンスが重要な場合は、Cによって生成されたコードを理解できる必要があります。 / C ++ / Golang / Rustコンパイラ。高レベル言語のコードを変更して目的のマシンコードを生成できない場合は、アセンブリで小さなスニペットを作成することもできます。

同様に、データベースとSQLの領域では、さまざまなSQLライブラリ(その一部はORMです)、たとえば、SQLAlchemyやPythonのDjango ORM、LINQ for .NETは、パフォーマンスを実現するために生成されたSQLコードを使用しながら、より高いレベルの抽象化を提供します。また、いくつかのより最適なDB固有のSQLを使用した操作のために、PostgresとMySQLなどでパフォーマンスが異なる可能性のある、使用済みのDBに関する移植性も提供されます。

また、高レベル言語と同様に、上記のSQLライブラリを使用して行われたクエリを再配置するだけであっても、SQLがどのように機能するかを理解することが重要です。

PS私はむしろこれをコメントにしたいと思いますが、私はそれについて十分な評判がありません。


2

いつものように、これは多くの要因に依存するものの1つです。SQLでできることはたくさんあります。また、それを使用する際の課題と、リレーショナルデータベースの実際的な制限もあります。

Jared Goguenがコメントで指摘しているように、SQLのテストと検証は非常に困難です。これにつながる主な要因は、(一般に)コンポーネントに分解できないことです。実際には、複雑なクエリを考慮する必要があります。別の複雑な要因は、SQLの動作と正確さがデータの構造と内容に大きく依存することです。これは、考えられるすべてのシナリオをテストする(またはそれらが何であるかを判断する)ことも、実行不可能または不可能であることが多いことを意味します。SQLのリファクタリングとデータベース構造の変更も同様に問題があります。

SQLからの脱却につながったもう1つの大きな要因は、リレーショナルデータベースが垂直方向にのみスケーリングする傾向があることです。たとえば、SQLで複雑な計算を作成してSQL Serverで実行する場合、それらはデータベースで実行されます。つまり、その作業はすべてデータベース上のリソースを使用しています。SQLで多くを行うほど、メモリとCPUの両方の面でデータベースに必要なリソースが増えます。これらのことを他のシステムで行うのは効率が悪い場合が多いですが、そのようなソリューションに追加できる追加のマシンの数に実際的な制限はありません。このアプローチは、モンスターデータベースサーバーを構築するよりも安価で、フォールトトレラントです。

これらの問題は、当面の問題に適用される場合と適用されない場合があります。使用可能なデータベースリソースで問題を解決できる場合は、問題スペースにSQLを使用するのが適切かもしれません。ただし、成長を考慮する必要があります。今日は問題ないかもしれませんが、数年後、リソースを追加するコストが問題になる可能性があります。


モンスターデータベースに代わるものではなく、単に膨大な数と多様な補助システムですか?補助システムがすべてコアシステムからぶら下がっている場合、補助システムにはどのような回復力がありますか?そして、正当化が単にコアシステムの技術的制限である場合、これはほとんどのビジネスシステムにとって時期尚早な最適化になることがよくあります。SQLは、必要に応じて、一般的に分離した方法で記述できます。
スティーブ

@Steveここで間違っているのは、他の人が「ハングオフ」する単一のコアシステムが必要だということです。
ジミージェームズ

@Steve例を挙げると、システム全体のデータベースを単一の非SQLデータベースに置き換えることができます(これが常に正しい選択だとは言いませんが、それができるというだけです)。システム、さらには地理的領域。このようなDBは補助的なものではなく、SQL DBの大規模な代替品です。
ジミージェームズ

@JimmyJamesは同意しましたが、コアシステムがない場合、依存関係の分析とデータの一貫性の維持に独自の問題を引き起こす可能性があります。それがそもそもモノリスの理由です-モノリスは特定の種類のシンプルさを生み出し、したがって特定の種類の分析とメンテナンスの効率を生み出します。非モノリシックソリューションは、いくつかの問題やコストを他のものと交換するだけです。
スティーブ

:@jmorenoそれに沿ってぐったりするために何かで投げリソースは、私は良いエンジニアリングを呼ぶものではありませんサイトの膨大なデータ量を処理するために」、およびトランザクションの数に追いつくために、memcachedのの9000個のインスタンスを実行していますデータベースが機能する必要があります。」 あなたのデザインのコストを考慮しますか、それとも誰かがあなたの個人的な好みを実行可能にするためにお金を払い出すと思いますか?
ジミージェームズ

2

それはDDDパターンの落とし穴ですか?

まず、いくつかの誤解を解き明かします。

DDDはパターンではありません。そして、実際にはパターンを規定していません。

エリック・エヴァンのDDD本の序文は次のように述べています。

主要なソフトウェア設計者は、少なくとも20年間、ドメインモデリングとデザインを重要なトピックとして認識してきましたが、驚くべきことに、何をする必要があるのか​​、どのように行うのかについてほとんど書かれていません。明確に定式化されたことはありませんが、オブジェクトコミュニティの底流として、私がドメイン駆動型設計と呼ぶ哲学が浮上しています。

[...]

成功に共通する機能は、設計の反復を通じて進化し、プロジェクトの構造の一部となったリッチドメインモデルでした。

この本は、設計決定を行うためのフレームワークと、ドメイン設計を議論するための技術用語を提供します。これは、広く受け入れられているベストプラクティスと、私自身の洞察と経験の統合です。

したがって、ソフトウェア開発とドメインモデリングにアプローチする方法に加えて、それらのアクティビティをサポートするいくつかの技術用語(さまざまな概念とパターンを含む用語)があります。また、まったく新しいものでもありません。

留意すべきもう1つのことは、ドメインモデルは、システム内で見つけることができるオブジェクト指向実装ではないことです。これは、それを表現する、またはその一部を表現するための1つの方法にすぎません。ドメインモデルは、ソフトウェアで解決しようとしている問題について考える方法です。それはあなたが物事をどのように理解し知覚するか、それらについて話す方法です。それは概念的です。しかし、あいまいな意味ではありません。それは深く洗練されており、努力と知識の収集の結果です。それはさらに洗練され、時間とともに進化する可能性があり、実装の考慮事項が含まれます(その一部はモデルを制約する可能性があります)。すべてのチームメンバー共有する必要があります (およびドメインの専門家が関与)、システムの実装方法を推進し、システムがシステムを密接に反映するようにします。

OO開発者はおそらくOO言語でモデルを表現するのが一般的に優れており、多くのドメイン概念の表現はOOPでより適切にサポートされますが、それについて本質的にプロSQLまたはアンチSQLはありません。ただし、モデルの一部を異なるパラダイムで表現する必要がある場合があります。

私が疑問に思っているのは、非常に複雑なクエリがあるときに何が起こるかです[...]?

さて、一般的に言えば、ここには2つのシナリオがあります。

最初のケースでは、ドメインの一部の側面は本当に複雑なクエリを必要とし、おそらくその側面はSQL /リレーショナルパラダイムで最もよく表現されるので、ジョブに適切なツールを使用してください。これらの側面を、ドメインシンキングとコンセプトの伝達に使用される言語に反映します。ドメインが複雑な場合、これはおそらくサブドメインの一部であり、独自の境界付きコンテキストです。

もう1つのシナリオは、SQLで何かを表現する必要があると認識されているのは、制約された思考の結果であるということです。人やチームが常にデータベース指向の考え方をしている場合、慣性によって、物事にアプローチする別の方法を見つけるのは難しいかもしれません。これは、古い方法で新しいニーズを満たすことができない場合に問題になり、箱から出して考える必要があります。設計へのアプローチとしてのDDDは、ドメインに関する知識を収集および抽出することにより、その枠から抜け出す方法を見つける方法の一部です。しかし、誰もが本のその部分を無視しているようで、リストされている技術用語とパターンのいくつかに焦点を当てています。


0

リレーショナルデータモデルはデータを正規化し、ファイルシステムに効果的に保存する可能性を提供したため、メモリが高価なときにSequelが一般的になりました。

現在、メモリは比較的安価であるため、正規化をスキップして、使用する形式で保存するか、高速化のために同じデータを大量に複製することもできます。

データベースを単純なIOデバイス見なし、データをファイルシステムに保存する責任があります-はい別のプリンターです。

PDFジェネレーターをプリンタードライバーに埋め込むか、プリンターから印刷されたすべての販売注文のログページを印刷するトリガーを追加しますか?

私たちのアプリケーションが特定のデバイスタイプに結合されることを望まないので、答えはノーだと思います(そのようなアイデアの効率についても話しません)

70年代から90年代に、SQLデータベースは効率的になりましたか?-確かではありません。一部のシナリオでは、非同期データクエリは、SQLクエリでの複数の結合よりも速く必要なデータを返します。

SQLは複雑なクエリ用に設計されたのではなく、効率的な方法でデータを保存するために設計され、保存されたデータをクエリするためのインターフェイス/言語を提供します。

複雑なクエリを使用したリレーショナルデータモデルを中心にアプリケーションを構築することは、データベースエンジンの悪用だと思います。もちろん、データベースエンジンプロバイダーは、ビジネスを自社の製品に緊密に結合することで満足します。この境界をさらに強化する機能を提供することは、彼らにとってより喜ばしいことです。


1
しかし、私はSQLが他のどの言語よりもセット計算に優れていると考え続けています。私の視点から。あなたの例は逆さまで、何百万もの行と結合を伴う非常に複雑な集合演算にC#を使用すると、間違ったツールが使用されますが、間違っている可能性があります。
レオナルドマンガノ

@LeonardoMangano、いくつかの例:c#では何百万行をチャンクして並列に計算でき、データを非同期に取得し、データが返されたときに「時間内に」計算を実行できます行ごと。コードに複雑なロジックを含めると、計算の実行方法に関する多くのオプションが提供されます。
ファビオ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.