CQRS یک الگوی معماری است که از کنار هم قرار دادن حروف اول عبارت Command Query Responsibility Segregation تشکیل شده است. این الگو، عملیات سیستم را به دو قسمت دستورات (Commands) و کوئری ها (Queries) تقسیم بندی می کند. CQRS با CQS که مخفف Command Query Separation است مرتبط بوده و از آن مشتق شده است.
در این پست، ابتدا CQS را تعریف کرده و ارتباط آن با CQRS را بررسی می کنیم. سپس به توضیح CQRS می پردازیم و در ادامه اصول اصلی، مزایا و همچنین برخی از تصورات اشتباه در مورد این الگو را بررسی می کنیم.
CQS یک الگوی طراحی است که از کنار هم قرار دادن حروف اول عبارت Command Query Separation تشکیل شده است. CQS یک مفهوم اولیه و اساسی است که برای عملیات انجام شده بر روی سیستم، دو نوع را تعریف می کند: دستوری که یک تسک را اجرا می کند و کوئری که اطلاعاتی را برمیگرداند، و تابعی نباید وجود داشته باشد که هر دوی این کارها را انجام دهد.
این اصطلاح توسط Bertrand Mayer و در کتاب «ساخت نرم افزار شی گرا» در سال ۱۹۸۸ خلق شد؛ او آن را به عنوان بخشی از کار خود بر روی زبان برنامه نویسی Eiffel ایجاد کرد.
CQRS اصل تعیین کننده CQS را می گیرد و آن را به اشیاٰء خاصی در سیستم گسترش می دهد، یکی در بازیابی داده و دیگری در تغییر داده. CQRS یک الگوی معماری گسترده و CQS، یک اصل عمومی رفتاری است.
CQRS تفکیک مسئولیت های دستورات و کوئری ها در یک سیستم است. این بدان معناست که ما منطق برنامه خود را به صورت عمودی برش می دهیم. علاوه بر این، پرش حالت (دستورالعمل فرمان) را از بازیابی اطلاعات (کنترل کوئری) جدا می کنیم.
CQRS توسط Greg Young تعریف شد.
Command and Query Responsibility Segregation از همان تعریف دستور (Command) و کوئری که Mayer استفاده کرد، استفاده می کند و این دیدگاه را حفظ می کند که آن دو باید خالص باشند. تفاوت اساسی این است که در CQRS، اشیا به دو شی تقسیم می شوند، یکی دستورات، و دیگری کوئری ها را شامل می شود.
«اشیا» در تعریف اصلی، با ذخیره سازی (Storage) مرتبط نبوده بلکه با کنترل کننده ها (Handlers) مرتبط هستند. ما خطوط لوله (Pipelines) مختلفی را برای رفتار های تجاری متفاوت ایجاد می کنیم، نه برای ذخیره سازی جداگانه.
اصل اساسی CQRS، جداسازی دستورات و کوئری ها و کارهایی است که آن ها انجام می دهند. دستورات و کوئری ها، نقشهای بسیار متفاوتی را در یک سیستم انجام میدهند و تفکیک آنها به این معنی است که هر کدام میتوانند در صورت نیاز بهینه شوند که میتواند برای سیستمهای توزیعشده بسیار مفید باشد.
Alexey Zimarev، چگونگی تفاوت دستورات و کوئری ها را به صورت زیر تعریف کرده است:
در سطح بالا، CQRS این حقیقت را بیان میکند که عملیاتهایی که انتقال حالت را راهاندازی میکنند باید بهعنوان دستور (Command) توصیف شوند، و هر بازیابی دادهای که به اجرای دستور نیاز نداشته باشد، باید یک پرس و جو (Query) نامیده شود. از آنجایی که مقررات عملیاتی برای اجرای دستورات و پرس و جوها اغلب متفاوت است، توسعه دهندگان باید استفاده از تکنیک های ماندگاری مختلف را برای رسیدگی به دستورات و پرس و جوها در نظر بگیرند؛ بنابراین باید آنها را از هم جدا کنند.
در ادامه، به دستورات، کوئری ها و ارتباط آن ها با مدل های خواندن (Read) و نوشتن (Write)، می پردازیم.
به گفته برتراند مایر، Command یک فرمان است؛ دستوری برای انجام یک کار خاص که قصد دارد چیزی را تغییر دهد.
یک دستور، یک کار را انجام می دهد اما نتیجه ای را بر نمی گرداند.
یک دستور باید هدف کاربر را منتقل کند. طبق توضیح الکسی زیمارف، اجرای یک دستور باید منجر به یک تراکنش روی یک جمع شود. اساساً، هر دستور باید به وضوح یک تغییر کاملاً تعریف شده را بیان کند.
دستورات، مدل نوشتن (Write Model) را تشکیل می دهند و مدل نوشتن باید تا حد امکان به فرآیندهای تجاری نزدیک باشد.
به گفته برتراند مایر، کوئری، یک درخواست برای اطلاعات است.
یک کوئری، یک نتیجه بر می گرداند اما حالت را تغییر نمی دهد.
کوئری، درخواستی برای دریافت اطلاعات و یا وضعیت اطلاعات از یک مکان مشخص است. هیچ چیزی در داده ها نباید با درخواست تغییر کند. از آنجایی که کوئری ها چیزی را تغییر نمی دهند، نیازی به درگیر کردن مدل دامنه (Domain Model) ندارند.
کوئری ها مدل خواندن (Read Model) را تشکیل می دهند. مدل خواندن باید از مدل نوشتن مشتق شود. همچنین لازم نیست دائمی باشد. مدلهای خواندن جدید را میتوان بدون تأثیرگذاری بر مدلهای موجود، به سیستم معرفی کرد. مدل های خواندن را می توان بدون از دست دادن منطق تجاری و یا اطلاعات، حذف و دوباره ایجاد کرد؛ زیرا در مدل نوشتن ذخیره می شوند.
استفاده از اصول CQRS در معماری، مزایای بسیاری را به همراه خواهد داشت:
از الگوی CQRS در موارد زیر استفاده می شود:
الگوی CQRS برای موارد زیر، توصیه نمی شود:
استفاده از CQRS را به بخش هایی از سیستم خود محدود کنید که در آن ها بیشترین ارزش را خواهد داشت.
برخی از چالش های استفاده از این الگو، عبارتند از:
این عبارت، لزوما درست نیست؛ فقط نیاز است که رفتار ها و مسئولیت های آن دو را از هم جدا کرد. این می
تواند داخل کد، در ساختار یک پایگاه داده و یا پایگاه های داده مختلف باشد.
CQRS حتی نیازی به استفاده از پایگاه داده ندارد؛ ممکن است از اکسل و یا هر چیز دیگر که حاوی داده است،
استفاده شود.
CQRS، برای رفتار یک سیستم کاربرد دارد، نه برای مکان ذخیره سازی داده. به خاطر داشته باشید که جمله
کلیدی «CQRS به رفتار اشاره دارد و نه ذخیره سازی»، کلید استفاده موثر از CQRS است.
جداسازی دستورات و کوئری ها و مدیریت متفاوت آنها می تواند یک سیستم در نهایت سازگار را تولید کند.
این تصور غلط وجود دارد که سیستم های درنهایت سازگار به دلیل تاخیرهای زمانی، دقیق نبوده و با جداسازی
دستورات و کوئری ها در CQRS، باید تاخیر ها و در نتیجه مشکلات سازگاری نهایی وجود داشته باشد.
صف های پیام مانند Kafka و RabbitMQ، بسته به شرایط به شما امکان ارسال پیام بین مدل های خواندن
و نوشتن را می دهند. اگر مدل شما ساده بوده و با نماهای مختلف پایگاه داده شروع می شود، به صف های
پیام نیازی نیست؛ به طور کلی، استفاده از آن، به نیازمان در آن زمینه تجاری خاص، بستگی دارد. ساخت
سیستم با در نظر گرفتن CQRS به شما این امکان را می دهد که انواع مختلف پایگاه های داده را در تعامل با
یکدیگر داشته باشید. صف های پیام رسانی برای نگه داری به روز مدل های خواندن در صورت تغییر مدل
نوشتن، مفید خواهند بود.
CQRS و DDD نسبت به هم، دو مفهوم مجزا و متعامد هستند و برای استفاده از DDD نیازی به استفاده از CQRS نیست و برعکس. DDD در اصل با CQRS هماهنگ است؛ اما این دو قطعاً به یکدیگر وابسته نیستند.
الگوی معماری CQRS، روشی برای جداسازی رفتار نوشتن و خواندن دادهها از یکدیگر است. این روش، برای سیستمهایی که با تعداد درخواستهای بسیار زیادی روی پایگاههای داده خود مواجه هستند مناسب است. CQRS در شرایط مناسب، باعث افزایش کارایی سیستم و مقیاس پذیری بهتر آن می شود.
لازم به ذکر است که استفاده از CQRS آسان نبوده و نیاز به نیروی متخصص دارد. جداسازی مدلهای خواندن و نوشتن از یکدیگر، خطر ایجاد ناهماهنگی میان دادهها را بالا می برد؛ بنابراین از CQRS باید در موارد کاربردی خود و به طور سنجیده استفاده شود.
«این مطلب، بخشی از تمرینهای درس معماری نرمافزار در دانشگاه شهیدبهشتی است.»
https://bertrandmeyer.com
https://www.eiffel.com/resources/faqs/eiffel-language
https://www.eventstore.com/cqrs-pattern
https://martinfowler.com/bliki/CQRS.html
https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs