ویرگول
ورودثبت نام
میر مجتبی هاشمی جنتی
میر مجتبی هاشمی جنتیدانش آموخته مهندسی نرم افزار | فعال در صنعت | یک برنامه نویس ساده
میر مجتبی هاشمی جنتی
میر مجتبی هاشمی جنتی
خواندن ۵ دقیقه·۲ ماه پیش

الگوی SAGA در نرم افزار

SAGA Pattern؛ مدیریت تراکنش‌های توزیع‌شده از تئوری تا پیاده‌سازی عملی

سلام دوستان. میخوام مقدمه ای در مورد SAGA بدم و سپس بریم جلو. احتمالاً این مقاله برای افراد غیرفنی زیاد جالب نباشه چون خیلی تخصصی داریم در مورد این پترن، یوزکیس ها، Event ها و Message ها صحبت میکنیم و اساساً مخاطب هدف، معمار ها و تحلیل گران نرم افزار، توسعه دهندگان و علاقه مندان به این موضوعات است.

شروع کنیم. بسم الله.

با حرکت سیستم‌ها به سمت معماری مایکروسرویس و Event-Driven، یکی از اولین مفاهیمی که به بن‌بست می‌رسد، «تراکنش» به معنای کلاسیک آن است. دیگر خبری از یک دیتابیس واحد و Transactionهای ACID نیست که بتوان با یک commit یا rollback همه‌چیز را کنترل کرد. در چنین فضایی، نیاز به الگویی داریم که بتواند تراکنش‌های بلندمدت و توزیع‌شده را مدیریت کند؛ جایی که چند سرویس مستقل باید در نهایت به یک نتیجه بیزینسی سازگار برسند. اینجاست که SAGA Pattern وارد می‌شود.

SAGA صرفاً یک الگوی تئوریک نیست، بلکه ستون فقرات بسیاری از سیستم‌های بزرگ و واقعی است. در این مقاله، سعی خواهم کرد SAGA را عمیق، فنی و کاملاً عملی بررسی کنیم؛ از مدل ذهنی و مفاهیم پایه تا پیاده‌سازی واقعی با ابزارهایی مثل Axon Framework و دیگر ابزار ها. مسئولیت پوزیشن های مختلف رو هم درگیر خواهم کرد تا درک موضوع راحت تر بشه. یعنی مسئولیت های معمار و تحلیل گر نرم افزار رو به همراه مسئولیت های توسعه دهنده ها، معماران و ادمین های پایگاه داده و ... بررسی میکنیم.

SAGA Pattern
SAGA Pattern

مسئله اصلی؛ چرا Transaction کلاسیک جواب نمی‌دهد؟

در یک سیستم توزیع‌شده، هر سرویس دیتابیس خودش را دارد. فرض کنید ثبت سفارش شامل چند مرحله است: ایجاد سفارش، پرداخت، رزرو موجودی و ارسال. هر کدام از این مراحل در سرویس جداگانه‌ای انجام می‌شود. اگر مرحله سوم شکست بخورد، دیگر نمی‌توان به‌سادگی Payment را rollback کرد، چون آن تراکنش مدت‌ها پیش commit شده است.

استفاده از Distributed Transaction و Two-Phase Commit در تئوری ممکن است، اما در عمل باعث coupling شدید، کاهش availability و مشکلات جدی در scale می‌شود. اگر بخواهید، میتونید اطلاعات بیشتری در مورد این موضوعات (تئوری CAP در پایگاه داده) در این مقاله بخونید. SAGA دقیقاً برای حل این مشکل طراحی شده است.

SAGA چیست؟ تعریف دقیق و فنی

SAGA یک تراکنش بیزینسی بلندمدت است که از چندین Local Transaction تشکیل می‌شود. هر Local Transaction در محدوده یک سرویس اجرا می‌شود و دیتابیس همان سرویس را تغییر می‌دهد. اگر تمام مراحل با موفقیت انجام شوند، SAGA کامل می‌شود. اما اگر یکی از مراحل شکست بخورد، SAGA با اجرای Compensating Transaction‌ها، اثر مراحل قبلی را خنثی می‌کند.

نکته کلیدی این است که Compensating Transaction الزاماً rollback فنی نیست، بلکه یک عملیات بیزینسی معکوس است. مثلاً Refund کردن پرداخت، نه undo کردن یک row در دیتابیس.

مدل ذهنی SAGA؛ State Machine بیزینسی

از دید فنی، SAGA را می‌توان به‌عنوان یک State Machine در نظر گرفت. هر SAGA یک state دارد و با دریافت Eventها بین stateها جابه‌جا می‌شود. این state نشان‌دهنده پیشرفت فرآیند بیزینسی است، نه وضعیت دیتابیس.

برای مثال، یک OrderSaga می‌تواند stateهایی مثل CREATED، PAID، INVENTORY_RESERVED و COMPLETED داشته باشد. هر transition با یک Event رخ می‌دهد و ممکن است باعث ارسال Command به سرویس دیگر شود.

انواع SAGA؛ Orchestration در مقابل Choreography

SAGA معمولاً به دو سبک اصلی پیاده‌سازی می‌شود. در مدل Choreography، هیچ موجودیت مرکزی وجود ندارد. هر سرویس به Eventهای سایر سرویس‌ها گوش می‌دهد و بر اساس آن تصمیم می‌گیرد. این مدل ساده‌تر به نظر می‌رسد، اما با بزرگ شدن سیستم، جریان بیزینسی پراکنده و سخت‌قابل‌درک می‌شود.

