CQRS و دیگر هیچ!

CQRS و دیگر هیچ!

CQRS سرواژه ی عبارت Command Query Responsibility Segregation است که به معنای تفکیک مسئولیت های Commandها (تغییر در داده ها) و Queryها (خواندن داده ها) است. همانطور که از نام آن مشخص است، کسانی که این مدل را ارائه کردند معتقد بودند ما می توانیم برای دو وظیفه ی اصلی خواندن داده های موجود و نوشتن یا تغییر دادن آنها، دو راه مختلف داشته باشیم. برای بعضی از شرایط CQRS می تواند یک رویکرد ارزشمند باشد ولی در بعضی دیگر از سیستم ها، این روش تفکیک مسئولیت ها می تواند باعث افزایش پیچیدگی و در نتیجه ریسک بیشتر شود.

این مقاله برگرفته از بلاگی است که مارتین فولر کبیر در سایتش نوشته است که می توانید مقاله ی اصلی را در این آدرس بخوانید.

وقتی درباره ی دیتابیس صحبت می کنید، از تکنولوژی صحبت می کنیم که چهار عمل اصلی CRUD یعنی اضافه کردن داده ی جدید (Create)، خواندن داده های موجود (Read)، تغییر و به روز رسانی داده های ذخیره شده در دیتابیس (Update) و حذف داده ها (Delete) در آن انجام می شود. اگر به این چهار عمل کمی متفاوت تر (یا با جزئیات کمتر نگاه کنیم) می توانیم آنها را به دو عمل تغییر دادن و خواندن داده ها تقسیم بندی کنیم.

ولی آیا اگر برنامه ی ما به یک برنامه ی بزرگتر و پیچیده تر تبدیل بشود، بازهم می توانیم به همین صورت ساده به این چهار عمل نگاه کنیم؟ برای مثال ممکن است ما نیاز داشته باشیم رکوردهایی که در دیتابیس های مختلف یا جدول های متفاوت ذخیره کرده ایم را به با هم ادغام کنیم و به عنوان یک رکورد مجازی داشته باشیم تا دسترسی به آن راحت باشد. یا مثلا ممکن است بخواهیم تنها در شرایط خاصی و با سیاست های پیچیده ای داده هایی برای Update انتخاب شوند یا در صورتی که داده ها از Policy (سیاست) های مختلف با موفقیت عبور کردند، در دیتابیس ذخیره شوند.

همانطور که در تصویر بالا می بینید، Model وظیفه دارد دستورات کاربر برای اضافه کردن یا تغییر دادن داده ها را دریافت کرده و این بعد از این که آن دستور و اطلاعات را اعتبار سنجی کرد، آنها را طبق سیاست های برنامه اجرا کند. همزمان باید داده هایی را هم که نیاز است به کاربر نشان داده شود از دیتابیس خوانده، تحلیل کند و برای کاربر ارسال نماید. همچنین ما نیاز داریم برنامه مان بخشی داشته باشد که از کاربر دستورهایش را بگیرد و به مدل ارسال کند و همچنین داده هایی که از دیتابیس آمده است را برای نمایش به کاربر آماده کند. همانطور که از تعریف های بالا هم مشخص است در این الگو دو وظیفه ی مختلف در هم ادغام شده است که می تواند Flow برنامه ی ما را با مشکلاتی روبرو کند.

الگویی که CQRS ارائه می دهد تفکیک مسیر این دو وظیفه در لایه ی برنامه است. یعنی می گوید اگر این دو وظیفه را در سطح طراحی و پیاده سازی برنامه از هم تفکیک کنیم، دو مسیر مشخص با وظیفه های تفکیک شده داریم که هر کدام از آنها می توانند کار خودشان را بهتر انجام بدهند و اختلالی در وظایف دیگری ایجاد نکنند. دغدغه ی  CQRS این است که وقتی سیستم بزرگ شد، اینکه از یک مدل برای Commandها و Queryها استفاده کنیم، باعث می شود دچار پیچیدگی های بشویم که دیگر آن مدل نمی تواند هیچ کدام از آن وظایف را به خوبی انجام دهد.

همانطور که در تصویر بالا هم مشاهده می کنید وقتی کاربر در حال تماشای وب سایت ما است، Interface نمایش سایت را فراخوانده است و داده های آن را دریافت کرده است. ولی وقتی که تصمیم میگیرد داده ای را به سمت سرور ما ارسال کند تا داده ای اضافه شود یا تغییر کند، Interface دیگری را فراخوانی می کند و Model Object دیگری اجرا می شود.

در CQRS ممکن است طراحی به گونه ای باشد که Command Model و Query Model هردو از یک دیتابیس واحد استفاده کنند که در این حالت دیتابیس محل اشتراک مدل ها خواهد بود. ولی در بسیاری از موارد وقتی از CQRS استفاده می کنید دیتابیس Query Model، یک Reporting Database (در مقابل Operation Database) خواهد بود که لازم است با سرعت زیادی داده ها را خوانده و به سمت کاربر ارسال نماید. و Command Model از دیتابیس جداگانه ای استفاده می کند که با توجه به این تفکیک نیاز است روشی هم برای به روز رسانی و هماهنگی (Sync) بین این دو دیتابیس انتخاب شود.

