誤解のないクリーンアーキテクチャ

円をブロックに変える







一見したところ、 Clean Architectureは、アプリケーションを構築するためのかなり単純な一連の推奨事項です。 しかし、私と私の同僚である強力な開発者の多くは、このアーキテクチャをすぐには理解しませんでした。 そして最近、チャットルームやインターネットで、それに関連する誤解が増えています。 この記事で、私はコミュニティがClean Architectureをよりよく理解し、よくある誤解を取り除くのを助けたいです







すぐに予約したいのですが、妄想は私的な問題です。 誰もが誤解される権利を持っています。 そして、それが彼に合っていれば、私は邪魔したくありません。 しかし、他の人の意見を聞くことは常に良いことであり、多くの場合、人々は最前線にいた人の意見さえも知りません。







起源



2011年に、 ロバートC.マーティンは 、ボブおじさんとしても知られ、 スクリーミングアーキテクチャに関する記事を公開しました。これは、アーキテクチャが使用するフレームワークではなく、アプリケーション自体について「叫ぶ」べきだと述べています。 後に、ボブおじさんが純粋な建築のアイデアと戦う記事が出ました。 そして2012年に、彼はこのアプローチの主要な説明である記事「 クリーンアーキテクチャ 」を公開しました。 これらの記事に加えて、ボブおじさんのビデオプレゼンテーションを見ることも強くお勧めします。







Clean Architectureに関して開発者の頭に最初に浮かんだ記事の元の概要は次のとおりです。







元の回路



Androidコミュニティでは、「 Androidを設計する...クリーンな方法?」という記事の後、Cleanはすぐに人気を博し始めました フェルナンド・セハスによって書かれました。 私は最初にクリーンアーキテクチャについて学びました。 そしてその後、彼はオリジナルを探しに行きました。 この記事では、Fernandoがこのレイヤースキームを提供しています。







フェルナンド・セハスのスキーム



他のレイヤーがこの図にあり、他のいくつかのインタラクターと境界がドメインレイヤーにあるという事実は、紛らわしいです。 元の写真は誰にとっても明確ではありません。 記事では、多くは曖昧またはわずかに抽象的です。 そして、誰もがビデオを見るわけではありません(通常、英語の知識が不十分なため)。 そして今、 誤解のために、人々は何かを発明し始め、物事を複雑にし、間違いを犯し始めます...







それを理解しましょう!







きれいな建築



クリーンアーキテクチャは、他のいくつかのアーキテクチャアプローチのアイデアを組み合わせたものです。









これは、依存関係ルール(依存関係ルール)を階層化して従うことによって実現されます。







依存関係ルール



依存性ルールは、内側の層が外側の層に依存してはならないことを示しています 。 つまり、ビジネスロジックとアプリケーションロジックは、プレゼンター、UI、データベースなどに依存すべきではありません。 元の図では、この規則は内側を指す矢印で表されています。







記事によると、外側の層で宣言されたエンティティ(クラス、関数、変数、その他)名前は、内側の層のコードには表示されません







このルールを使用すると、外側のレイヤーの変更が内側のレイヤーに影響を与えないため、メンテナンスが容易なシステムを構築できます。







レイヤー



ボブおじさんは4つのレイヤーを選択します。









この記事の中で、これらのレイヤーがどのようなものであるかをさらに詳しく検討します。 それまでの間、それらの間のデータの転送について説明しましょう。







遷移



レイヤー間の遷移は、Boundariesを介して 、つまり、リクエスト用とレスポンス用の2つのインターフェースを介して行われます。 元の図(Input / OutputPort)の右側に表示されます。 これらは、内側の層が外側に依存しないようにするために必要です(依存性ルールに従って)が、同時にデータを渡すことができます。







元の回路上のデータの流れ



両方のインターフェイスは、内側のレイヤーに属します(画像内の色に注意してください)。







見てください、コントローラーはInputPortでメソッドを呼び出し、UseCaseを実装し、次にUseCaseがPresenterを実装するOutputPortインターフェースに応答します。 つまり、データはレイヤー間の境界を越えましたが、すべての依存関係はUseCaseレイヤーを内側に向けています。







