ボブおじさんのクリーンアーキテクチャ-各レイヤーのエンティティ/モデルクラス?


44

バックグラウンド :

私はAndroidアプリでボブおじさんのクリーンアーキテクチャを使用しようとしています。私はそれを行う正しい方法を示しようとしている多くのオープンソースプロジェクトを研究し、RxAndroidに基づいた興味深い実装を見つけまし

気づいたこと:

すべてのレイヤー(プレゼンテーション、ドメイン、およびデータ)には、同じエンティティ(UMLを話す)のモデルクラスがあります。さらに、データが境界を越えたとき(レイヤーから別のレイヤーへ)にオブジェクトを変換するマッパークラスがあります。

質問 :

すべてのCRUD操作が必要な場合、すべてのモデルクラスが同じ属性で終わることがわかっている場合、すべてのレイヤーにモデルクラスが必要です。または、クリーンアーキテクチャを使用する場合のルールまたはベストプラクティスですか?

回答:


52

私の意見では、それは絶対に意味ではありません。そして、それはDRYの違反です。

アイデアは、真ん中のエンティティ/ドメインオブジェクトが、ドメインを可能な限り適切かつ便利に表すようにモデル化されるということです。ドメイン自体はほとんど変更されないため、すべての中心にあり、すべてがそれに依存する可能性があります。

外部のデータベースがこれらのオブジェクトを直接保存できる場合、レイヤーを分離するために別の形式にマッピングすることは無意味であるだけでなく、モデルの複製を作成することも意図していません。

まず、クリーンなアーキテクチャは、異なる典型的な環境/シナリオを念頭に置いて作成されました。独自の種類の特別なオブジェクトを必要とする巨大な外部層を持つビジネスサーバーアプリケーション。たとえば、SQLRowオブジェクトを生成しSQLTransactions、その代わりにアイテムを更新する必要があるデータベース。センターでそれらを使用する場合、コアがデータベースに依存するため、依存関係の方向に違反することになります。

エンティティオブジェクトを読み込んで格納する軽量のORMでは、そうではありません。彼らは、内部SQLRowとドメインの間のマッピングを行います。@EntitiyドメインオブジェクトにORMの注釈を挿入する必要がある場合でも、これは外部層の「言及」を確立しないと主張します。注釈は単なるメタデータであるため、特に注釈を探していないコードには注釈が表示されません。さらに重要なことは、それらを削除したり、別のデータベースの注釈に置き換えたりしても、変更する必要はありません。

対照的に、ドメインを変更し、それらすべてのマッパーを作成した場合、多くの変更を行う必要があります。


修正:上記は少し単純化しすぎており、間違っている可能性さえあります。クリーンアーキテクチャには、レイヤーごとにリプレゼンテーションを作成することを望む部分があるためです。しかし、それはアプリケーションのコンテキストで見なければなりません。

すなわち、次のhttps://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html

重要なことは、分離された単純なデータ構造が境界を越えて渡されることです。エンティティまたはデータベース行をごまかして渡したくありません。データ構造に、依存関係ルールに違反する種類の依存関係を持たせたくありません。

エンティティを中心から外側の層に渡すことは、依存関係の規則に違反しませんが、言及されています。しかし、これには、想定されるアプリケーションのコンテキストで理由があります。エンティティを渡すと、アプリケーションロジックが外側に移動します。外側のレイヤーは、内側のオブジェクトを解釈する方法を知る必要があり、「ユースケース」レイヤーのような内側のレイヤーが行うべきことを効果的に実行する必要があります。

それに加えて、コアへの変更が必ずしも外側のレイヤーの変更を必要としないように、レイヤーを分離します(SteveCallenderのコメントを参照)。そのコンテキストでは、オブジェクトが使用目的を具体的に表す方法を簡単に確認できます。また、この通信の目的のために特別に作成されたオブジェクトの観点から、その層は互いに対話する必要があります。これは、3つの表現(各レイヤーに1つ、レイヤー間のトランスポートに1つ)があることを意味する場合もあります。

そして、上記に対処するhttps://blog.8thlight.com/uncle-bob/2011/11/22/Clean-Architecture.htmlがあります。

他の人々は、私のアドバイスの最終的な結果が、コードの重複と、システムのレイヤー全体であるデータ構造から別のデータ構造へのデータの大量のコピーになることを心配しています。確かに私はこれも望まない。そして、私が示唆したことは、データ構造の繰り返しとフィールドコピーの異常を必然的にもたらすものではありません。

