شیما سیف الهی
شیما سیف الهی
خواندن ۱۱ دقیقه·۳ سال پیش

الگوی CQRS

الگوی CQRS چیست؟

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

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

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

الگوی معماری CQRS
الگوی معماری CQRS


مسئله

در معماری سنتی از یک مدل داده یکسان برای کوئری و بروزرسانی پایگاه داده استفاده می‌شود که رویکردی آسان است و برای عملیات CRUD به خوبی کار می‌کند. در برنامه‌های پیچیده‌تر این رویکرد دشوار است. مثلا در سمت خواندن برنامه ممکن است کوئری‌های مختلفی را انجام دهد و اشیای انتقال داده را با اشکال مختلف برگرداند و نگاشت اشیا ممکن است پیچیده و دشوار شود. در سمت نوشتن مدل ممکن است اعتبارسنجی و منطق کسب و کار پیچیده را پیاده‌سازی کند. اغلب یک عدم تطابق بین خواندن و نوشتن داده وجود دارد، مانند ستون‌ها یا ویژگی‌های اضافی که باید به درستی بروزرسانی شوند حتی اگر به عنوان بخشی از یک عملیات مورد نیاز نباشند.

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

معماری سنتی
معماری سنتی


راه حل

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

الگوی CQRS پایه
الگوی CQRS پایه


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

اگر از پایگاه داده‌های خواندن و نوشتن مجزا استفاده شود، باید به صورت هماهنگ نگهداری شوند که معمولا با انتشار مدل نوشتن یک رویداد، هر زمان که پایگاه داده را بروزرسانی می‌کند انجام می‌شود. ذخیره خواندن می‌تواند یک نسخه فقط خواندنی از مخزن نوشتن باشد یا مخزن های خواندن و نوشتن می‌توانند ساختارهای متفاوتی با یکدیگر داشته باشند. با استفاده از چند نسخه فقط خواندنی می‌توان عملکرد کوئری را افزایش داد. به ویژه در نسخه‌های توزیع شده که نسخه‌های فقط خواندنی نزدیک نمونه‌های برنامه قرار دارند. جداسازی مخزن‌های خواندن و نوشتن همچنین سبب مقیاس‌بندی مناسب و متناسب با بار می‌گردد. مثلا معمولا مخازن خواندن بار بیشتری نسبت به مخازن نوشتن دارند.

جداسازی مخازن خواندن و نوشتن در الگوی CQRS
جداسازی مخازن خواندن و نوشتن در الگوی CQRS


انواع معماری CQRS

مدل CQRS با یک پایگاه داده: در یک پایگاه داده CQRS هر دو سمت کوئری و فرمان به یک پایگاه داده مرتبط هستند. دستورات use-caseها را در دامنه اجرا می‌کنند که وضعیت موجودیت را تغییر می‌دهد. سپس موجودیت در پایگاه داده ذخیره می‌شود. کوئری‌ها به طور مستقیم از طریق دسترسی به لایه داده اجرا می‌شوند.

Single Database CQRS
Single Database CQRS


مدل CQRS با دو پایگاه داده: دو پایگاه وجود دارد که یکی برای عملیات نوشتن و دیگری برای عملیات خواندن است. سمت فرمان از پایگاه داده نوشتن بهینه‌ شده برای عملیات نوشتن استفاده می‌کند و سمت کوئری از پایگاه داده خواندن بهینه شده برای عملیات خواندن استفاده می‌کند. با تغییر هر حالت توسط فرمان، داده تغییر یافته باید از پایگاه داده نوشتن به پایگاه داده خواندن به عنوان یک تراکنش هماهنگ واحد در میان هر دو پایگاه داده یا با استفاده از الگوی سازگاری احتمالی منتقل می‌شود. این معماری سبب بهبود عملکرد سمت کوئری نرم‌افزار می‌شود زیرا کاربران وقت بیشتری را برای خواندن داده‌ها نسبت به نوشتن داده‌ها صرف می کنند.

Two-Database CQRS
Two-Database CQRS


