الگوی معماری CQRS چیست؟

11 دقیقه زمان مطالعه
1400/06/23
1 نظر

الگوی معماری نرم افزار Command and Query Responsibility Segregation که به اختصار CQRS نامیده می‌شود. به زبان ساده‌تر این الگو وظیفه جدا کردن Commandها و Queryها از یکدیگر یا به عبارتی جدا کردن مسئولیت Read کردن و Write کردن را برعهده دارد. همان طور که در شکل مشاهده می‌کنید، client ما از سرویس‌های جداگانه‌ای برای read کردن و write کردن اطلاعات استفاده می‌کند و حتی databaseهای آن‌ها نیز با یکدیگر متفاوت است. در سیستم‌هایی که به صورت Event Sourcing کار می‌کنند این اصل بسیار لازم است. برای نمایش اطلاعات ممکن است ما به تجمیعی از رکوردهای دیتابیس که insertها در آن انجام شده است،  نیاز داشته باشیم.

Command and Query Responsibility Segregation

به همین دلیل هنگام insert کردن فرآیند projection روی داده‌ها (eventها) انجام شده و QueryModel ها را برای ما می‌سازد که بعدا هنگام read از آنها استفاده کنیم و از سرعت خوبی برخوردار باشیم. لازم به ذکر است که EventSourcing و Projection جزئیات بسیاری دارند که در این مقاله به آن‌ها پرداخته نمی‌شود. اما مطالعه آن درک بهتری در CQRS به ما خواهد داد. اجازه دهید در ابتدا از طرح یک مشکل، به عنوان صورت مساله شروع کنیم تا ضرورت درک و استفاده از  تعریف را  بهتر درک کنیم. 

فرض کنیم سایتی داریم که برای آن تبلیغات عمومی انجام شده و کاربران سامانه می‌خواهند در سایت و در یک مسایقه شرکت کنند. با توجه به این که در اواخر مهلت پایان شرکت در مسابقه، کابران زیادی هم زمان اقدام به مشارکت می‌کنند و مدیران سایت نیز می‌خواهند میزان مشارکت کاربران و درصد پاسخ صحیح را بررسی کنند.

حالا چه مشکلی در این جا داریم؟ وقتی کاربر در مسابقه شرکت می‌کند ، جواب آن در جدول T ذخیره می‌شود. فرض کنیم جدول T اطلاعات زیادی از سال‌های گذشته تا کنون داشته و تعداد رکوردهای خیلی بالایی دارد. از طرفی مدیر سیستم نیز از جدول T گزارش‌های آماری می‌خواهد. گزارش آماری یعنی توابعی از جمله Aggregate ها مثل Sum و Average و … که همگی مصرف CPU بالایی دارند. در نتیجه در بانک اطلاعاتی که وظیفه ثبت و نگه‌داری این اطلاعات را دارد شاهد کندی زیادی خواهیم بود.

برای این مشکل شاید راه حل‌های زیادی وجود داشته باشد و به این نکته باید توجه کرد که سیستم‌ها با توجه به حجم اطلاعات، اهمیت کاری که انجام می‌دهند و تعداد کاربران یا تراکنش‌های هم زمانی که دارند، با یکدیگر کاملا متفاوت هستند. در نتیجه ادبیات و نحوه رویکرد در هرکدام از آنها متفاوت خواهد بود. اما در این مقاله ذهن خود را بر حل مساله توسط CQRS معطوف خواهیم کرد.

راه‌‌حل مسئله با الگوی معماری CQRS

راه‌حل پیشنهادی این است که ما تمام پاسخ‌ها را در یک جدول ثبت کنیم و از جدول دیگری اطلاعات را به مدیر سیستم نمایش دهیم. حتی بهتر است اطلاعاتی که write می‌شوند در یک database و read کردن اطلاعات از یک دیتابیس دیگر باشد. اما انجام این کار چه فایده‌ای برای ما دارد ؟

یکی از مسائل مهم در برنامه نویسی  استفاده از ابزار مناسب برای اجرای کار مورد نظر است. شاید برای insertکردن اطلاعات بتوان از یک دیتابیس Relational استفاده کرد که مسائل مربوط به ACID را نیز حتما باید در نظر گرفت اما ممکن است هنگام گزارش‌گیری در لحظه، درگیر چندین join شویم و یا به دلیل search در یک متن طولانی دچار کندی در بخش searchکردن شویم و نمایش گزارش‌ها از یک دیتابیس document base برای ما راحت‌تر و سریع‌تر باشد.

ممکن است نوشتن اطلاعات در یک دیتابیس باشد اما برای خواندن اطلاعات از چندین سیستم برای حالت read-only استفاده کنیم که پردازنده سیستم write کننده اطلاعات را درگیر نکند. حال اگر ما کلاس‌هایی که مسئولیت Read و Write آن‌ها جداگانه باشند، طراحی کرده باشیم. می‌توانیم بدون وابستگی به تکنولوژی‌ این دو دیتابیس رو جدا از هم دانسته و استفاده مفیدتر و بهتری را انجام دهیم. 

