インスタントデザイン

人々はJava用に書かれた古い本から建築を学びます。 これらの本は優れていますが、当時の楽器を使用して、当時の問題を解決しています。 時間は変わり、C#はJavaよりも軽いScalaに似ており、新しい優れた本はほとんどありません。



この記事では、良いコードと悪いコードの基準、測定方法と測定対象を調べます。 典型的なタスクとアプローチの概要を見て、長所と短所を分析します。 最後に、Webアプリケーションを設計するための推奨事項とベストプラクティスがあります。



この記事は、DotNext 2018モスクワ会議からの私のレポートの転写です。 テキストに加えて、ビデオとカットの下のスライドへのリンクがあります。







サイトの スライドレポートページ
私について簡単に説明すると、私はカザン出身で、ハイテクグループで働いています。 私たちはビジネス向けのソフトウェアを開発しています。 最近、私は企業ソフトウェア開発と呼ばれるカザン連邦大学でコースを教えています。 時々、エンジニアリングの実践、エンタープライズソフトウェアの開発に関するHabrに関する記事を執筆しています。



ご想像のとおり、今日はエンタープライズソフトウェアの開発、つまり最新のWebアプリケーションの構築方法について説明します。





基準



基準を策定します。 デザインについて話すのが「私のカンフーはあなたのカンフーより強い」というスタイルの話をするとき、私は本当にそれが好きではありません。 ビジネスには、原則として、お金と呼ばれる特定の基準があります。 時は金なりということは誰もが知っているので、これらの2つのコンポーネントが最も重要です。







だから、基準。 原則として、企業は「単位時間あたりできるだけ多くの機能」を要求することが最も多くありますが、1つの注意事項があります。これらの機能は機能するはずです。 そして、それが壊れる可能性のある最初のステップはコードレビューです。 つまり、プログラマーは「3時間以内にやる」と言ったようです。 3時間が経過すると、レビューがコードに反映され、チームリーダーは「ああ、いいえ、やり直してください」と言いました。 さらに3つあります。コードレビューで合格した反復の数ですので、3時間を掛ける必要があります。



次のポイントは、受け入れテストの段階からの戻りです。 同じこと。 この機能が機能しない場合は、機能しません。これらの3時間は1週間、2分間-よくあることです。 最後の基準は、回帰とバグの数ですが、それでもテストと受け入れにもかかわらず、実稼働を経ました。 これも非常に悪いです。 この基準には1つの問題があります。 リポジトリに何かをプッシュするという事実と、2週間後に何かが壊れたという事実との関係を追跡するのは難しいため、追跡するのは困難です。 ただし、それでも可能です。



アーキテクチャ開発



むかしむかし、プログラマーがプログラムを書き始めたばかりだったとき、まだアーキテクチャはなく、誰もが好きなことをすべてやりました。







したがって、私たちはそのような建築スタイルを得ました。 ここでは「ヌードルコード」と呼ばれ、海外では「スパゲッティコード」と言います。 すべてがすべてと結びついています。ポイントAで何かを変えています-ポイントBで壊れます。何が何と結びついているのかを理解することは完全に不可能です。 当然、プログラマはこれが機能しないことをすぐに認識し、いくつかの構造を作成する必要があり、いくつかのレイヤーが役立つと判断しました。 さて、ミンスミートがコードであり、ラザニアがそのようなレイヤーであると想像するなら、ここにレイヤードアーキテクチャの図を示します。 ひき肉は細かく刻まれたままですが、今ではレイヤー1のひき肉はそれを取り込んでレイヤー2のひき肉と話をすることはできません。コードに何らかの形を与えました:写真でさえ、登山がより枠組されていることがわかります。







おそらく誰もが古典的な階層化アーキテクチャに精通しているでしょう。UI、ビジネスロジック、データアクセスレイヤがあります。 会社を辞めた建築家にちなんで名付けられたあらゆる種類のサービス、ファサード、レイヤーがまだありますが、それらの数に制限はありません。







次の段階は、いわゆるタマネギのアーキテクチャでした。 大きな違いがあるように思われます。その前に小さな正方形があり、ここに円がありました。 まったく違うようです。







