オニオンアーキテクチャPopUpService
このタイプのアーキテクチャを想定すると、それをクリーン、ddd、オニオン、または六角形と呼びます。依存関係は内側を指し、アプリケーションロジックはUI /プレゼンテーションから大幅に分離されることを意味します。
ポップアップを表示するようなものを処理するにはどうすればよいですか?ポップアップは、明らかにプレゼンテーション層に実際に実装する必要があります。しかし、ポップアップを表示し、ユーザー入力に従って動作する必要があるクラス/ユースケース/サービス/実際のロジックは、アプリケーション層に存在します。
だから私はアプリケーション層でインターフェースIPopUpServiceを作成します。突然、永続層でアクセスできるようになりました。私は文字通りポップアップをデータベースクエリに入れることができます。これはかなり大きな欠陥のようです。
ちなみに、なぜこのアーキテクチャの例はWebAPIとしてのみ提供されているのでしょうか。githubでの話し合いやソリューションの例。それはフロントエンドにとらわれないはずですが、人々はいつもどういうわけかはるかに簡単なものになってしまいます。
私の特定のアプリケーションはwpfmvvmです。Mvvmは、ロジックをUIから切り離すことを想定したもう1つのアーキテクチャスタイルです。それでも私は同じ問題に遭遇します。私のビューモデルは、IPopUpServiceを使用する非wpfプロジェクトにあります。ただし、これにより、リポジトリでも使用できるようになります。私はそれをしません、しかし私にはオプションがあります、それは私には間違っているようです。
「インターフェイスを介してすべてをアプリケーション層に配置する」こと全体は、1つの層、1つの大きなプロジェクトを持つことに似ています。IPopUpServiceへの依存関係は、wpfのPopUpBoxへの依存関係と同じではないことを理解していますが、十分に近いと感じています。私はどこか間違って考えていますか?アクセスを制限することは考慮事項ではありませんか?
回答
ポップアップはUIの特定の実装に純粋に関連する概念であるため、通常、一般的な「ポップアップ」サービスはどこにも配置しません。
適切な代替案を見つけるには、具体的なケースを分析する必要があります。
- 永続性レイヤーでデータベースクエリを実行するために追加情報が必要な場合、ユースケースはすでにこの情報を永続性呼び出しに配信しているはずです。したがって、ユースケース(通常は何らかのUI入力と出力が含まれます)は、必要な情報をすでに「認識」しており、この情報を最初の呼び出しに渡す必要があります。
- コアドメインが何らかの形で関係者に情報を伝えたい場合は、何らかのメッセージサービスを使用してください。このメッセージは、ポップアップを表示するためにUIによって取得される場合があります。また、このメッセージは、ロガーや電子メールサービス、または任意の数の代替実装によって取得される場合があります。
これらは2つの例にすぎませんが、アプリケーションの計画方法を「UIで何が起こるか」から「アプリケーションで何が起こるか」という質問にシフトする方法を示していると思います。
全体的な考え方は、コアドメインを使用でき、サービスは複数の(UI)コンテキストで使用できるということです。たとえば、永続性コンポーネントは、バッチまたはREST呼び出しからも使用できるように設計する必要があります。これは、操作の途中で追加情報を要求する機能を完全に除外します。
あなたはとても明白な何かに出くわしました、ほとんどの人はそれをただ見ません:これらのアーキテクチャは実際には何も切り離しません。
これらの種類のアーキテクチャで必要なことを行うには、このポップアップに関連するすべてのデータをエクスポート/公開し、UIにこのデータを使用してポップアップを表示させる必要があります。これは明らかにデカップリングの反対です。片側に何か新しいものが必要で、ほとんどの場合、反対側も変更する必要があります。
私がこれからの認知的不協和に対処するために使用した方法は、私がコアでいくつかのデータを公開しているだけであると合理化したということです。それはポップアップに関するものではなく、他の何かにも使用できました。もちろん、そうなることはめったにありませんでした。
もちろん、これがすべての例がWebベースであり、通常は非常に簡単である理由です。
だから、あなたは本物の何かを発見しました。掘り下げて、誰も信用しないでください(有名な作者からの悪いコンテンツがたくさんあります):)。常に自分で試してみてください。
このようなものは機能しますか?
Something.Ui.WpfApplication
+ Services
- PopupService
Something.Ui
+ Contracts
- IPopupService
+ ViewModels
Something.Data
+ Repositories
Something.Core
+ Contracts
+ Domain
これにより、ビューオブジェクトがビュー(プレゼンテーション)層に分離されます。
コア/ドメインコントラクトはすべて重要なビジネスオペレーションですが、各レイヤーが独自のコントラクトを持つこともできます。
上のレイヤーは、下のレイヤーから実装されます。
コントラクトは通常、互換性のために使用されるため、次のような結果になる可能性があります。
Something.Ui.WpfApplication
+ Services
- RedPopUpService
Something.Ui.WinFormsApp
+ Services
- BluePopUpService
Something.Ui
+ Contracts
- IPopUpService
+ Services
- PopUpSharedLogic
+ ViewModels
「それで、アプリケーション層でインターフェースIPopUpServiceを作成します。突然、永続層でそれにアクセスできるようになりました。」
何かを参照できるという事実は、あなたがすべきだという意味ではありません。相互依存の数を注意深く制御する必要があります(複雑さを最小限に抑えるため。絡み合ったWebは必要ありません)。
さらに、永続層自体は、基本的に、アプリケーション層(たとえば、1つまたは複数のインターフェース)によって(およびその内部で)定義された、いくつかの永続関連の抽象化を実装します。したがって、それらについてもほぼ同じことを言うことができます(外層の非永続部分からそれらを参照できます-そうする必要があるという意味ではありません)。したがって、それが暗示されているだけであっても、そこにはある種の分解があります。レイヤー自体はモノリシックではありません。レイヤーをまたがる依存関係は、一般的に言えば、実際には内側のレイヤーの特定の部分に限定されており、すべてを網羅しているわけではありません。
アプリケーション層自体は、何らかの方法で分解されます(機能、ユースケース、サブシステムなどによって)。繰り返しますが、目標は、依存関係を最小限に抑えることで複雑さを制御することです。この分解はほとんど論理的ですが、ある時点でレイヤーを複数のDLLに分割することもできます。それでもすべて1つのレイヤーですが、外側のレイヤーのコンポーネントは、必要なDLLのみを参照し、すべてではありません。アプリケーションをそのようなDLLに分解できるようにするために、アプリケーションに大きな設計変更を加える必要はありません。
これで、システムを開発し、問題のドメインをよりよく理解するにつれて、特定の概念が変化し、他の概念が出現し、他の概念が廃棄されます。あなたIPopUpServiceは、概念的には、狭く専門化されたインターフェースです。これは、ロバート・マーチンが出力ポートと呼ぶものです。それに依存することなく、より低いレベルのサービスを呼び出すことができる抽象化。ただし、この特定の抽象化は、それほど抽象的ではありません。これ自体は、特にこの段階では悪いことではありません。しかし、それは、標準的な仮定が、ある種のGUIが関係することになるということを意味します。
さて、永続層のクラスが実装されたIPopUpService場合、それはかなり奇妙なことになるでしょう、私は同意します。続行する前に、何かを明確にするためだけに。あなたは言った:
「突然、永続層の[IPopUpService]にアクセスできるようになりました。」
それはあなたが持っていることはありませんアクセスではなく、あなたがそれへの依存性を持っている、とあなたが使用するアプリケーション層のための実装を提供している、それまでに。問題は、あなたが狭く専門化されたインターフェースを取り、名前を変更したり再概念化したりすることなく、より広い目的を与えたことです。
今のところGUIの側面を忘れた場合、IPopUpService実際に行うことは、これらの線に沿ったものです。それは、ビジネスロジック関連の結果(成功、失敗など)を示す信号を外層に送信します。そして、私は「シグナルを送信する」という意味です。なぜなら、アプリケーション層は実際には抽象型の変数でメソッドを呼び出すだけだからです。副作用(ポップアップが表示されている)は、プレゼンテーション層で発生します。
ただし、これらのシグナルには独自のビジネス関連性がある可能性があるため、ポップアップだけでなく、それ以上の結果が表示されるようにすることもできます。たとえば、イベントのログを作成し、それをファイルやデータベーステーブルに保存したい場合があります。したがって、このインターフェイスを(IOperationResultConsumer1のようなものに)一般化して、プレゼンテーション、データベース、およびインフラストラクチャレイヤーがそれぞれ独自のバージョン(ポップアップを表示するため、もう1つ)を実装して、これらすべてのシナリオで使用できるようにすることができます。イベントまたはログエントリを保存するための2つ)。
または、他の要因や制約に基づいて、これらのものに対して個別のインターフェイスを使用する方が理にかなっていると判断する場合もあります。たとえば、ロジックに微妙な違いがあり、簡単に一般化できない場合があり、統合インターフェイスのアプローチがより複雑になります。
要約すると、私の2つの主要なポイントは、(1)システムのレイヤードビューは非常に高レベルのビューであり、それを超えて、レイヤー内のコードをどのように編成するかを意識し、依存関係、および(2)最初に思いついた抽象化は、必ずしも最終的に得られるものではありません。時間の経過とともに、ある程度の改良が行われる(または行われるはずです)。
1これ以上の名前のATMは考えられませんでした。