الگوی واسط (Mediator Pattern) و کاربرد حیاتی آن در CQRS

معماری نرم‌افزار، مانند هر حوزه مهندسی دیگری، همواره به دنبال روش‌هایی است که پیچیدگی را کاهش داده، قابلیت نگهداری (Maintainability) را افزایش داده و سیستم را مقیاس‌پذیرتر سازد. در این مسیر، دو مفهوم قدرتمند—الگوی واسط (Mediator Pattern) و CQRS (Command Query Responsibility Segregation)—نقش‌های کلیدی ایفا می‌کنند.
کینگتو - آموزش برنامه نویسی تخصصصی - دات نت - سی شارپ - بانک اطلاعاتی و امنیت

الگوی واسط (Mediator Pattern) و کاربرد حیاتی آن در CQRS

31 بازدید 0 نظر ۱۴۰۴/۰۸/۲۷

این مقاله به بررسی عمیق الگوی واسط، چگونگی حل مشکل وابستگی‌های متقابل، و سپس نمایش کاربرد حیاتی آن به‌عنوان یک هسته مرکزی برای پیاده‌سازی مؤثر معماری CQRS می‌پردازد.

 

الگوی واسط (Mediator Pattern) چیست؟

الگوی واسط، یکی از الگوهای طراحی رفتاری (Behavioral Design Patterns) است که توسط باند چهار (Gang of Four) معرفی شد. هدف اصلی آن، کاهش وابستگی‌های متقابل و مستقیم بین مجموعه‌ای از اشیاء (Components) است.

 

تعریف و ساختار

الگوی واسط یک شیء مرکزی تعریف می‌کند که نحوه تعامل اشیاء دیگر با یکدیگر را کپسوله‌سازی می‌کند. در واقع، اشیاء دیگر به‌جای اینکه مستقیماً همدیگر را فراخوانی کنند، تنها با واسط (Mediator) صحبت می‌کنند.

  • کامپوننت‌ها (Components/Colleagues): اشیائی که حاوی منطق کاری خاصی هستند. آن‌ها به‌جای اینکه با همتایان خود در ارتباط باشند، فقط واسط را می‌شناسند.

  • واسط (Mediator): این رابط یا کلاس وظیفه اصلی را بر عهده دارد: مدیریت و هدایت درخواست‌ها بین کامپوننت‌ها. واسط مشخص می‌کند که در پاسخ به درخواست یک کامپوننت، کدام کامپوننت‌های دیگر باید مطلع شوند یا عمل کنند.

 

مشکل حل شده: وابستگی اسپاگتی (Spaghetti Dependencies)

در سیستم‌های پیچیده، کامپوننت‌ها اغلب باید به وقایع (Events) یا تغییرات وضعیت در کامپوننت‌های دیگر پاسخ دهند. وقتی این تعاملات مستقیم باشند، نمودار وابستگی‌ها شبیه یک بشقاب اسپاگتی درهم‌تنیده می‌شود.

  • مشکلات وابستگی مستقیم:

    • افزایش Coupling: هر کامپوننت باید از جزئیات پیاده‌سازی همتایان خود آگاه باشد. تغییر در یکی، می‌تواند منجر به تغییرات زنجیره‌ای در دیگری شود.

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

    • پیچیدگی تست: تست واحد (Unit Testing) بسیار سخت می‌شود، زیرا برای تست یک کامپوننت، باید وابستگی‌های آن را نیز مدیریت (Mock) کرد.

الگوی واسط با تبدیل تعاملات چندبه‌چند (Many-to-Many) به تعاملات چندبه‌یک (Many-to-One) (همگی به واسط)، این مشکل را حل کرده و سیستم را Loose Coupled (کم‌تر وابسته) می‌سازد.

 

معرفی CQRS (Command Query Responsibility Segregation)