依存関係がデータフローとは反対の方向に向けられるように、 依存関係の反転原理が適用されます (略語SOLIDの文字D)。 つまり、UseCaseをプレゼンターに直接依存する(依存規則に違反する)代わりに、レイヤーのインターフェイスに依存するため、Presenterはこのインターフェイスを実装する必要があります。







UsingCaseがGateway / Repositoryにアクセスする場合など、まったく同じスキームが他の場所でも機能します。 リポジトリに依存しないように、インターフェースが強調表示され、UseCasesレイヤーに配置されます。







国境を越えるデータに関しては、 単純な構造であるべきです。 DTOとして渡すか、HashMapにラップするか、メソッドを呼び出すときに引数として渡すことができます。 しかし、それらは内層にとってより便利な形式でなければなりません( 内層にあるため )。







モバイルアプリの機能



Clean Architectureは、わずかに異なるタイプのアプリケーションを念頭に置いて考案されたことに注意する必要があります 。 大企業向けの大規模サーバーアプリケーションであり、さらなる開発を必要としない中程度の複雑さのモバイルクライアントサーバーアプリケーションではありません(もちろん、さまざまなアプリケーションがありますが、ほとんどがそのようなものであることを認める必要があります)。 これを理解しないと、 過剰なエンジニアリングにつながる可能性があります。







元の図には、コントローラーという単語があります。 これは、特にRuby On Railsのフロントエンドのために図に現れました。 多くの場合、リクエストを処理して結果を返すControllerと、この結果をビューに表示するPresenterを分離しています。 多くの人はすぐには推測しませんが、Androidアプリケーションではコントローラーは必要ありません







ボブおじさんはまた、記事で4層ある必要はないと言っています 。 任意の数を指定できますが、依存関係ルールを常に適用する必要があります。







Fernando Cejasの記事のスキームを見ると、著者はこの機会を利用してレイヤーの数を3つに減らしたと思うかもしれません。 しかし、これはそうではありません。 見てみると、ドメインレイヤーにはインタラクター(これはUseCasesの別名)とエンティティの両方があります。







フェルナンドの記事に感謝します。これはAndroidコミュニティでのCleanの開発に大きな弾みをつけましたが、彼のスキームも混乱を招きました。







誤解:レイヤーと直線性



ボブおじさんの元のサーキットとフェルナンド・セハスのスキームを比較すると、多くの人が混乱し始めます。 線形スキームは簡単に認識され、人々はオリジナルを誤解し始めます。 そして、オリジナルを理解していないので、彼らは誤解して線形になり始めます。 円の中の碑文の位置は神聖な意味を持っている、またはコントローラーを使用する必要がある、または2つのスキームのレイヤーの名前を相関させる必要があると誰かが考えています。 おかしくて悲しいですが、基本的なスキームがエラーの主な原因になっています!







修正を試みます。 はじめに、メイン回路をクリアして、不要なものを削除します。 そして、ゲートウェイの名前をリポジトリに変更します。 これは、このエンティティのより一般的な名前です。







簡素化された元のレイアウト



少し明確になりました。 次に、これを行います。 レイヤーを断片にカットし、このスキームをブロックスキームに変更します。ここで、色はレイヤーが属することを示します。







円をブロックに変える



上で言ったように、色はレイヤーを示します。 また、下の矢印は、依存関係ルールを示しています。







結果の図では、UIからデータベースまたはサーバーへ、またはその逆へのデータの流れを簡単に想像できます。 しかし、レイヤーをカテゴリに配置することにより、線形性に向けて別のステップを踏みましょう。







カテゴリー別のレイヤー

フェルナンド・セハスとは異なり、私は意図的にこの分離をレイヤーで呼ぶことはしません。 すでにレイヤーを分割しているからです。 カテゴリまたはパーツと呼びます。 好きな名前を付けることができますが、「レイヤー」という言葉を再利用しないでください。







それでは、フェルナンドサーキットで何が起こったのかを比較しましょう。







フェルナンドセハスサーキットとの比較