مدل CQRS برای یافتن منبع رویداد: پیچیده‌ترین معماری CQRS است و وضعیت برنامه به صورت یک توالی از رویدادها ذخیره می‌شود که هر رویداد نمایانگر یک مجموعه تغییر برای داده است. رویداد فعلی با اجرای دوباره رویدادها ایجاد می‌شود. می‌توان از یک رویداد برای اطلاع‌رسانی سایر اجزا به ویژه برای اطلاع رسانی مدل خواندن استفاده کرد. در این رویکرد فقط موجودیت‌های وضعیت فعلی ذخیره نمی‌شوند و هر حالتی که برای موجودیت رخ داده است به عنوان تصاویر لحظه‌ای ذخیره می‌شوند. مدل خواندن از رویدادها برای ایجاد یک تصویر لحظه‌ای وضعیت فعلی استفاده می‌شود که برای کوئری کاراتر است. موجودیت‌ها به عنوان داده‌های نرمال ذخیره نمی‌شوند بلکه به عنوان تغییرات مستقیم با زمان مهر یک رویداد ذخیره می‌شوند. فرمان‌ها می‌توانند موجودیت فعلی را تغییر دهند. تغییرات رویداد جدید را تولید می‌کنند که در منبع رویداد ذخیره می‌شوند. بنابراین وضعیت فعلی موجودیت در یک پایگاه داده خواندن قرار داده می‌شود تا خواندن بسیار سریع صورت گیرد. یافتن منبع رویداد سبب افزایش پیچیدگی‌ می‌گردد.

Event-Sourcing CQRS
Event-Sourcing CQRS


مؤلفه‌های مورد استفاده در مدل CQRS

سمت Query: باید مسئول و بهینه‌ شده برای خواندن داده باشد. کوئری‌ها داده را می‌خوانند و سپس آن‌ها را در فرم مورد نیاز به لایه presentation نگاشت می‌کنند. این فرم‌ها به عنوان اشیای انتقال داده شناسایی می‌شوند.

سمت Commands: مسئول و بهینه شده برای نوشتن داده است. دستورات موارد مورد استفاده ر اجرا می‌کنند، وضعیت‌های موجودیت‌ها را تغییر می‌دهند و آن‌ها را ذخیره می‌کنند.


موارد استفاده از مدل CQRS

  • این مدل در بخش‌های خاصی از یک سیستم یعنی لایه برنامه استفاده می‌شود و در کل سیستم استفاده نمی‌شود. این مدل لایه برنامه را به دو سمت دستورات و کوئری‌ها تقسیم می‌کند.
  • با استفاده از CQRS می‌توان به آسانی با چند دامنه پیچیده مقابله کرد. معمولا همپوشانی کافی سمت فرمان و کوئری وجود دارد که به اشتراک‌گذاری یک مدل را تسهیل می‌کند.
  • سبب مدیریت برنامه‌هایی با کارایی بالا می‌گردد. می‌توان load را به صورت مجزا از خواندن و نوشتن انجام داد و می‌توان هر یک را به صورت جداگانه مقیاس‌گذاری کرد. اگر برنامه تفاوت زیادی را بین خواندن‌ها و نوشتن‌ها مشاهده کند، سودمند است.
  • دامنه‌های مشارکتی که در آن‌ها بسیاری از کاربران به داده یکسان به طور موازی دسترسی دارند. با استفاده از CQRS می‌توان دستوراتی با دانه‌بندی کافی با کمینه کردن تداخلات ادغام در سطح دامنه تعریف کرد و تداخل‌های حاصل را می‌توان با دستورات ادغام کرد.
  • رابط‌های کاربری مبتنی بر وظیفه که در آن کاربران از طریق یک فرایند پیچیده به عنوان یکی سری از گام‌ها با مدل‌های دامنه پیچیده هدایت می‌شوند. مدل نوشتن یک پشته پردازش دستور کامل با منطق کسب و کار، اعتبارسنجی ورودی و اعتبارسنجی کسب و کار است. مدل نوشتن ممکن است به عنوان مجموعه‌ای از اشیای مرتبط به عنوان یک واحد برای تغییرات داده (یک جمع در اصطلاح طراحی مدل محور) رفتار کند که باید همیشه این اشیا در وضعیتی ثابت باشند. مدل خواندن منطق کسب و کار یا پشته اعتبارسنجی ندارد و فقط یک شی انتقال داده را برای استفاده در مدل view را برمی‌گرداند. مدل خواندن با مدل نوشتن سازگار است.
  • سناریوهایی که یک تیم توسعه‌دهنده می‌تواند روی مدل دامنه پیچیده که بخشی از مدل نوشتن است، تمرکز کند و تیم‌های دیگر می‌توانند روی مدل‌ خواندن و رابط کاربری تمرکز کنند.
  • سناریوهایی که انتظار می‌رود سیستم در طول زمان تکامل یابد و ممکن است شامل چندین نسخه از مدل باشند یا جایی که قوانین کسب و کار به طور منظم تغییر می‌کنند.
  • ادغام با سایر سیستم‌ها به ویژه در ترکیب با منبع رویداد که در آن جا خرابی موقت یک زیر سیستم نباید روی در دسترس پذیری سایر سیستم‌ها تاثیر بگذارد.

