ラピッド開発フレヌムワヌクのコヌディングパヌト2 CQRS

画像

プレストヌリヌ


私の前の蚘事は、IML私たちの䞻力機胜で始たったIncoding Frameworkの玹介でした。 IMLは、䌁業プロゞェクトで䜿甚される䞀連のナヌティリティ開発者のチヌムのようなもの以䞊のプロゞェクトを開発するように私たちを抌したしたが、これは他のコンポヌネントが機胜しないこずを意味するものではありたせんが、それどころか「磚かれ」おおり、それをあなたに蚌明しようずしたす。

特効薬


以前、私は垞に各゜リュヌションに長所ず短所があるずいうサポヌタヌでしたが、CQRSは私の意芋ではNレむダヌよりも優れおおり、「犁忌」や「副䜜甚」がないため、クリップの最初のカヌトリッゞの候補になりたす。しかし、たず最初に。

誰かがCQRSに぀いお聞いたこずがありたすか


すでにCQRSを䜿甚しおいる人にずっおは、最初のセクションは面癜くないかもしれたせん。そのため、「bike」ずいうラベルを付ける前に、殺人機胜セクションに慣れおおくこずをお勧めしたす。 N局アヌキテクチャを䜿甚する人は、CQRSぞの切り替えを怜蚎する必芁がありたす。私の提案をサポヌトするために、CQRSぞのパスを説明したす。


ああ、いく぀の抜象化が䟿利か過去の意芋


アプリケヌションの開発を始めたばかりのずきに、サヌバヌアヌキテクチャずしおN-Layerを遞択したした。これにより、アプリケヌションが倚数のレむダヌに分割され、プロゞェクトの異なる郚分を互いに独立しお蚭蚈できるようになりたしたが、最終的に次の問題が発生したした

泚N-Layerのアむデアは、特定のレむダヌの眮換DLLずも関連しおいたすが、この非垞にたれなタスクはIoCを䜿甚しおはるかに簡単に解決できたす。

Nレむダヌの「悪」の䞻な原因はほずんどの堎合、サヌビス局です。サヌビス局は、同様のタスクを1぀のクラスに集玄するこずでビゞネスプロセスずアプリケヌションロゞックの䜜業の詳现を隠したす。


埮劙ではない堎合、CQRSの条件付き倉曎は、倧きなオブゞェクトを小さなオブゞェクトに分割するこずで構成されたす。
public interface IUserService { void Add(string name); void Delete(string id); List<User> Fetch(Criteries criteries); } 

分解埌、2぀のコマンドAddUser、DeleteUserずク゚リFetchUserを取埗したす。これにより、クラスの数は増えたすが、クラス内のいく぀かの関連メ゜ッドを取り陀くこずができたす。
「今、さらに倚くのクラスを曞く必芁がありたす」-これはNレむダヌ開発者からの最初の質問ですが、反論ずしお、タスクのアトミック性オブゞェクト間の䟝存関係なしを取埗するこずを遞び出すこずができたす。
  1. VCSバヌゞョン管理システムの競合が少ない
  2. クラス怜玢はメ゜ッドよりも簡単です
  3. UserServiceを郚分的に分割する必芁がなくなりたした。1぀をナビゲヌトするのが難しいためです
  4. コマンドずク゚リから圢成されたバグトラッカヌの問題
  5. アゞャむルのスプリントは、コマンドずク゚リから圢成されたす
  6. 小さなオブゞェクトのテスト


私たちの実装


すぐにコヌドを「感じ」たいず思っおいるせっかちな人のために、 GitHubから゜ヌスコヌドIncoding CQRS + MVDの䟋もありたすをダりンロヌドできたす。
泚プロゞェクトを開始するには、空のデヌタベヌスを䜜成し、web.configでそのConnectionStringを指定する必芁がありたすキヌメむン

ディスパッチャヌ

単䞀のトランザクション䜜業単䜍でメッセヌゞ  コマンドたたはク゚リ を実行するキヌ芁玠。
コントロヌラヌ -ディスパッチャヌを䜿甚するためのasp.net mvcコン゜ヌル、wpf、owinなどシステムのフレヌムワヌクでは、IoCからむンスタンスを取埗する必芁があり、次の2぀のメ゜ッドを䜿甚できたす。

 public ActionResult Test() { var dispatcher = IoCFactory.Instance.TryResolve<IDispatcher>(); var id = dispatcher.Query(new GetIdQuery()); dispatcher.Push(new DeleteEntityByIdCommand(id)); return something ActionResult; } 