توجه داشته باشید که یکی از اصلی ترین چالش هایی که طراحان سیستم های نرم افزاری با آن دست و پنجه نرم می کنند، راه حل بهینه ایست که بتواند سیستم را از چالش های Sync نبودن اطلاعات تغییر کرده در زمان خواندن آنها نجات بدهد. به زبان ساده ممکن است کاربری تغییر در داده ای انجام داده باشد که در دیتابیس Command ذخیره شده است ولی در دیتابیس Query هنوز داده ی قدیمی وجود دارد و کاربری که می خواهد آن را مشاهده کند، داده ی قدیمی را می بیند. برای حل این مسئله فکر و تحقیق کنید. راه حل های متعددی هست که خارج از موضوع این مقاله است ولی داشتن دانش حل این مسئله برای شما می تواند خیلی مفید باشد.

چه زمانی از CQRS استفاده کنیم؟

مثل هر الگوی طراحی دیگری در دنیای نرم افزار، CQRS هم در بعضی از شرایط خوب است و باعث می شود مسائل بهتر حل شوند و در بعضی دیگر از شرایط اصلا خوب نیست و باعث پیچیدگی اضافی می شود. دقت داشته باشید که لازم نیست CQRS در کل سیستم پیاده سازی شود و می شود فقط بخش های خاصی از سیستم را با این الگو طراحی کرد و مسیر Commandها و Queryها را از هم تفکیک کرد.

یکی از کاربردهای CQRS زمانی است که ما با سیستمی مواجه هستیم که بین حجم یا شرایط پردازش اطلاعات در Command و Query تناسبی وجود ندارد و مثلا فرض کنید برای خواندن اطلاعاتی که می خواهیم در صفحه ای از سایت مان به کاربر نشان بدهیم نیاز است تعداد زیادی از سیاست های مختلف امنیتی و غیر امنیتی را بررسی کنیم و اطلاعات باید از جدول های متعدد با شرایط خاصی خوانده شود و بعد روی آنها تغییراتی اعمال شود. البته این شرایط اصلا معمول نیست و احتمال این که با چنین شرایطی مواجه شوید خیلی کم است.

ولی اصلی ترین و پرکاربردترین جایی که توصیه می شود از CQRS استفاده کنیم، زمانی است که بین حجم درخواست های Query و Command در برنامه ی ما تناسب وجود ندارد. برای درک بهتر این موضوع با یک مثال با هم پیش برویم. فرض کنید از شما خواسته شده است سیستم نرم افزار اینستاگرام را طراحی کنید. همانطور که می دانیم وقتی یک نفر وارد اینستاگرام میشود ابتدا پست ها و استوری های منتشر شده توسط تمام افرادی که دنبال می کند را مشاهده می کند. اگر فرض کنیم طی روز 100 میلیون نفر از اینستاگرام استفاده می کنند یعنی 100 میلیون بار باید تمام پست ها و استوری ها پردازش شود و به آنها نمایش داده شود. این درحالی است که در همان روز شاید حداکثر یک میلیون پست و استوری جدید ایجاد شود. این یعنی تعداد درخواست های خواندن اطلاعات (Query) 100 برابر بیشتر از تعداد درخواست های اضافه شدن اطلاعات (Command) است.

در چنین سیستم هایی، پیشنهاد می شود از الگوی طراحی نرم افزار CQRS استفاده کنیم تا با تفکیک وظایف و بخش های مرتبط با Query و Command بتوانیم هر کدام از این بخش ها را به صورت جداگانه بهینه یا Scale کنیم. البته دقت داشته باشید که همین یک فاکتور هم نمی تواند شما را به سمت الگوی طراحی CQRS ببرد و شاید بتوانید با استفاده ی بهینه از Cache یا ReportingDatabase مشکل را به صورت بهینه تری حل کنید.

در آخر به این دو نکته دقت داشته باشید:

  • هرچند در بعضی از شرایط CQRS باعث میشود فرآیندهای نرم افزاری بهبود پیدا کنند ولی در خیلی از موارد همین پیچیدگی ایجاد شده توسط این الگو می تواند مخرب باشد. پس با احتیاط از CQRS استفاده کنید.
  • شما به عنوان یک طراح سیستم های نرم افزاری، لازم است الگوهای مختلف را بشناسید، مزایا و معایب استفاده کردن از آنها را بدانید تا در حل مسائل مختلفی که در نرم افزارتان ایجاد می شود بتوانید بر اساس نیازمندی که وجود دارد و شرایط خاص هر محصول، بهترین الگو را پیشنهاد بدهید.

اگر دوست داشتید درباره ی CQRS بیشتر بدانید:

از بهترین نوشته‌های کاربران سکان آکادمی در سکان پلاس