موارد عدم استفاده از مدل CQRS

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


چالش‌های پیاده‌سازی مدل CQRS

پیچیدگی: مدل CQRS برای پایه سادگی بنا نهاده شده است ولی ممکن است منجر به طراحی برنامه پیچیده‌تر منجر می‌شود. به خصوص اگر شامل الگوی منبع رویداد باشد.

پیام‌رسانی: اگر چه مدل CQRS نیاز به پیام‌رسانی ندارد، استفاده از پیام‌رسان برای پردازش دستورات و انتشار رویدادهای بروزرسانی شده مرسوم است. برنامه باید از پیام‌های خراب و تکراری جلوگیری کند.

ثبات احتمالی: اگر پایگاه داده خواندن و نوشتن از یکدیگر جدا شوند، داده خوانده شده ممکن است قدیمی شود. مدل خواندن ذخیره شده باید بروزرسانی شود تا تغییرات در مدل نوشتن ذخیره شده منعکس شود. زمانی که کاربر درخواستی را بر اساس داده خوانده شده قدیمی می‌دهد، تشخیص دشوار می‌باشد.


مزایای مدل CQRS

  • جداسازی فعالیت‌های خواندن از فعالیت‌های نوشتن سبب می‌شود تا بتوان از بهترین فناوری پایگاه داده برای وظایف استفاده کرد. مثلا یک پایگاه داده SQL برای نوشتن و یک پایگاه داده non-SQL برای خواندن مورد استفاده قرار می‌گیرد.
  • تعداد فعالیت خواندن بیشتر از تعداد فعالیت نوشتن است، بنابراین می‌توان تاخیر زمان پاسخ را با قرار دادن منابع خواندن داده در موقعیت‌‌های جغرافیایی استراتژیک برای افزایش عملکرد کاهش داد.
  • جداسازی فعالیت خواندن از فعالیت نوشتن منجر به مقیا‌س‌بندی کاراتر ظرفیت ذخیره‌سازی بر اساس استفاده در دنیای واقعی می‌شود.
  • مرزهای بین رفتار سیستم را پاک می‌کند یعنی راهنمایی خاصی روی ساختار برنامه در رابطه با رفتار ارائه می‌دهد.
  • به منطق تجاری نزدیکتر است. کد و معماری توسط عملیات تجاری آن‌ها مجزا شدند و تمرکز بر روی موارد کسب و کار را آسان‌تر می‌سازد.
  • سبب اتصال و چسبندگی ضعیف می‌گردد. منطق به صورت منسجم است و کمتر به هم مرتبط است و ایجاد برنامه‌های ماژولار و قابل نگهداری را آسان‌تر می‌سازد.
  • سبب کاهش بار شناختی می‌گردد. به دلیل جداسازی عمودی، کدهای مرتبط با هم نگه داشته می‌شوند. برای تغییر کد موجود یا ایجاد کد جدید نیاز به درک کل معماری یا منطق کسب و کار نیست و تمرکز روی یک کار خاص آسان‌تر است.
  • سبب مقیاس‌بندی آسان، بهینه‌سازی و تغییرات معماری می‌شود. به دلیل نگهداری کد در منابع ذخیره‌سازی، تنظیم دقیق یک خط لوله آسان‌تر است. تمرکز روی بهینه‌سازی‌های دقیق در مکان‌های بحرانی برای کسب و کار، آسان‌تر است.
  • قابل پیش‌بینی است. به دلیل جداسازی قوانین کلی برای رفتار عملیات وجود دارد. تغییر وضعیت کوئری برنامه تعجب‌آور نیست. حفظ فاصله بین نوشتن و خواندن، احتمال پایان یافتن با کد spaghetti را کاهش می‌دهد.


معایب مدل CQRS

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

«این مطلب، بخشی از تمرینهای درس معماری نرم‌افزار در دانشگاه شهیدبهشتی است»


منابع و مراجع:

https://levelup.gitconnected.com/3-cqrs-architectures-that-every-software-architect-should-know-a7f69aae8b6c
https://martinfowler.com/bliki/CQRS.html
https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs
https://www.redhat.com/architect/pros-and-cons-cqrs
https://www.eventstore.com/cqrs-pattern


معماری_نرم_افزار_بهشتیالگوی CQRSمعماری نرم افزاردانشگاه شهید بهشتیمستند سازی
شاید از این پست‌ها خوشتان بیاید