IMOは、オブジェクトの単純な1:1コピーは、実際には適切なレイヤーおよび/または抽象化を使用していないため、アーキテクチャの臭いであることを意味します。

彼は後で彼がすべての「コピー」をどのように想像するかを説明します

2つの間で単純なデータ構造を渡すことにより、UIをビジネスルールから分離します。コントローラーにビジネスルールについては何も知らせません。代わりに、コントローラーはHttpRequestオブジェクトを単純なバニラデータ構造にアンパックし、ビジネスオブジェクトを呼び出すことでユースケースを実装するインタラクターオブジェクトにそのデータ構造を渡します。次に、インタラクターは応答データを別のバニラデータ構造に収集し、UIに返します。ビューはビジネスオブジェクトについて知りません。彼らは単にそのデータ構造を見て、応答を提示します。

このアプリケーションでは、表現に大きな違いがあります。流れるデータは、エンティティだけではありません。そして、これは異なるクラスを保証し要求します。

ただし、Photoエンティティに約0のビジネスルールがあり、それらを扱う「ユースケース」がほとんど存在せず、実際にはキャッシングとダウンロードに関心がある写真ビューアーなどの単純なAndroidアプリケーションに適用されます(そのプロセスはIMOより明確に表されます)、写真の別々の表現をするポイントは消え始めます。写真そのものがデータ転送オブジェクトであり、実際のビジネスロジックコアレイヤーが欠落しているという感覚さえ感じます。

「2つの間で単純なデータ構造を渡すことにより、ビジネスルールからUIを分離する」「途中で写真の名前を3回変更して表示する場合」には違いがあります

それに加えて、これらのデモアプリケーションがクリーンアーキテクチャを表すのに失敗するのは、レイヤーを分離するためにレイヤーを分離することを非常に重視しているが、アプリケーションの動作を事実上隠すことです。これはhttps://blog.8thlight.com/uncle-bob/2011/09/30/Screaming-Architecture.htmlで言われていることとは対照的です-つまり

ソフトウェアアプリケーションのアーキテクチャは、アプリケーションのユースケースについて叫びます

クリーンアーキテクチャでは、レイヤーを分離することに重点が置かれていません。これは、依存関係の方向性と、アプリケーションのコア(エンティティとユースケース)を、外部への依存関係のない理想的なプレーンJavaで表すことに焦点を当てています。そのコアに対する依存関係についてはそれほどではありません。

そのため、アプリケーションにビジネスルールとユースケースを表すコアが実際にある場合、および/または異なる人々が異なるレイヤーで作業する場合は、意図した方法でそれらを分離してください。一方、単純なアプリをすべて自分で作成する場合は、無理をしないでください。流な境界を持つ2層で十分かもしれません。また、レイヤーは後で追加することもできます。


1
@RamiJemli理想的には、エンティティはすべてのアプリケーションで同じです。これが、「企業全体のビジネスルール」と「アプリケーションビジネスルール」の違いです(ビジネスロジックとアプリケーションロジックの場合があります)。コアは、エンティティの非常に抽象的な表現であり、どこでも使用できるほど十分に汎用的です。顧客サポート、現金自動預け払い機、顧客自身のWeb UIなど、多くのアプリケーションを備えた銀行を想像してください。これらはすべて同じものを使用できますBankAccountが、アプリケーション固有のルールを使用すると、そのアカウントで何ができるかを確認できます。

4
クリーンアーキテクチャの重要なポイントは、インターフェイスアダプターレイヤーを使用してエンティティの異なるレイヤーの表現間で変換する(またはマップと言うように)ことにより、そのエンティティへの依存性を減らすことです。ユースケースまたはエンティティレイヤーに変更がある場合(可能性は低いですが、要件が変更されるとこれらのレイヤーが変更されるため)、変更の影響はアダプターレイヤーに含まれます。すべてのアーキテクチャでエンティティの同じ表現を使用することを選択した場合、この変更の影響ははるかに大きくなります。
SteveCallender

1
@RamiJemliでは、生活をシンプルにするフレームワークを使用するのが良いです。重要なのは、アーキテクチャがそれらに依存している場合は注意し、それらをすべての中心に置くことです。RxJava blog.8thlight.com/uncle-bob/2015/08/06/let-the-magic-die.htmlについての記事もあります-使用すべきではないと言っているわけではありません。それはもっと似ています:私はこれを見てきましたが、1年で変わるでしょう、そしてあなたのアプリケーションがまだ周りにあるとき、あなたはそれで立ち往生しています。単純な古いSOLID原則を適用しながら、詳細を作成し、単純な古いJavaで最も重要なことを行います。
zapl