そうでもない。 全体的な違いは、その時点でSOLIDの原則が定式化されたことであり、古典的なタマネギでは、何らかの理由で抽象ドメインコードが実装、データアクセスに依存するため、依存関係の逆転に問題があることが判明したため、データアクセスを展開することにしました、およびデータアクセスがドメインに依存するようにします。







ここで私はドローイングを練習し、タマネギのアーキテクチャを描きましたが、古典的な「リング」ではありません。 多角形と円の間に何かを得ました。 これは、「タマネギ」、「六角形」、または「ポートとアダプター」という言葉に出会った場合、それがすべて同じであることを示すためだけに行いました。 ポイントは、ドメインが中心にあり、サービスにラップされていることです。必要に応じて、ドメインまたはアプリケーションサービスにすることができます。 そして、DALが移動したUI、テスト、インフラストラクチャの形の外の世界-彼らはこのサービス層を通してドメインと通信します。



簡単な例。 メール更新



このようなパラダイムで、ユーザーのメールアドレスを更新するという単純なユースケースがどのように見えるかを見てみましょう。







リクエストを送信し、検証し、データベースの値を更新し、新しいメールに通知を送信する必要があります。「すべてが正常であり、メールを変更しました。すべて問題ありません」、「200」ブラウザに返信します。







コードは次のようになります。 ここには、標準のASP.NET MVC検証があり、データを読み取り、更新するORMがあり、通知を送信する何らかの電子メール送信者がいます。 すべてが良いようですね。 1つの注意点-理想的な世界で。



現実の世界では、状況はわずかに異なります。 ポイントは、承認、エラーチェック、フォーマット、ログ、およびプロファイリングを追加することです。 これはすべて、ユースケースとは関係ありませんが、すべてそうでなければなりません。 そして、その小さなコードは大きくて恐ろしいものになりました。ネストが多く、コードが多く、読み取りが難しく、最も重要なことは、ドメインコードよりもインフラストラクチャコードの方が多いという事実です。







「サービスはどこですか?」とあなたは言います。 すべてのロジックをコントローラーに書き込みました。 もちろん、これは問題です。今はサービスを追加しますが、すべて問題ありません。







サービスを追加すると、本当に良くなります。大きな足布の代わりに、小さな美しいラインが1つあるからです。



良くなっていますか? なっています! そして、このメソッドを異なるコントローラーで再利用できるようになりました。 結果は明らかです。 このメソッドの実装を見てみましょう。







しかし、ここではすべてがそれほど良くありません。 このコードはまだここにあります。 すべてを同じようにサービスに移しました。 問題を解決するのではなく、単にそれを偽装して別の場所に転送することにしました。 以上です。







これに加えて、いくつかの他の質問が発生します。 コントローラーまたはここで検証を行う必要がありますか? まあ、コントローラーのようなものです。 また、データベースにアクセスして、そのようなIDがあるかどうか、またはそのような電子メールを持つ他のユーザーがいないことを確認する必要がある場合はどうでしょうか。 うーん、それではサービスで。 しかし、ここでのエラー処理は? このエラー処理はおそらくここにあり、コントローラーのブラウザーに応答するエラー処理です。 SaveChangesメソッドは、サービス内にありますか、それをコントローラーに転送する必要がありますか? 1つのサービスが呼び出された場合、そのサービスを呼び出す方が論理的であり、コントローラー内に呼び出す必要のある3つのサービスメソッドがある場合、これらのサービスの外部で呼び出してトランザクションが1つになるようにする必要があるためです これらの反射は、おそらくレイヤーが問題を解決しないことを示唆しています。







そして、この考えは複数の人に起こりました。 Googleを使用する場合、これらの立派な夫のうち少なくとも3人が同じことについて書いています。 上から下へ: シンプルインジェクター IoCコンテナーの作成者であるStephen .NET Junkie(残念ながら、彼はインターネット上のどこにも表示されないため、彼の姓を知りません)。 次のジミー・ボガードはAutoMapperの著者です。 そして、下にあるのは、楽しみと利益のためF#の著者であるScott Vlashinです。