私は今、すべてが適所に落ち始めたことを願っています。 私の意見では、フェルナンドにはまだ4つの層があると上記で述べました。 今ではそれも明らかになったと思います。 ドメイン部分には、ユースケースとエンティティの両方があります。







このようなスキームは簡単に認識されます。 結局のところ、通常、アプリケーションのイベントとデータはUIからバックエンドまたはデータベースに、またはその逆に移動します。 このプロセスを説明しましょう:







UIからのデータストリーム

赤い矢印はデータ流れを示します







ユーザーイベントはプレゼンターに送られ、プレゼンターはユースケースに渡されます。 ユースケースはリポジトリにリクエストを行います。 リポジトリはどこかでデータを取得し、エンティティを作成してUseCaseに渡します。 したがって、ユースケースは必要なすべてのエンティティを取得します。 次に、それらとそのロジックを適用すると、Presenterに返される結果を受け取ります。 次に、結果がUIに表示されます。







レイヤー間の遷移(カテゴリではなく、異なる色でマークされたレイヤー)では、前述の境界が使用されます。







2つのスキームがどのように関連するか理解したので、次の誤解を見てみましょう。







誤解:エンティティではなくレイヤー



タイトルからわかるように、誰かがエンティティがダイアグラムに表されていると考えています(これは特にUseCasesとEntitiesに影響します)。 しかし、これはそうではありません。







ダイアグラムにはレイヤーが描かれており、多くのエンティティを含めることができます 。 これらには、レイヤー間の遷移(境界)、さまざまなDTO、レイヤーのメインクラス(UseCasesレイヤーのインタラクターなど)のインターフェイスが含まれます。







ボブおじさんのスピーチのビデオに示されているパーツから組み立てられた図を見るのは不必要ではありません。 クラスと依存関係を示します







ボブおじさんによるパフォーマンスのクラス図



二重線を参照してください? これらは、レイヤー間の境界です。 ビデオでは、すべてのロジック(アプリケーションおよびビジネス)が外界から隔離されているという事実に主眼が置かれていたため、エンティティレイヤーとユースケースレイヤーの分離は示されていません。







私たちはすでに境界に精通しており、ゲートウェイのインターフェースは同じです。 Request / ResponseModelは、レイヤー間でデータを転送するための単なるDTOです。 依存関係のルールによれば、それらは写真に見られる内側の層にあるはずです。







コントローラーについても話しましたが、興味はありません。 その機能はPresenterによって実行されます。







また、図のViewModelはMVVMのViewModelではなく、 アーキテクチャコンポーネントの ViewModelでもありません。 これは、Viewデータを送信するための単なるDTOであるため、Viewはダムであり、フィールドを単にメッシュします。 ただし、これらは実装の詳細であり、プレゼンテーションパターンの選択と個人的なアプローチに依存します。







UseCasesレイヤーには、インターアクターだけでなく、プレゼンターと連携するための境界、リポジトリと連携するためのインターフェイス、および要求と応答のためのDTOも含まれています。 これから、オリジナルのスキームがまだ層を反映していると結論できます。







誤解:エンティティ



エンティティは、誤解によって正当に最初の場所を取ります。







それだけでなく、ほとんど誰も(最近まで私を含めて)それが何であるかを理解していないだけでなく、彼らはDTOと混同されています。







チャットに参加した後、紛争が発生しました。そこでは、エンティティはデータレイヤーでJSONを解析した後に取得されたオブジェクトであり、DTOはInteractorsが操作するオブジェクトであることが証明されました...

他の人がそのようなエラーを起こさないように、うまく理解しようとします。







エンティティとは何ですか?







ほとんどの場合、インターアクターが機能するPOJOクラスとして認識されます。 しかし、これはそうではありません。 少なくともそうではありません。







記事の中で、ボブおじさんは、 エンティティはビジネスのロジックつまり特定のアプリケーションに依存しないが、多くの人に共通するすべてのものを カプセル化すると述べています 。 ただし、別のアプリケーションがあり、既存のビジネス向けに調整されていない場合、エンティティは、最も一般的で高レベルのルールを含むアプリケーションのビジネスオブジェクトになります







