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

Event-Driven Architecture
یکی از پاسخها استفاده از معماری رویداد محور است. در این معماری به جای اینکه سرویس سفارش خودش، انبار، ایمیل و بخش مالی پروژه را صدا بزند( که اگر یکی از این سرویسها خراب باشد یا کند کار کند، کل فرایند میایستد!) فقط اعلام میکند که: "سفارش جدید ثبت شد" و باقی سرویس ها به این خبر گوش میدهند و هرکدام وظیفه خودشان را انجام میدهند . درواقع در این مدل وقتی هر سرویس کاری را انجام میدهد یک " رویداد " منتشر میکند. در همچین سیستمی اگر برای مثال سرویس ایمیل موقتا کار نکند، وقتی دوباره بالا آمد و آماده کار بود همان رویداد را میبیند و ایمیل را ارسال میکند. در این روش وابستگی میان سرویس ها کم است و سیستم مقایسپذیرتر و در برابر خرابی مقاومتر میشود. نتفلیکس و اوبر از جمله شرکتهایی هستند که به شکل گسترده از این معماری استفاده میکنند.

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

Event Sourcing
حال سوال مهمتر: اگر یک باگ باعث شود موجودی حساب اشتباه محاسبه شود، چطور بفهمیم اشتباه از کجا شروع شده؟ یا اگر یک مشتری ادعا کند که تراکنشی انجام نشده، چطور میتوانیم دقیقا نشان دهیم چه اتفاقی افتاده است؟
Event Sourcing پاسخ این مشکلات است و یعنی به جای ذخیره وضعیت فعلی، همه اتفاقاتی که منجر به وضعیت فعلی است را ذخیره کنیم! در این حالت میتوانیم هر لحظه تاریخچه کامل و قابلیت امکان برگشت به هرلحظه قبلی را داشته باشیم. این قابلیت در سیستم های بانکی، مالی، سبد خرید و زنجیره تامین بسیار ارزشمند است و البته معایبی هم دارد! وقتی از Event Sourcing استفاده کنیم خطاهای منطقی راحتتر پیدا شده ولی حجم داده خیلی زیاد میشود و پیچیدگی هم بیشتر.

Message Queue
رویداد ها چطور از یک سرویس به یک سرویس دیگر میرسند؟ Message Queue مانند یک صندوق پستی عمل میکند و نقش یک واسط مطمئن را میان تولید کننده و مصرف کننده پیام دارد. وقتی Message Queue داریم فرستنده پیامش را در صف میگذارد و بدون اینکه منتظر بماند به کار خود ادامه میدهد. گیرنده نیز هر موقع که آماده بود و توانایی پردازش داشت پیام را از صف برمیدارد و پردازش میکند. به این نحو تولید کننده و مصرفکننده مستقل از هم کار میکنند و اگر یکی از آنها خراب شود کار دیگری مختل نمیشود.
از نمونه ابزار های محبوب که برای Message Queue استفاده میشود میتوانیم به RabbitMQ و Kafka اشاره کنیم. که تفاوتهایی نیز با هم دارند. برای مثال RabbitMQ یک صف پیام سنتی است یعنی بعد از خواندن پیام، پیام از صف پاک میشود. این ویژگی برای کارهایی که نیاز به فقط یک بار پردازش دارند مناسب است ولی در Kafka ماجرا متفاوت است. پیام ها بعد از خوانده شدن از صف پاک نمیشوند و برای مدت معینی نگهداری میشوند. مصرفکننده میتواند هر بار از یک نقطه مشخص پیامها را دوباره بخواند. این ویژگی برای سناریوهایی که نیاز به تاریخچه کامل رویداد داریم (Event Sourcing) عالی است.
این تفاوت در این دو نوعMessage Queue در کاربردهای مختلفی حتی ممکن است در کنار هم استفاده شوند. مثلا در یک سیستم ثبت سفارش، وقتی ثبت سفارش انجام میشود پیام به یک صف مثل Kafka میرود که برای تحلیل های بعدی مثل ارسال به انبار، ارسال ایمیل و ... آماده باشد ولی اگر یک سرویس بخواهد فوری کاری را انجام دهد و جوابی بدهد مثل اعتبارسنجی کارت بانکی میتواند از RabbitMQ استفاده کند.
منابع:
Kafka Docs
RabbitMQ Docs
Fowler (Event Sourcing, CQRS)