これらすべての人々は同じことについて話し、層に基づいてではなく、ユースケース、つまりビジネスが私たちに求めている要件に基づいてアプリケーションを構築することを提案します。 したがって、C#のユースケースは、IHandlerインターフェイスを使用して決定できます。 入力値があり、出力値があり、このユースケースを実際に実行するメソッド自体があります。







そして、このメソッドの内部には、ドメインモデル、または読み取り用の非正規化モデルがあります。何かを探す必要がある場合、DapperまたはElastic Searchを使用できます。 -ストアドプロシージャを備えたシステム-問題なく、ネットワーク要求も-一般に、そこに必要なものはすべてあります。 しかし、レイヤーがない場合はどうすればいいですか?







開始するには、UserServiceを削除しましょう。 メソッドを削除してクラスを作成します。 そして削除し、再度削除します。 そして、クラスを取得して削除します。







考えてみましょう、これらのクラスは同等ですか? GetUserクラスはデータを返し、サーバー上の何も変更しません。 これは、たとえば、「Give me the user ID」というリクエストに関するものです。 UpdateEmailおよびBanUserクラスは、操作の結果を返し、状態を変更します。 たとえば、サーバーに「状態を変更してください。何かを変更する必要があります」と伝えた場合。







HTTPプロトコルを見てみましょう。 HTTPプロトコルの仕様に従って、サーバーの状態を変更せずにデータを返すGETメソッドがあります。







また、サーバーの状態を変更し、操作の結果を返すことができる他のメソッドがあります。







CQRSパラダイムは、HTTPプロトコル用に特別に設計されているようです。 クエリはGET操作であり、コマンドはPUT、POST、DELETEです。何も発明する必要はありません。







ハンドラを再定義し、追加のインターフェイスを定義します。 IQueryHandlerは、入力値のタイプがIQueryであるという制約を掛けたという点でのみ異なります。 IQueryはマーカーインターフェイスであり、このジェネリックのみです。 QueryHandlerに制約を設定するにはジェネリックが必要です。QueryHandlerを宣言すると、QueryではなくQueryオブジェクトを渡すことができますが、Queryオブジェクトをそこに渡すと、その戻り値がわかります。 これは、インターフェイスが1つしかない場合に便利です。そのため、コード内で実装を探す必要がなく、また混乱しないようにする必要があります。 IQueryHandlerを記述し、そこで実装を記述します。TOutでは、別のタイプの戻り値に置き換えることはできません。 コンパイルしません。 したがって、どの入力値がどの入力データに対応するかをすぐに確認できます。







CommandHandlerの状況は1つ例外がありますが、このジェネリックはもう1つのトリックに必要です。これについてはもう少し詳しく見ていきます。



ハンドラーの実装



ハンドラー、私たちが発表した、それらの実装は何ですか?







問題はありますか? 何かが失敗したようです。



デコレータは救助に急ぎます



しかし、それは役に立たなかった。まだ道の真っin中にいるので、もう少しファイナライズする必要があり、今回はデコレーターパターン、つまりその素晴らしいレイアウト機能を使用する必要がある。 デコレータは、デコレータに包まれたり、デコレータに包まれたり、デコレータに包まれたりします。退屈するまで続けてください。







次に、すべてが次のようになります。入力Dtoがあり、最初のデコレータ、2番目、3番目に入り、ハンドラに移動して終了し、すべてのデコレータを通過して、ブラウザでDtoを返します。 後で継承するために抽象基本クラスを宣言し、Handlerの本体をコンストラクターに渡し、追加のデコレーターロジックがハングする抽象Handleメソッドを宣言します。







これで、デコレータの助けを借りて、パイプライン全体を構築できます。 チームから始めましょう。 何があったの? 入力値、検証、アクセス制御、ロジック自体、このロジックの結果として発生するいくつかのイベント、および戻り値。







検証から始めましょう。 デコレータを宣言します。 これらのすべてを実行し、検証が失敗し、戻り値の型がIEnumerable<validationresult>