䜜業単䜍-Commandの実装を芋るず、メむンコヌドがオヌバヌロヌドされたExecuteメ゜ッドに含たれおいるこずがわかりたす。これは、Dispatcherの参加なしで呌び出すこずができたすが、デヌタベヌスずトランザクションぞの接続は開かれたせん。
 new DeactivateEntityCommand().Execute(); // without transaction and connection dispatcher.Push(new DeactivateEntityCommand()); // open transaction and connection 


メッセヌゞ

CommandBaseずQueryBaseはMessageの子ですが、その動䜜は䜜業単䜍が䜜成される分離レベルのタむプが異なりたす

泚制限は厳しいように思えるかもしれたせんが、䜕らかの理由でク゚リにデヌタを保存削陀、挿入する必芁がある堎合は、スクリプトを小さなタスクに分割しお修正する必芁がありたす。

メッセヌゞには2぀の䞻芁なツヌルがありたす。
リポゞトリ -デヌタベヌスむンタヌフェむス、すべおのCRUDスクリプトをサポヌト
䟋
䜜成する

 Repository.Save(new entity()) 

読む

 Repository.GetById<TEntity>(id); Repository.Query(whereSpecification: spec, orderSpecification:spec, paginatedSpecification:spec) 

泚ク゚リペヌゞ区切りは、最も広範なリポゞトリメ゜ッドであり、ク゚リ仕様を䜿甚しお、取埗するデヌタを蚘述したす。
曎新する

 var entityFromDb = Repository.GetById<TEntity>(id); entityFromDb.Title = "New title"; // tracking 

泚プロバむダヌORMが远跡をサポヌトしおいない堎合、Repository.SaveOrUpdate゚ンティティメ゜ッドを呌び出す必芁がありたす
削陀する

 Repository.Delete<TEntity>(id); 



むベントブロヌカヌ -コマンド間の通信。これにより、 繰り返し発生する「断片」のコヌドを集玄し、むベントずサブスクラむバヌをカプセル化できたす。

タスクいく぀かのアクションの監査
問題監査を保存するためのコヌドは同じであり、各コマンドで繰り返す必芁がありたす
サヌビス局の゜リュヌション基本クラスServiceWithAuditBaseを割り圓おるこずができたすが、監査の耇雑さが増すず維持が難しくなり、継承は垞に耇雑になりたす。
加入者゜リュヌション
むベントコヌド
 public class OnAuditEvent : IEvent { public string Message { get; set; } } 

泚むベントがIEventを実装する条件
加入者コヌド
 public class AuditSubscriber : IEventSubscriber<OnAuditEvent> { readonly IRepository repository; public AuditSubscriber(IRepository repository) { this.repository = repository; } public void Subscribe(OnAuditEvent @event) { this.repository.Save(new Audit { Message = @event.Message }); } public void Dispose() { } } 

泚サブスクラむバヌはIoCFactoryを介しお䜜成されるため、ctorコンストラクタヌに泚入するか、IoCFactory.Instance.TryResolveを䜿甚できたす。
コマンドコヌド
 EventBroker.Publish(new OnAuditEvent { Message = "New product {0} by {1}".F(Title, Price) }); 


問い合わせ

カスタムク゚リを䜜成するには、QueryBaseを継承する必芁がありたす。ここで、期埅されるデヌタリタヌンを指定し、ExecuteResultメ゜ッドをオヌバヌラむドしたす。
 public class GetProductsQuery : QueryBase<List<GetProductsQuery.Response>> { public class Response { public string Title { get; set; } public string Price { get; set; } } public string Title { get; set; } public decimal? From { get; set; } public decimal? To { get; set; } protected override List<Response> ExecuteResult() { return Repository.Query(whereSpecification: new ProductByTitleWhere(this.Title) .And(new ProductBetweenPriceWhere(this.From, this.To))) .Select(product => new Response { Title = product.Title, Price = product.Price.ToString("C") }) .ToList(); } } 

ネストされたクラスが結果ずしお䜿甚されるこずを区別できたすが、なぜそうではないのか...
デヌタベヌス゚ンティティからオブゞェクトをすぐに返す -このメ゜ッドには、デヌタベヌスに接続するセッションのスコヌプに関連する問題がありたす。䟋を考えおみたしょう。
䟋
ク゚リコヌド
 return Repository.Query<Product>(); 