「エンティティはビジネスオブジェクトです」というフレーズが最も紛らわしいと思います。 さらに、上の図では、Interactorはビデオからゲートウェイからエンティティを取得します。 また、これらは単なるPOJOオブジェクトであるという感覚を強化します。







しかし、この記事では、 Entityはメソッドまたは構造と機能のセットを持つオブジェクトになり得るとも述べています 。 つまり、データではなくメソッドが重要であるという事実に重点が置かれています。







これは、最近見つけたボブおじさんの説明でも確認されています。

ボブおじさんは、エンティティにはアプリケーションに依存しないビジネスルールが含まれていると言っています。 そして、それらは単なるデータオブジェクトではありません。 エンティティには、データを持つオブジェクトへの参照が含まれる場合がありますが、その主な目的は、さまざまなアプリケーションで使用できるビジネスロジックメソッドを実装することです







そして、ゲートウェイが写真のエンティティを返すという事実について、彼は次のように説明しています。

ゲートウェイの実装は、データベースからデータを受け取り、それを使用して、ゲートウェイが返すエンティティに渡されるデータ構造を作成します。 実装されたこれは構​​図かもしれません







class MyEntity { private MyDataStructure data;}
      
      











class MyEntity extends MyDataStructure {...} 
      
      





, :







And remember, we are all pirates by nature; and the rules I'm talking about here are really more like guidelines…

( : , , , , …)

, , . - , .







, Entities :









, , Entities UseCases, .







: UseCase / Interactor



UseCase Interactor. : « Interactor ». : « Interactor’e UseCase?».







Interactor’a , . :







«...interactor object that implements the use case by invoking business objects.»







:







Interactor – , use case ( ), - (Entities).







Use Case ?

Uncle Bob «Object-Oriented Software Engineering: A Use Case Driven Approach», Ivar Jacobson 1992 , , Use Case.







Use case – , , .







, :







 Use Case

Use Case , .







, , . .







– Use Case’a, – .







:









Ivar Jacobson Use Case , ControlObject.

Uncle Bob , , Controller MVC Interactor. , UseCase.

.







, Interactor use case execute() , . .







.







- , Interactor’a – . . .







Interactor’ , use case’.

, , . Interactor’, .







: « – UseCase’», – . . , UseCase Interactor , .







Interactor UseCase, : Interactor/UseCase – , use case ( ).







, , -, , – Repository.









- , . Uncle Bob Gateway, Repository.







Repository



Repository? , , ( Fernando Cejas ), .







Repository - . , , .







Android- Repository , .







Hannes Dorfmann’.







Gateway



Repository, «» , login()



(, Repository, – , ).







, Gateway – , . Gateway , API . , , .







«», .







Repository/Gateway Interactor?



, . !

Repository Interactor.







, , , Repository Presenter’a, Interactor.







Repository , Dependency Rule Repository . – Interactor . Interactor, , , proxy-interactor’, , .







, , Interactor, , , Interactor , . .







:



, . .







DTO Entities . , . Dependency Rule .







– . .







DTO :









DTO Enitities:









.







:







, / , ( ). , API . , , .







: Interactor’e



, . , c:

So when we pass data across a boundary, it is always in the form that is most convenient for the inner circle.

( , )







Interactor .

Interface Adapters, Presenter Repository.







?



. API . , login()



Profile OrderState. , , Repository.







LoginResponse Profile OrderState , Interactor’e Repository?







Interactor’e. , .. .







Repository. :









Interactor, , .







Interactor Repository?



Interactor Repository. , « Repository/Gateway Interactor?».







Clean Architecture .

:









, , . , . .







RxJava Clean Architecture



Android- RxJava. , Fernando Cejas , RxJava Clean Architecture.







, , , , Boundaries ( Dependency Rule) Observable Subscriber.







, RxJava , . , – Clean Architecture.







, RxJava . Java 9 util.concurrent.Flow, Reactive Streams, RxJava2. - RxJava, .







: Clean Architecture MVP?



, ? .

:









: Clean Architecture



. Google Architecture Components.







- . . .







, – . , , , . , .







, , .







!



. . Clean Architecture, , , « »!








All Articles