であるかどうかを確認してから、型が一致するため返すことができます。 そして、それが他のハンドラーである場合は、例外をスローする必要があります。ここには結果がなく、別の戻り値の型があります。







次のステップはセキュリティです。 また、デコレータを宣言し、CheckPermissionメソッドを作成して検証します。 突然何かがうまくいかなかった場合、それだけです、続行しません。 これで、すべてのチェックを完了し、すべてが正常であることを確認した後、ロジックを実行できます。



プリミティブへの執着



ロジックの実装を示す前に、少し前に、つまりそこに来る入力値から始めたいと思います。







さて、このようなクラスを選択すると、ほとんどの場合、このようになります。 少なくとも私が日常業務で目にするコード。







検証が機能するように、検証の種類を示すいくつかの属性をここに追加します。 これはデータ構造の面では役立ちますが、データベースの値をチェックするなどの検証には役立ちません。 それは単なるEmailAddressであり、データベースに移動するためにこれらの属性をどのように使用するかを確認する方法、場所は明確ではありません。 属性の代わりに、特別なタイプに切り替えると、この問題は解決します。







int



プリミティブの代わりに、intキーを持つ特定のエンティティであるジェネリックを持つId型を宣言します。 そして、このエンティティをコンストラクタに渡すか、そのIDを渡しますが、同時に、Idが取得して返すことができる関数を渡す必要があります。







電子メールでも同じことを行います。 すべてが同じように見えるように、すべてのメールを最終行に変換します。 次に、Email属性を取得し、ASP.NET検証との互換性のために静的として宣言し、ここでは単にそれを呼び出します。 つまり、これも実行できます。 ASP.NETインフラストラクチャでこれらすべてをキャッチするには、シリアル化やModelBindingをわずかに変更する必要があります。 そこには多くのコードはありません。それは比較的単純なので、そこで止まりません。







これらの変更後、プリミティブ型ではなく、特殊な型がここに表示されます:IdおよびEmail。 そして、これらのModelBinderと更新されたデシリアライザが機能した後、これらの値が正しいことを確実に知ることができます。そのような値がデータベースにあることも含まれます。 「不変式」







次に説明するポイントは、クラス内の不変条件の状態です。これは、多くの場合、クラス、多くのゲッターセッターのみが存在する貧血モデルが使用されるためです。 複雑なビジネスロジックを扱うため、コードが自己文書化されていることが重要です。 代わりに、ORMの空と共に実際のコンストラクタを宣言することをお勧めします。アプリケーションコード内のプログラマがそれを呼び出せないように保護されていると宣言できます。 ここでは、プリミティブ型ではなく、電子メール型を渡します。それは既に正しく正しいものです。nullの場合、例外をスローします。 いくつかのFody、PostSharpを使用できますが、C#8がまもなく登場するため、Null不可の参照型が存在するため、言語でのサポートを待つことをお勧めします。 次の瞬間、名前と姓を変更したい場合、おそらくそれらを一緒に変更したいので、それらを一緒に変更する適切なパブリックメソッドが必要です。







このパブリックメソッドでは、これらの行の長さがデータベースで使用するものと一致することも確認します。 そして、何かが間違っている場合、実行を停止します。 ここでは、同じトリックを使用します。 特別な属性を宣言し、アプリケーションコードで呼び出します。







さらに、そのような属性はDtoで再利用できます。 さて、名前と姓を変更したい場合、そのような変更コマンドがあります。 ここに特別なコンストラクタを追加する価値はありますか? それは価値があるようです。 それは良くなり、誰もこれらの値を変更せず、それらを壊さず、正確になります。







実際にはそうではありません。 実際、Dtoは実際にはオブジェクトではありません。 これは、逆シリアル化されたデータを格納する辞書です。 つまり、オブジェクトのふりをすることはもちろんですが、責任は1つだけです。それは、シリアル化と非シリアル化です。 この構造に対処しようとすると、デザイナーとModelBinderの発表を開始します。このようなことは非常に面倒で、最も重要なことは、新しいフレームワークの新しいリリースで中断します。 これはすべて、マークサイモンによる記事「プログラムの境界はオブジェクト指向ではありません」でよく説明されています。興味深い場合は、彼の投稿を読むほうが良いでしょう。そこで詳しく説明されています。