CQRS یک الگوی معماری است که پیشنهاد می‌کند عملیات تغییر وضعیت (Commands) و عملیات بازیابی داده (Queries) باید از هم جدا شوند. این جداسازی، فرصت‌های بزرگی برای بهینه‌سازی و مقیاس‌پذیری ایجاد می‌کند.

 

مؤلفه‌های اصلی CQRS

  • فرمان‌ها (Commands): وظیفه تغییر وضعیت در سیستم را بر عهده دارند (مثلاً CreateUserCommand، UpdateProductCommand). آن‌ها باید شامل تمام داده‌های لازم برای انجام یک عمل باشند و هیچ مقداری را برنمی‌گردانند (Void Operation).

  • پردازشگر فرمان (Command Handlers): کلاس‌هایی که منطق کسب‌وکار مربوط به یک فرمان خاص را اجرا می‌کنند.

  • پرس‌وجوها (Queries): وظیفه بازیابی داده‌ها را دارند (مثلاً GetUserByIdQuery، GetAllActiveProductsQuery). آن‌ها وضعیت سیستم را تغییر نمی‌دهند.

  • پردازشگر پرس‌وجو (Query Handlers): کلاس‌هایی که عملیات خواندن داده‌ها را انجام می‌دهند.

 

مزایای CQRS

  1. بهینه‌سازی مستقل: می‌توان دیتابیس‌های مجزا (مدل‌های خواندن و نوشتن) یا بهینه‌سازی‌های متفاوتی را برای عملیات‌های خواندن (که معمولاً پرفشارتر هستند) و عملیات‌های نوشتن پیاده‌سازی کرد.

  2. تمرکز بر وظیفه: پردازشگر فرمان می‌تواند بر پیچیدگی منطق Domain (نوشتن) تمرکز کند، درحالی‌که پردازشگر پرس‌وجو می‌تواند تنها بر کارایی بازیابی داده (خواندن) متمرکز باشد.

  3. مقیاس‌پذیری: می‌توان عملیات خواندن و نوشتن را به طور مستقل از هم مقیاس کرد.

 

 

الگوی واسط به‌عنوان ستون فقرات CQRS

در معماری CQRS، هر فرمان یا پرس‌وجو به پردازشگر (Handler) مربوط به خود نیاز دارد. در یک سیستم بزرگ، کامپوننت ارسال‌کننده فرمان (مثلاً یک کنترلر API) نباید بداند کدام Handler مسئول اجرای آن فرمان است. اینجاست که الگوی واسط وارد عمل می‌شود.

 

واسط به‌عنوان Dispatcher (توزیع‌کننده)

الگوی واسط، واسطه‌ای بین فرستنده (Sender) و گیرنده (Receiver) ایجاد می‌کند. در CQRS، این واسط نقش توزیع‌کننده (Dispatcher) پیام را بازی می‌کند.

  1. ارسال پیام: یک کنترلر API، یا هر سرویس دیگری، یک فرمان (Command) یا پرس‌وجو (Query) ایجاد کرده و آن را به واسط ارسال می‌کند.

  2. وظیفه واسط: واسط بر اساس نوع فرمان/پرس‌وجو، با استفاده از مکانیزم بازتاب (Reflection) یا یک رجیستری (Registry) داخلی، پردازشگر مربوطه را شناسایی می‌کند.

  3. اجرای منطق: واسط، فرمان/پرس‌وجو را به پردازشگر صحیح (Handler) ارجاع می‌دهد تا عملیات انجام شود.

مثال: کنترلر تنها فراخوانی می‌کند _mediator.Send(new CreateOrderCommand(...)). کنترلر نیازی به CreateOrderCommandHandler ندارد و آن را نمی‌شناسد. این جدایی وظایف، وابستگی را به صفر می‌رساند.

 