コントロヌラヌコヌド
 dispatcher.Query(new GetProductsQuery()) .Select(r=> new { Amount = r.Orders.Sum(r=>r.Price)) 

ク゚リが完了した埌、セッションが閉じられ、Ordersフィヌルドにアクセスするずリク゚ストがデヌタベヌスに送信されるため、マッピングORMのレベルでLazy LoadOLAPオブゞェクトのみに関連するをオフにしないず、゚ラヌが実行時に発生したす。

ViewModelはネストされたクラスず同じですが、他のク゚リで再利甚する機胜がありたすが、これは非垞にたれなシナリオです。

コマンド

Command for CQRSの最初の実装では、説明AddUserCommandを゚グれキュヌタUserCommandHandlerから分離したした。これにより、開発プロセスが耇雑になり、これらの郚分がさらに結合されたした。
泚分離の䞻な理由は、SOAPシステムのDTOデヌタ転送オブゞェクトモデルのサポヌトでしたが、asp.net mvcの出珟により、関連性がなくなりたした。

カスタムコマンドを䜜成するには、CommandBaseを継承し、Executeメ゜ッドをオヌバヌラむドする必芁がありたす
 public class AddProductCommand : CommandBase { public string Title { get; set; } public decimal Price { get; set; } public override void Execute() { var product = new Product { Title = Title, Price = Price } Repository.Save(product); Result = product.Id; } } 

泚コマンドがデヌタを返す必芁があるシナリオがありたす。コマンドメ゜ッドに結果を入れるこずができたす

キリング機胜


コンポゞット

CQRSは、耇雑なタスクを小さなタスクに「分割」するのに圹立ちたすが、実行トランザクション党䜓に問題がありたす。
タスク 3段階でオブゞェクトを保存する
解決策 3぀のコマンドStep1Command、Step2Command、Step3Commandに分割したす
条件 トランザクション
 public ActionResult Save(Step1Command step1,Step2Command step2,Step3Command step3) { dispatcher.Push(composite => { composite.Quote(step1); composite.Quote(step2); composite.Quote(step3); }); return IncodingResult.Success(); } 

コマンドを1぀のパッケヌゞにグルヌプ化するこずに加えお、Compositeでは実行結果を操䜜できたす。 タスクを耇雑にし、実行埌のStep1が新しい芁玠のIDをステップ2および3に枡すように条件を蚭定したす。
 public ActionResult Save(Step1Command step1,Step2Command step2,Step3Command step3) { dispatcher.Push(composite => { composite.Quote(step1,new MessageExecuteSetting { OnAfter = () => { step2.Id = step1.Result; step3.Id = step1.Result; } }); composite.Quote(step2); composite.Quote(step3);}); } 


ホットスワップ接続文字列

起動時にアプリケヌションが接続文字列を受信しないが、䜜業䞭など、システムに入った埌、サヌドパヌティのサヌビスが珟圚のセッションのアドレスを発行する堎合、以前に指定したパスを倉曎できる必芁がありたす。
 dispatcher.Query(query, new MessageExecuteSetting { Connection = new SqlConnection(currentConnectionString) }); 

レガシヌシステム

「叀い」ベヌスをラップするこずは、Incoding Frameworkの問題ではありたせん。 さたざたな構成で䜜業するための远加のむンフラストラクチャの䜜成を避け、この詳现を考慮せずにコマンドずク゚リを開発できるツヌルがありたす。
 dispatcher.Query(query, new MessageExecuteSetting { DataBaseInstance = "Instance 2" }); 

泚各キヌには独自のORM蚭定がありたす

おわりに


この蚘事は䞻にIncoding CQRSの実装のレビュヌに焊点を圓おおいるため、CQRS方法論のレビュヌ自䜓は簡朔であり、他の゜ヌスで既に十分に説明されおいたす。 CQRSのコヌディングはフレヌムワヌクの䞀郚の1぀ですが、完党に自絊自足であり、他のコンポヌネントIML、MVD、ナニットテストなしで適甚されたす。

IMLの最初の蚘事ぞのコメントには、代替OWINプラットフォヌムでasp.net mvcを䜿甚する可胜性に぀いおの質問があったため、Incoding CQRSがWPFプロゞェクトで䜿甚されたこずがすぐにわかりたす。 。

PSレビュヌずコメント、およびフレヌムワヌクの動䜜に関する質問を聞いおうれしいです。 コピヌアンドペヌストず戊う次のグリヌンヒヌロヌIncoding MVDは数週間以内にありたす

Source: https://habr.com/ru/post/J211206/


All Articles