つまり、ダーティな外部世界があり、入力にチェックを入れ、クリーンなモデルに変換してから、すべてをシリアル化、ブラウザ、ダーティな外部世界に戻します。



ハンドラー



これらの変更がすべて行われた後、Handerはどのように表示されますか?







ここでは、読みやすくするために2行を書きましたが、一般的には1行で書くことができます。 型システムがあり、検証があるため、データは正確です。つまり、データは鉄筋コンクリートであり、再度チェックする必要はありません。 そのようなユーザーも存在し、そのような忙しい電子メールを持つ他のユーザーはいません、すべてを行うことができます。 ただし、SaveChangesメソッドへの呼び出しはなく、通知もありませんし、ログとプロファイラーもありませんよね? 先に進みます。



イベント



ドメインイベント。







おそらく、この概念が彼の投稿「ドメインイベント-救い」でウディダハンによって広まったのは初めてでしょう。 そこで彼は、Raiseメソッドを使用して静的クラスを宣言し、そのようなイベントをスローすることを提案しています。 少し後に、ジミーボガードは「A better domain events pattern」と呼ばれるより良い実装を提案しました。







Bogardのシリアル化を1つの小さな変更で示しますが、重要です。 イベントをスローする代わりに、リストを宣言できます。また、何らかの反応が発生する場所で、これらのイベントを保存するエンティティ内で直接宣言できます。 この場合、このemail



getterはUserクラスでもあり、このクラスはこのプロパティがgetterおよびsetterを持つプロパティのふりをするのではなく、実際にこれに何かを追加します。 つまり、これは冒encapsulationではなく、実際のカプセル化です。 変更するとき、電子メールが異なることを確認し、イベントをスローします。 このイベントはまだどこにも到達しておらず、エンティティの内部リストにのみ含まれています。







さらに、SaveChangesメソッドを呼び出す時点で、ChangeTrackerを使用し、ドメインイベントがあるかどうかにかかわらず、インターフェイスを実装するエンティティがあるかどうかを確認します。 そして、もしあれば、これらすべてのドメインイベントを取得し、それらをどう処理するかを知っているディスパッチャに送信しましょう。



このディスパッチャの実装は別の議論のトピックであり、C#での複数のディスパッチにはいくつかの困難がありますが、これも行われています。 このアプローチには、別の非自明な利点があります。 2人の開発者がいる場合、1人はこの電子メールを変更するコードを記述でき、もう1人は通知モジュールを作成できます。 それらは絶対に相互に接続されておらず、異なるコードを記述し、1つのDtoクラスのこのドメインイベントのレベルでのみ接続されています。 最初の開発者はある時点でこのクラスを単に破棄し、2番目の開発者はそれに反応し、通常発生するユーザー設定を考慮して、電子メール、SMS、電話へのプッシュ通知、およびその他すべての100万件の通知で送信する必要があることを認識しています。







これが最小ですが重要なポイントです。 Jimmyの記事ではSaveChangesメソッドのオーバーロードを使用していますが、そうしないのが最善です。 また、SaveChangesメソッドをオーバーロードし、HandlerにdbContextが必要な場合は、循環依存関係を取得するため、デコレーターで実行する方が適切です。 これで作業できますが、ソリューションは少し便利でなく、少し美しくありません。 したがって、パイプラインがデコレータ上に構築されている場合、それを別に行う理由はありません。



ロギングとプロファイリング







コードのネストは残りましたが、最初の例では、最初にMiniProfilerを使用し、次にcatchを試し、次にifを試しました。 合計で3つのレベルのネストがありましたが、このレベルのネストはそれぞれ独自のデコレーターにあります。 そして、プロファイリングを担当するデコレーターの内部には、ネストのレベルが1つしかないため、コードは完全に読み取られます。 さらに、これらのデコレーターには1つの責任しかないことは明らかです。 デコレータがロギングを担当している場合、プロファイリングの場合、プロファイルのみの場合、他のすべてが他の場所にある場合にのみログに記録します。



