پیاده سازی CQRS توسط MediatR - قسمت اول
اندازه‌ی قلم متن
تخمین مدت زمان مطالعه‌ی مطلب: چهار دقیقه

در مطالب قبلی (1 , 2) الگوی CQRS معرفی شد. همانطور که می‌بینید، پیاده سازی این الگو هرچند با فریمورک آماده‌ای همچون SimpleCQRS، دارای پیچیدگی زیادی است و باعث نوشتن حجم زیادی کد می‌شود.

فریمورک MediatR توسط توسعه دهنده کتابخانه‌ی محبوب AutoMapper ایجاد شده‌است. این فریمورک پیاده سازی کاملی از الگوی طراحی Mediator در NET. است که داخل خود، تمام پیچیدگی‌های پیاده سازی CQRS را Abstract کرده و با حداقل کد ممکن، می‌توانید به‌راحتی CQRS را داخل پروژه‌ی خود پیاده سازی کنید.

در این سری مطالب به بررسی کامل الگوی CQRS و مزایا و معایب استفاده از آن می‌پردازیم و سپس با استفاده از کتابخانه‌ی Mediatr، این الگو را داخل یک پروژه پیاده سازی می‌کنیم.

CQRS

در CQRS متد‌های برنامه به 2 بخش Read و Write تقسیم می‌شوند. بخش‌هایی که State کلی برنامه ( شامل Database, Cookie, Session, LocalStorage, Memory و ... ) را تغییر می‌دهند، Command و بخش‌هایی که صرفا جنبه خواندنی دارند و وضعیت سیستم را تغییر نمی‌دهند مثل خواندن و نشان دادن اطلاعات از دیتابیس، Query می‌نامند.

* نکته : Naming Convention مورد استفاده برای Command‌‌ها به صورت دستوری است و کار Command در نام آن مشخص است؛ مثال : RegisterUser, SendForgottenPasswordEmail, PlaceOrder

مزایا:
1- شما می‌توانید تکنولوژی‌های مورد استفاده‌ی در بخش‌های Command و Query برنامه‌ی خود را به‌راحتی از هم جدا سازید. به‌عنوان مثال Apache Cassandra در ذخیره سازی داده‌ها ( Write Side ) به عنوان یک دیتابیس قابل اعتنا شناخته میشود و از طرفی دیگر ElasticSearch بدلیل سرعت فوق العاده‌ی خود، برای خواندن داده‌ها استفاده میشود. در این روش، دیتابیس‌ها باید Sync باشند تا داده‌های به‌روز به کاربر نمایش داده شود که این موضوع چالش‌های خود همچون Eventual Consistency و Strong Consistency را دارد که در مقالات بعدی آن‌ها را بررسی خواهیم کرد.

2- در برنامه‌های معمول، اکثرا بخش Read Side، بیشتر از Write Side استفاده می‌شود و کاربران معمولا اطلاعات را دریافت و می‌بینند تا اینکه در آن تغییری ایجاد کنند؛ در این صورت شما می‌توانید بخش Read برنامه‌ی خود را Scale کرده و تعداد سیستم یا منابع بیشتری را به این قسمت از برنامه‌ی خود اختصاص دهید ( Horizontal Scaling, Vertical Scaling ). 

3- این جداسازی باعث تمرکز بیشتر شما بر روی قسمت‌های مختلف برنامه می‌شود؛ بخش‌هایی که وضعیت سیستم را تغییر می‌دهند از بخش‌هایی که صرفا داده‌هایی را خوانده و نمایش می‌دهند، بطور کامل جدا شده‌اند و به‌راحتی قابلیت تغییر هرکدام از این بخش‌ها را خواهید داشت.

معایب : معمولا از معایب این الگو، از پیچیدگی پیاده سازی آن یاد می‌شود که در این آموزش با استفاده از Mediatr سعی بر از بین بردن این پیچیدگی را داریم.

Events

Event‌ها رویدادهایی هستند که خبر انجام کاری را که قبلا داخل سیستم انجامش به پایان رسیده است، به Consumer‌های خود می‌دهند. بعنوان مثال می‌خواهیم بعد از ثبت نام موفق یک کاربر داخل سیستم، Notification و یا ایمیلی را به او ارسال کنیم. بعد از ثبت نام کاربر میتوانیم Event ای به نام UserRegistered را که شامل Username و Email کاربر در بدنه خود است، Raise کنیم.

Event‌ها می‌توانند چندین Consumer داشته باشند؛ بنابراین می‌توانیم یک EventHandler را برای UserRegistered بنویسیم که Email ارسال کند و EventHandler دیگری ایجاد کنیم که Notification ای را برای کاربر بفرستد.

* نکته : Naming Convention مورد استفاده برای Event‌ها به صورت گذشته‌است و خبر یک کار، که قبلا انجام شده است را می‌دهد؛ مثال : UserRegistered, OrderPlaced

Event Sourcing

Event Sourcing به معنای ذخیره‌ی تمام Event‌های رخ داده در برنامه داخل یک دیتابیس Append-Only است. در این نوع دیتابیس‌ها فقط میتوانیم Event‌های جدیدی به آن اضافه کنیم و قادر به ویرایش و حذف Event‌ها نیستیم؛ چون منطق Event، کارهایی است که در گذشته اتفاق افتاده‌اند و ما قادر به تغییر چیزی که در گذشته رخ داده‌است، نیستیم.

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

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

روش دیگری وجود دارد که بجای به‌روزرسانی مداوم state جاری، تمام Event هایی که در آن تراکنشی داخل سیستم رخ داده و این تراکنش State برنامه را تحت تاثیر خود قرار داده‌است، داخل یک دیتابیس اضافه نماییم. در این صورت بدلیل داشتن تمام رویدادهای اتفاق افتاده‌ی در برنامه، می‌توان وضعیت جاری سیستم را شبیه سازی و متوجه شد.

* در این سری آموزشی از دیتابیس  Event Store برای پیاده سازی Event Sourcing استفاده خواهیم کرد.

در مقاله‌ی بعدی، امکانات فریمورک MediatR را بررسی خواهیم کرد.
  • #
    ‫۵ سال و ۳ ماه قبل، دوشنبه ۲۷ خرداد ۱۳۹۸، ساعت ۲۳:۴۲
    سلام؛ اگر فرض کنیم که پروژه ای تا 60 درصد پیاده سازی شده و زیر ساخت آن برای  Event Sourcing دیده نشده باشد، حال چطور می‌توان این مفهوم را برای این پروژه پیاده سازی کرد؟ (در نظر داشته باشید که پروژه بالغ بر 100 Entity دارد و از Entity Framework Core و IUnitOfWork استفاده می‌کند)
    • #
      ‫۵ سال و ۲ ماه قبل، پنجشنبه ۳۰ خرداد ۱۳۹۸، ساعت ۱۵:۰۵
      پیاده سازی Event Souring به خودی خود با استفاده از MediatR کار پیچیده ای نیست و براحتی با استفاده از Behavior هاش قابل پیاده سازی هست.
      اگر پروژه شما با MediatR نیست ، شما میتونید از Attribute‌ها ، Middleware‌ها و یا AOP برای پیاده سازی خودکار این مکانیزم استفاده کنید که نسبت به استفاده از Command‌های MediatR ، کمی کار سختتر خواهد بود.