نکته‌ای در این بین وجود دارد که شاید ما را به اشتباه انداخته باشد و آن هم بحث CQS است. یعنی Command Query Separation. در این اصل می‌گوییم که command و query از یکدیگر جدا باشند اما از جدا بودن مسئولیت آن‌ها صحبتی نمی‌کنیم. وقتی صحبت از مسئولیت است یعنی این دو به لحاظ کلاس هم باید از یکدیگر متفاوت باشند.

برای نمونه مثالی از CQS مطرح می‌کنیم :

نمونه مثالی از CQS

در تصویر بالا مشاهده می‌شود که کد نوشته شده در سمت راست یک متد Command را اجرا می‌کند که در فایل write کند و در همان متد جواب را بر می‌گرداند. با این فرض که اگر فایل مورد نظر وجود نداشت خطا handle شده است.که این نقض، اصل CQS است.  اما در کد نوشته شده در سمت چپ ابتدا وجود داشتن فایل را در یک متد دیگر بررسی می‌کند و بعد از که این مرحله را پاس کرد به مرحله write کردن در فایل می‌رود و خروجی هم برای این کار ندارد.

مقیسه cqrs با cqs

در حالت CQS دیدیم که جدا شدن read و write در سطح method انجام شد. اما در CQRS بحث جداسازی مسئولیت‌ها به وجود می‌آید و modelها و  classهای آن‌ها نیز از یکدیگر جدا خواهند شد. در واقع CQRS سطح CQS را در سطح معماری بالاتر برده است.

تفاوت CQRS و CQS

مزایای الگوی معماری CQRS چیست؟

در ادامه به چند مورد از مزایای CQRS می‌پردازیم و با آن‌ها بیش‌تر آشنا می‌شویم.

مقیاس پذیری (Scalability)

برای مثال شما از بانک اطلاعاتی SQL Server استفاده کرده و چندین Node ایجاد می‌کنید. یکی از این Nodeها فقط برای Write کردن و همسان نگه داشتن سایر Nodeها استفاده می‌شود. Nodeهای درگیر نیز فقط عملیات read را پاسخ می‌دهند. در نتیجه برای بخش‌های مختلف می‌توان Nodeهای مختلفی گذاشت و میزان بار روی هر سیستم را کنترل کرد. این که برای هرکاری به کدام database وصل شویم در CQRS قابل کنترل است. همچنین اضافه کردن Node های مختلف در database تغییری را سمت کد نخواهد آورد و به راحتی قابل افرایش خواهد بود.

عملکرد (Performance)

تعریف این موضوع را نیز با یک مثال بیان می‌کنیم. فرض کنید شما برای read کردن اطلاعات از یک database که مکانیزم InMemory دارد مثل Redis استفاده می‌کنید. سرعت خواندن از Memory بسیار بالاتر از خواندن از هارد است. حتی هارد‌های SSD. در کل فرآیند IO بسیار هزینه بر خواهد بود. در نتیجه write کردن فایل را به صورت async به بخش Command می‌سپاریم و خواندن اطلاعات را از بانک اطلاعاتی InMemory خود می‌خوانیم. در نتیجه سرعت خواندن اطلاعات ما بسیار بالاتر می‌رود.

سادگی (Simplicity)

سادگی یکی از مزایای این اصل است. چون شما وقتی که مسئولیت خواندن و نوشتن را جدا می‌کنید درواقع اصل Single Responsible بودن را رعایت کرده‌اید. رعایت این اصل یعنی شما برای خواندن و یا نوشتن Use-Case جداگانه‌ای دارد و در صورت تغییر کردن یکی از آن‌ها لازم نیست مورد دیگر تغییری داده شود یا اگر تکنولوژی یکی از آن‌ها تغییر کند لازم نیست بخش دیگر تغییری کند.

جمع‌بندی

همان طور که در مقاله هم اشاره شد، در زمینه نرم‌افزار هیچ نسخه واحدی برای طراحی نرم افزار و یا پیاده‌سازی نمی‌توان داشت. CQRS یکی از مواردی است که می‌توان در حالتی که طراحی به صورت Event Sourcing است و یا نگرانی‌های Performance و Scale کردن نرم افزار را داریم به ما کمک کند. تمرکز آن بر روی جدا کردن database ها یا حتی جدول های نگه‌داری اطلاعات نوشتنی و اطلاعات خواندنی است و این اصل را بیان می‌کند که خواندن و نوشتن دو مسئولیت جدا از هم است و نباید باعث ایجاد تاخیر و یا وابستگی به یکدیگر باشند. در پایان پیشنهاد می‌کنیم برای فهم عمیق این موضوع در خصوص Event Sourcing و Domain Driven Design نیز مطالعه‌ داشته باشید.

منابع:

https://martinfowler.com/bliki/CQRS.html

https://udidahan.com/2009/12/09/clarified-cqrs/

امتیاز شما به این مقاله:
نویسنده: یک تلاشگر اهل مطالعه و فیلم که بیشتر از یک دهه است در زمینه توسعه نرم‌افزار کار می‌کند.

مطالب مرتبط