1
@zapl Webサービスレイヤーについても同じように感じていますか?言い換えれば、@SerializedNameドメインモデルにGsonアノテーションを付けますか?または、Web応答をドメインモデルにマッピングする新しいオブジェクトを作成しますか?
tir38

2
@ tir38分離自体には利点はありませんが、分離に伴う将来の変更のコストです。=>アプリケーションに依存します。1)異なる表現間で変換する追加のステージを作成および維持するのに時間がかかります。たとえば、ドメインにフィールドを追加し、それを他の場所に追加するのを忘れることは前代未聞ではありません。単純なアプローチでは実現できません。2)後で必要になった場合に備えて、より複雑なセットアップに移行するのに費用がかかります。レイヤーを追加すると、それがすぐに必要とされていない複数の層を正当化するために大規模なアプリケーションではそのために簡単です、簡単ではありません
zapl

7

あなたは実際にそれを正しくしました。また、SRPを受け入れるため、DRYの違反はありません。

たとえば、business-method createX(String name)があり、business-Method内で呼び出されるDAO-LayerにMethod createX(String name)がある場合があります。彼らは同じ署名を持っているかもしれず、多分委任だけがあるかもしれませんが、彼らは異なる目的を持っています。UseCaseでcreateX(String name)を使用することもできます。それでも冗長ではありません。つまり、同じ署名は同じ意味を意味しません。他の名前を選択して、セマンティクスを明確にします。これ自体に名前を付けても、SRPにはまったく影響しません。

UseCaseはアプリケーション固有のロジックを担当し、ビジネスオブジェクトはアプリケーションに依存しないロジックを担当し、DAOは保存を担当します。

セマンティクスが異なるため、すべてのレイヤーに独自の表現および通信モデルがあります。多くの場合、「エンティティ」は「ビジネスオブジェクト」と見なされ、多くの場合、それらを分離する必要性は見られません。しかし、「巨大な」プロジェクトでは、レイヤーを適切に分離するための努力が必要です。プロジェクトが大きくなるほど、異なるレイヤーとクラスで表される異なるセマンティクスが必要になる可能性が高まります。

同じセマンティックのさまざまな側面を考えることができます。User-Objectは画面に表示する必要があり、内部整合性ルールがあり、どこかに保存する必要があります。各側面は、異なるクラス(SRP)で表される必要があります。マッパーを作成するのは苦痛になる可能性があるため、これらの側面で作業したほとんどのプロジェクトでは、1つのクラスに溶け込んでいます。これは明らかにSRPの違反ですが、実際には誰も気にしません。

クリーンアーキテクチャとソリッドのアプリケーションを「社会的に受け入れられない」と呼びます。許可されれば、それで作業します。現在、私はそれを行うことはできません。SOLIDを真剣に考えることを考えなければならない瞬間を待ちます。


データ層のメソッドは、ドメイン層のメソッドと同じ署名を持つべきではないと思います。ドメインレイヤーでは、signUpやloginなどのビジネス関連の命名規則を使用し、データレイヤーでは、save(DAOパターンの場合)またはadd(このパターンがコレクションをメタファーとして使用するためリポジトリの場合)を使用します。最後に、エンティティ(データ)とモデル(ドメイン)のことではなく、UserModelとそのマッパー(プレゼンテーションレイヤー)の無用さを強調しています。プレゼンテーション内でドメインのUserクラスを呼び出すことができ、これは依存関係ルールに違反しません。
ラミジェムリ

インタラクターの実装で直接マッピングを行うことができるため、マッパーは不要です。
クリストファーペリー

5

いいえ、すべてのレイヤーにモデルクラスを作成する必要はありません。

エンティティDATA_LAYER)-データベースオブジェクトの完全または部分的な表現です。DATA_LAYER

マッパーDOMAIN_LAYER)-実際には、EntityをModelClassに変換するクラスであり、DOMAIN_LAYER

ご覧くださいhttps : //github.com/lifedemons/photoviewer


1
もちろん、データレイヤーのエンティティに反対するわけではありませんが、あなたの例では、プレゼンテーションレイヤーのPhotoModelクラスは、ドメインレイヤーのPhotoクラスと同じ属性を持っています。技術的には同じクラスです。それは必要ですか?

私はあなたのマッパーは、IMO、それはその逆である必要があり、あなたのデータ層のエンティティに依存してあなたの例のように、ドメイン層が他の層に依存してはならないと何かがあなたの例ではオフになっていると思います
navid_gh
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.