応答



パイプライン全体が機能した後、Dtoを取得してブラウザーにさらに送信し、JSONをシリアル化することしかできません。







しかし、もう1つ小さなことがあります。このようなことは時々忘れられます。すべての段階で例外が発生する可能性があり、実際には何らかの方法でそれらを処理する必要があります。







Scott Vlashinと彼のレポート「鉄道指向プログラミング」に再び言及するしかありません。 なんで? 元のレポートは、F#言語でのエラーの処理、フローをわずかに異なる方法で整理する方法、およびException'ovを使用するよりもこのようなアプローチが望ましい理由を完全に扱っています。 F#では関数型言語であり、スコットは関数型言語の機能を使用するため、これは非常にうまく機能します。







おそらく、ほとんどの人はまだC#で​​書いているので、C#でアナログを書くとこのアプローチは次のようになります。 例外をスローする代わりに、成功したブランチと失敗したブランチを持つクラスResultを宣言します。 したがって、2人のデザイナー。 クラスは1つの状態のみになります。 このクラスは、ユニオン型の特殊なケースであり、F#から識別されたユニオンですが、C#には組み込まれていないため、C#で書き直されます。







コード内で誰かがnullをチェックしない可能性があることをパブリックgetterを宣言する代わりに、パターンマッチングが使用されます。 繰り返しになりますが、F#では組み込みのパターンマッチング言語になります。C#では、操作の成功結果をどう処理するか、チェーンをさらに下に変換する方法、およびエラーのある関数を1つの関数に渡す別のメソッドを記述する必要があります。 つまり、どのブランチが機能していたかに関係なく、これを1つの返された結果にキャストする必要があります。 F#では、これは非常にうまく機能します。これは、機能的な構成が存在するためです。 .NETでは、複数の結果が得られるとすぐにこれは少し悪くなりますが、多くの-そしてほとんどすべてのメソッドが何らかの理由で失敗する可能性があります-結果の関数型のほとんどすべてが結果型になり、それらを必要とします何かを組み合わせるために。







それらを組み合わせる最も簡単な方法はLINQ使用することです。実際、LINQはIEnumerableだけでなく、SelectManyメソッドとSelectメソッドを正しい方法で再定義すると、C#コンパイラーはこれらの型にLINQ構文を使用できることがわかります。 一般に、Haskell do-notationを使用したトレーシングペーパーか、F#の同じ計算式を使用したトレーシングペーパーが作成されます。 これはどのように読むべきですか? ここに、操作の3つの結果があります。3つすべてのケースで問題がなければ、これらの結果r1 + r2 + r3を取得して追加します。 結果の値のタイプもResultになりますが、Selectで宣言する新しいResultです。 一般に、これは1つではないにしても、有効なアプローチです。







他のすべての開発者にとって、C#でそのようなコードを書き始めるとすぐに、このようなものに見え始めます。 「これらはひどい恐ろしい例外です。書いてはいけません! 彼らは悪です! 誰も理解できず、デバッグできないコードを書く方が良いでしょう!」







C#はF#ではなく、わずかに異なり、これが行われていることに基づいて異なる概念はありません。そして、フクロウをそのように地球上に引っ張ろうとすると、それは穏やかで珍しいことになります。







代わりに、文書化され、誰もが知っている、開発者の間で認知的不協和を引き起こさない組み込みの通常のツールを使用できます。 ASP.NETにはグローバルなハンドラー例外があります。







検証に問題がある場合は、コード400または422(処理不能なエンティティ)を返す必要があることを知っています。 認証と許可に問題がある場合、401と403があります。何かがうまくいかなかった場合、何かがうまくいきませんでした。 そして、何かがうまくいかず、ユーザーに正確に何を伝えたいのか、あなたの例外タイプを定義し、それがIHasUserMessageであると言って、このインターフェースでMessageゲッターを宣言し、チェックします:このインターフェースが実装されている場合、メッセージを受け取ることができます例外からJSONでユーザーに渡します。 このインターフェイスが実装されていない場合は、何らかのシステムエラーが発生します。ユーザーに何か問題が発生したことを伝えるだけで、既にそれを行っています。