کاهش Coupling (پیوند)

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

  • بدون واسط: کنترلر مجبور است هر CommandHandler یا QueryHandler را مستقیماً تزریق (Inject) کند. این موضوع تزریق‌های زیادی در Constructor کنترلر ایجاد می‌کند (Constructor Injection Hell).

  • با واسط: کنترلر تنها یک وابستگی دارد: IMediator. این وابستگی واحد، رابط بین لایه ارائه (Presentation Layer) و لایه دامنه/برنامه‌کاربردی (Domain/Application Layer) را بسیار تمیز و ساده نگه می‌دارد.

 

پیاده‌سازی میان‌بر (Pipeline Behaviors)

یکی از قدرتمندترین مزایای استفاده از یک واسط (مانند کتابخانه‌های مشهور MediatR در دات‌نت یا پیاده‌سازی‌های مشابه در جاوا و پایتون) این است که امکان تزریق رفتارهای میان‌بر (Pipeline Behaviors) را فراهم می‌کند.

این میان‌برها می‌توانند قبل یا بعد از اجرای Handler اصلی اجرا شوند و وظایفی را مانند موارد زیر انجام دهند:

  • اعتبارسنجی (Validation): بررسی معتبر بودن داده‌های فرمان، قبل از رسیدن به Handler.

  • احراز هویت/مجوز (Authentication/Authorization): بررسی اینکه کاربر اجازه اجرای فرمان را دارد.

  • کشینگ (Caching): مدیریت کش برای پرس‌وجوها (در لایه Query).

  • ثبت وقایع (Logging): ثبت اطلاعات مربوط به شروع و پایان اجرای یک فرمان.

  • مدیریت تراکنش (Transaction Management): باز کردن و بستن تراکنش‌های دیتابیس حول اجرای فرمان.

این قابلیت، منطق کراس-کاتینگ (Cross-Cutting Concerns) را از Handlerهای اصلی دور نگه می‌دارد و آن‌ها را تنها بر منطق کسب‌وکار متمرکز می‌کند (Single Responsibility Principle).

 

۳.۴. تسهیل پیاده‌سازی Event Sourcing (اختیاری)

در پیاده‌سازی‌های پیشرفته CQRS که از Event Sourcing نیز استفاده می‌کنند، واسط می‌تواند به عنوان یک توزیع‌کننده وقایع (Event Dispatcher) عمل کند.

  1. پس از اجرای موفقیت‌آمیز یک فرمان، Handler مربوطه ممکن است یک یا چند رویداد دامنه (Domain Event) را منتشر کند (مثلاً OrderCreatedEvent).

  2. واسط، این رویداد را دریافت کرده و آن را به تمام پردازشگرهای رویداد (Event Handlers) که به این رویداد علاقه‌مندند، ارسال می‌کند.

این مکانیسم امکان ایجاد اثرات جانبی (Side Effects) یا تعاملات غیرهم‌زمان (Asynchronous Interactions) بین بخش‌های مختلف سیستم را بدون وابستگی مستقیم فراهم می‌کند (مانلاً ارسال ایمیل پس از ثبت سفارش).

 

جمع‌بندی و نتیجه‌گیری

الگوی واسط یک ابزار قدرتمند برای جداسازی دغدغه‌ها (Separation of Concerns) و کاهش Coupling در سیستم‌های نرم‌افزاری است. در یک معماری سنتی، این الگو به مدیریت تعاملات پیچیده بین کامپوننت‌های UI یا سرویس‌های Backend کمک می‌کند.

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

  1. کنترلرها را از جزئیات پیاده‌سازی Handlerها جدا می‌کند.

  2. امکان پیاده‌سازی آسان میان‌برهای رفتاری برای وظایف کراس-کاتینگ (مانند اعتبارسنجی و کشینگ) را فراهم می‌آورد.

  3. مسیر را برای پیاده‌سازی الگوی Event Dispatcher و در نهایت Event Sourcing هموار می‌سازد.

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

 
لینک استاندارد شده: rdYh7PbPED

0 نظر

    هنوز نظری برای این مقاله ثبت نشده است.
جستجوی مقاله و آموزش
دوره‌ها با تخفیفات ویژه