Choreography Based SAGA
Choreography Based SAGA

در مقابل، مدل Orchestration یک Coordinator مرکزی دارد که معمولاً خود SAGA است. این Coordinator تصمیم می‌گیرد مرحله بعدی چیست و چه Commandی باید ارسال شود. این مدل کنترل، مانیتورینگ و Debug بهتری فراهم می‌کند و در سیستم‌های پیچیده انتخاب رایج‌تری است.

Orchestration Based SAGA
Orchestration Based SAGA

SAGA و Event-Driven Architecture

SAGA به‌شدت با Event-Driven Architecture گره خورده است. ارتباط بین مراحل SAGA معمولاً از طریق Event انجام می‌شود. یک سرویس پس از انجام Local Transaction، Event مربوطه را منتشر می‌کند و SAGA با دریافت آن Event تصمیم می‌گیرد قدم بعدی چیست.

این رویکرد باعث loose coupling بین سرویس‌ها می‌شود و امکان scale مستقل هر سرویس را فراهم می‌کند.

Compensating Transaction؛ قلب تحمل خطا در SAGA

مهم‌ترین بخش SAGA، طراحی Compensating Transaction است. اگر این بخش به‌درستی طراحی نشود، کل SAGA عملاً بی‌معنا می‌شود. Compensating Transaction باید idempotent باشد و بتواند در شرایط retry و failure به‌درستی کار کند. اگر در مورد idempotency میخواهید اطلاعات بیشتری کسب کنید، در این مقاله، مفاهیم پایه رو مورد بررسی قرار دادیم.

همچنین باید پذیرفت که SAGA معمولاً به Eventual Consistency می‌رسد، نه Consistency آنی. این یک trade-off آگاهانه برای دستیابی به availability و scalability است.

پیاده‌سازی SAGA با Axon Framework

Axon Framework یکی از فریم‌ورک‌هایی است که SAGA را به‌صورت first-class پشتیبانی می‌کند. در Axon، SAGA با annotation تعریف می‌شود و lifecycle آن بر اساس Eventها مدیریت می‌شود.

هر SAGA instance معمولاً با یک Event شروع می‌شود، مثلاً Axon.OrderCreatedEvent . این Event را با یک association key به SAGA متصل می‌کند. از آن لحظه به بعد، هر Event مرتبط می‌تواند state داخلی SAGA را تغییر دهد و باعث ارسال Command جدید شود.

State داخلی SAGA می‌تواند در دیتابیس ذخیره شود یا حتی Event Sourced باشد، بسته به نیاز سیستم.

مثال واقعی؛ Order Processing Saga

فرض کنید کاربر سفارشی ثبت می‌کند. OrderCreatedEvent منتشر می‌شود و OrderSaga شروع می‌شود. Saga با ارسال ProcessPaymentCommand پرداخت را آغاز می‌کند. اگر PaymentSucceededEvent دریافت شود، Saga وارد مرحله رزرو موجودی می‌شود. اگر PaymentFailedEvent رخ دهد، Saga مستقیماً پایان می‌یابد.

اگر در مرحله رزرو موجودی خطا رخ دهد، Saga با ارسال RefundPaymentCommand اثر مرحله پرداخت را جبران می‌کند. در نهایت، سیستم بدون هیچ Transaction توزیع‌شده‌ای به وضعیت بیزینسی سازگار می‌رسد.

Persistence و Reliability در SAGA

از آنجا که SAGA معمولاً طول عمر بالایی دارد، persistence آن حیاتی است. State SAGA باید بعد از crash یا restart سیستم قابل بازیابی باشد. بسیاری از فریم‌ورک‌ها، از جمله Axon، این persistence را به‌صورت built-in فراهم می‌کنند. همچنین Message Delivery باید حداقل once باشد و SAGA باید برای duplicate Eventها آماده باشد.

چالش‌ها و Anti-Patternها

SAGA اگر اشتباه استفاده شود، می‌تواند به پیچیدگی بیش از حد منجر شود. استفاده از SAGA برای عملیات ساده، طراحی Compensating ضعیف یا تبدیل SAGA به God Object از جمله anti-pattern های رایج هستند.

SAGA باید نماینده یک فرآیند بیزینسی مشخص باشد، نه جایگزین مستقیم Transaction دیتابیس.

جمع‌بندی

SAGA Pattern راه‌حل استاندارد و عملی برای مدیریت تراکنش‌های توزیع‌شده در سیستم‌های مدرن است. این الگو با پذیرش Eventual Consistency و استفاده از Compensating Transaction، امکان ساخت سیستم‌هایی scalable، resilient و قابل توسعه را فراهم می‌کند.

اگر سیستم و پروژه ما شامل چند سرویس مستقل با فرآیندهای بیزینسی پیچیده است، SAGA نه یک انتخاب، بلکه یک ضرورت معماری است و معمار ما باید در مرحله معماری و تحلیل، اون رو در نظر بگیرد.

نرم افزارsagaمهندسی نرم افزار
۴
۰
میر مجتبی هاشمی جنتی
میر مجتبی هاشمی جنتی
دانش آموخته مهندسی نرم افزار | فعال در صنعت | یک برنامه نویس ساده
شاید از این پست‌ها خوشتان بیاید