クエリパイプライン



チームでこれを終了し、Read-stackの内容を確認します。 リクエスト、検証、レスポンス自体については、ほぼ同じです。個別に停止することはありません。 ここには追加のキャッシュがあるかもしれませんが、一般的にキャッシュにも大きな問題はありません。



セキュリティ



セキュリティチェックを見てみましょう。 このリクエストを行うことができるかどうかをチェックする同じセキュリティデコレータが存在する場合もあります。







ただし、複数のレコードを表示し、リストを表示する別のケースがあります。一部のユーザーには完全なリストを表示する必要があり(たとえば、一部のスーパー管理者)、他のユーザーには制限付きリストを表示する必要があります。 , , , , , .



問題は非常に簡単に解決されます . (IPermissionFilter), queryable queryable. , queryable, , where, : « , …» — , permission'. , , permission', , permissionFilter' , . permission', , . queryable dbContext, . permissionFilter' , permissionFilter' . permissionFilter, , .







ORM, , Global Filters entity-? , context -.



Query Pipeline. Read Model



. CQRS , Dto, .







C#, , , LINQ, - , , , , . LinqQueryHandler'. constraint : Query, , . - , Dto .







Handle , . , TQuery . , queryable extension AutoMapper'. - , AutoMapper LINQ, , Select, .



, . , DotNext, , , , , , expression' , .



SQL



先に進みます。 , DotNext', — SQL. Select , , , queryable- .







, . , Title, Title , . , . SubTitle, , , - , queryable- . , .



, . , , . , , . «JsonIgnore», . , , Dto. , , . JSON, , Created LastUpdated , SubTitle — , . , , , , , . , - .







. , -, , . , pipeline, . — , , . , SaveChanges, Query SaveChanges. , , , NuGet, .



. , - , , . , , , , , — . , , : « », — . .





, ?







- . .







, , , . MediatR , . , , — , MediatR pipeline behaviour. , Request/Response, RequestHandler' . Simple Injector, — .







, , , , TIn: ICommand.







Simple Injector' constraint' . , , , constraint', Handler', constraint. , constraint ICommand, SaveChanges constraint' ICommand, Simple Injector , constraint' , Handler'. , , , .



? Simple Injector MeriatR — , , Autofac', -, , , . , .



,



, «».







, «Clean architecture». .







- - , MVC, , .







, , , Angular, , , , . , : « — MVC-», : « Features, : , Blog - Import, - ».



, , , , MVC-, , - , . MVC . , , — . .











- , - -, .



-, , . , . , - , User Service, pull request', , User Service , . , - , - , . - , .



. , . , , , . , , , , , , , - . , ( , ), , «Delete»: , , . かなり快適です。



— «», , , , . , : , , , . , . , , . , , .



: . « », : , , . , , , , , , , . , . , - pull request , — , — - , . VCS : - , ? , - , , .







, , , . : . , . , , , , . , , , . , , . « », , . , , — , , .



: , - , . . - , , , , . - , - , , , , . .







まとめます。 , IHandler . .



IHandler ICommandHandler IQueryHandler , . , , . , CommandHandler, CommandHandler', .



なぜそう , Query , Query — . , , , Hander, CommandHandler QueryHandler, - use case, .



— , , , , : , .



, . , . , -.



C# 8, nullable reference type . , , , , .



ChangeTracker' ORM.



Exception' — , F#, C#. , - , - , . , , Exception', , LINQ, , , , , , Dapper - , , , .NET.



, LINQ, , permission' — . , , - , , . , — .



. リンクは次のとおりです。












— . . — «Domain Modeling Made Functional», F#, F#, , , , , . C# , , Exception'.



, , — «Entity Framework Core In Action». , Entity Framework, , DDD ORM, , ORM DDD .



広告の分。 15-16 2019 .NET- DotNext Piter, . , .



All Articles