CQRS + MediatR

George Kopadze
TBC Engineering
Published in
3 min readMar 23, 2021

CQRS (Command Query Responsibility Segregation) ტერმინი საზოგადოებას Greg Young-მა 2010 წელს გააცნო და აღწერს პატერნს სადაც მონაცემების წაკითხვა, მონაცემების შეცვლისგან არის განცალკევებული და დაფუძნებულია CQS (Command Query Seperation)-ის თეორიაზე. პატერნის გამოყენება საშუალებას გვაძლევს ლოგიკურად ან/და ფიზიკურად განვაცალკეოთ მონაცემების წაკითხვის ფუნქციონალი, მათი ცვლილების ფუნქციონალისგან. თუმცა ამგვარი დაყოფა ყველა ტიპის პროექტში არ არის გამართლებული და ღირებული, რადგან შესაძლოა ამ მიდგომამ პროექტების უმეტესობაში შემოიტანოს სარისკო კომპლექსურობა.

CQRS პატერნის გამოყენების მოტივაცია მჭიდროდ არის დაკავშირებული Event Sourcing-თან და Event-Driven არქიტექტურასთან მითუმეტეს თუ ჩვენი სისტემა შედგება მიკროსერვისებისგან.

აპლიკაციების უმრავლესობაში გვხვდება მონაცემთა ბაზასთან CRUD (Create, Read, Update, Delete) ოპერაციები. CRUD ოპერაციები CQRS პატერნში ითარგმნება შემდეგნაირად. CQRS პატერნი ყოფს მოვალეობებს 2 ნაწილად, Command -ები და Query-ები. Command-ების დანიშნულება არის მონაცემების მდგომარეობის შეცვლა, ხოლო Query-ების მონაცემების წაკითხვა.

ამგვარი დაყოფა გვაძლევს რამდენიმე გამოსარჩევ სარგებელს. მაგალითად მოვიყვან:

  • Query-ის მხარეს შეგვიძლია გვქონდეს განსხვავებული მონაცემთა საცავი (მაგ. არარელაციური), რომელიც უფრო მეტად იქნება ოპტიმიზირებული მონაცემების წაკითხვისთვის.
  • ფიზიკური განცალკევება Queries-ის და Command-ების უფრო ეფექტურად, რომ გავშალოთ დიდი მაშტაბზე. შეგვიძლია Query-ები ცალკე API -ში გვქონდეს და მაშტაბირება მოვახდინოთ მხოლოდ Query-ის API-ის. აპლიკაციის შრეზე კი გვექნება გაყოფილი Query-ები და Command-ები.
  • მონაცემებთან კავშირის ფრეიმვორკის აგნოსტიურობა. რაც გულისხმობს, რომ CQRS-ის გამოყენება საშუალებას გვაძლევს Query-ის ნაწილში გამოვიყენოთ მაგ. ADO.NET-ი (View, Stored Procedures, Table Valued Functions) და Command-ების მხარეს EntityFramework-ი.

ასეთი დაყოფისას ობიექტებს შორის კავშირებმა და დამოკიდებულებებმა რომ არ მიიღოს ქაოსის სახე, ამ პრობლემის მოგვარებაში გვეხმარება Mediator პატერნი.

Mediator არის ქცევითი დიზაინ პატერნი, რომლის გამოყენებაც ამცირებს ობიექტებს შორის დამოკიდებულებას. პატერნი ზღუდავს პირდაპირ კავშირს ობიექტებს შორის და აიძულებს ერთმანეთთან კომუნიკაციას mediator-ის ობიექტის გამოყენებით. განვიხილოთ CQRS-ის რეალიზაცია .NET-ში MediatR- ბიბლიოთეკის გამოყენებით.

იმისათვის რომ .NET Core-ის პროექტში დავაინიციროთ MediatR ბიბლიოთეკა, საჭიროა startup.cs ფაილში ჩავამატოთ MediatR-ის ინიცირების კოდის ფრაგმენტი.

იმისათვის რომ სადემონსტრაციო პროექტში სიმარტივე შევინარჩუნოთ ავიღოთ .NET Core -ზე Web API-ის ტიპის პროექტი და CQRS-ისთვის დამახასიათებელი ლოგიკურად დავყოთ ამავე პროექტში.

მაგალითი: პროექტში CQRS-ის პატერნით განაწილებული მოვალეობები

Command-ი გამოიყენება მონაცემების ცვლილებებისას. MediatR ბიბლიოთეკას გააჩნია ინტერფეისი IRequest<T> სადაც T-ს ნაცვლად ეთითება იმ ობიექტის ტიპი, რაც პასუხად უნდა დააბრუნოს Query-იმ ან Command-მა. Command-ის და შესაბამისი CommandHandler-ის ობიექტი გამოიყურება შემდეგნაირად:

Handler-ის კონსტრუქტორში ხელმისაწვდომია DI Container-ში რეგისტრირებული ყველა სერვისი და აქვს ასინქრონული დამუშავების მხარდაჭერა. მოთხოვნის და Handler-ის ობიექტი Command-ებისთვის და Query-ებისთვის გამოიყურება ერთნაირად, განსხვავდება მხოლოდ დასახელებები. განსხვავებული დასახელებების და ფოლდერებში ლოგიკური დაჯგუფებით ვაღწევთ Command ების და Query-ების განცალკევებას.

Query-ი გამოიყენება მონაცემების წაკითხვისთვის.

ახლა კი, ვნახოთ როგორ გამოიყურება AccountsController-ი. ვინაიდან ძირითადი ლოგიკა გატანილი გვაქვს შესაბამის მხარეს (Query, Command)-ის Controller-ში რჩება მხოლოდ მედიატორის (შუამავლის) ობიექტის გამოძახება.

მაგალითისთვის გამოყენებული პროექტის სრული სორს კოდი შეგიძლიათ იხილოთ შემდეგ მისამართზე:

CQRS-ის პატერნთან ერთად, თიბისის გუნდში მუდმივად ხდება სხვადასხვა არქიტექტურული მიდგომების თუ კოდის დიზაინ პატერნების განხილვა, გაცნობა და პროექტებში გამოყენება. რაც საშუალებას გვაძლევს უფრო მეტი ეფექტურობით შევუსაბამოთ ტექნიკური გადაწყვეტა, პროექტში არსებულ მოთხოვნებს.

პოსტის მეორე ნაწილში განვიხილავ, როგორ შეგვიძლია MediatR-ის ბიბლიოთეკის გამოყენებით შევიმუშაოთ Publish/Subscribe მიდგომა. და ასევე განვიხილავთ MediatR-ის Behavior Pipeline-ებს. რა დამატებით საშუალებებს გვაძლევს აღნიშნული ფუნქციონალის რეალიზება და რა სახის მიდგომები არსებობს.

--

--

George Kopadze
TBC Engineering

I am a senior software engineer and engineering manager with over 11+ years of experience in application design and developmen.