مواقعی وجود دارد که فقط نمی خواهیم ببینیم کجا هستیم، بلکه می خواهیم بدانیم چگونه به آنجا رسیده ایم! Event Sourcing تضمین میکند که همه تغییرات در وضعیت برنامه به صورت دنبالهای از رویدادها ذخیره میشوند. نه تنها میتوان این رویدادها را جستوجو کرد، بلکه میتوان از گزارش رویداد برای بازسازی حالتهای گذشته و بهعنوان پایهای برای تنظیم خودکار وضعیت برای مقابله با تغییرات آینده استفاده کرد.
ایده اساسی Event Sourcing این است که اطمینان حاصل شود که هر تغییری در وضعیت یک برنامه کاربردی در یک شی رویداد ثبت می شود، و این اشیاء رویداد خودشان به ترتیبی که برای مدت عمر مشابه خود حالت برنامه اعمال شده اند ذخیره می شوند.
به عنوان یک مثال ساده، اعلان های حمل و نقل در نظر گرفته میشود. در این مثال کشتی های زیادی در دریاهای آزاد وجود دارد م و باید مکان آن ها مشخص شود. یک راه ساده برای انجام این کار، داشتن یک برنامه کاربردی ردیابی با کمک روشهایی است که این امکان را می دهد تا زمان حرکت و یا رسیدن کشتی به بندر مشخص شود.
در این حالت وقتی سرویس فراخوانی می شود، کشتی مربوطه را پیدا کرده و مکان آن را به روز می کند. شی از نوع کشتی وضعیت شناخته شده فعلی کشتی ها را ثبت می کنند.
معرفی Event Sourcing یک مرحله به این فرآیند اضافه می کند. اکنون این سرویس یک شی رویداد ایجاد میکند تا تغییر را ثبت کند و آن را برای به روز رسانی کشتی پردازش میکند.
تفاوت جالب زمانی است که به آنچه در برنامه پس از چند تغییر باقی می ماند نگاه کرد. چند تغییر ساده را فرض میشود:
کشتی "کینگ روی" سانفرانسیسکو را ترک می کند.
کشتی «پرنس ترور» وارد لس آنجلس می شود.
کشتی "کینگ روی" وارد هنگ کنگ شد.
با سرویس اولیه، فقط وضعیت نهایی مشخص میشود که توسط اشیاء کشتی گرفته شده است.
به کمک Event Sourcing هر رویداد نیز ذخیره میشود. درواقع دو چیز متفاوت نگهداری میشود، یک وضعیت برنامه و یک گزارش رویداد است.
مشخص ترین چیزی که با استفاده از Event Sourcing به دست میآید این است که اکنون گزارشی از تمام تغییرات موجود است. نه تنها می توان مکان هر کشتی را مشخص کرد، بلکه می توان فهمید کشتی کجا بوده است. با این حال این یک مزیت کوچک است. همچنین میتوان این کار را با نگهداشتن تاریخچه پورتهای گذشته در شی کشتی، یا با نوشتن در یک فایل گزارش هر زمان که کشتی حرکت میکند، انجام داد. هر دوی این ها می تواند تاریخچه کافی به ما بدهد.
کلید Event Sourcing این است که تضمین میشود تمام تغییرات در اشیاء دامنه توسط اشیاء رویداد آغاز می شود. این منجر به تعدادی امکانات می شود که می توان در بالای گزارش رویداد ایجاد کرد:
بازسازی کامل: میتوان وضعیت برنامه را به طور کامل کنار گذاشت و با اجرای مجدد رویدادها از گزارش رویداد در یک برنامه خالی، آن را دوباره ساخت.
پرس و جو زمانی: میتوان وضعیت برنامه را در هر نقطه از زمان تعیین کرد. در واقع، این کار با شروع از یک حالت خالی و اجرای مجدد رویدادها تا یک زمان یا رویداد خاص انجام می شود. میتوان با در نظر گرفتن خطوط زمانی متعدد (مشابه با انشعاب در یک سیستم کنترل نسخه) این کار را بیشتر انجام داد.
پخش مجدد رویداد: اگر متوجه شود یک رویداد گذشته نادرست بوده است، میتوان پیامدهای آن را با معکوس کردن آن و رویدادهای بعدی و سپس پخش مجدد رویداد جدید و رویدادهای بعدی محاسبه کرد. (یا در واقع با دور انداختن حالت برنامه و پخش مجدد همه رویدادها با رویداد صحیح به ترتیب.) همین تکنیک میتواند رویدادهایی را که به ترتیب اشتباه دریافت میشوند مدیریت کند - یک مشکل رایج در سیستمهایی که با پیامرسانی ناهمزمان ارتباط برقرار میکنند.
نمونه رایج برنامهای که از Event Sourcing استفاده میکند، سیستم کنترل نسخه است. چنین سیستمی اغلب از پرس و جوهای زمانی استفاده می کند. هر زمان که از Dump و Restore برای جابجایی میان فایلهای مخزن استفاده میشود، Subversion از بازسازی های کامل استفاده میکند. برنامه های سازمانی که از Event Sourcing استفاده می کنند نادرتر هستند.
سادهترین راه برای استفاده از Event Sourcing این است که یک حالت برنامه درخواستی را با شروع از یک وضعیت برنامه خالی و سپس اعمال رویدادها برای رسیدن به حالت مطلوب محاسبه کرد. به همان اندازه ساده است که بفهمیم چرا این روند کند است، به خصوص اگر رویدادهای زیادی وجود داشته باشد. در بسیاری از برنامهها، درخواست وضعیتهای برنامه اخیر رایجتر است، اگر چنین است، یک جایگزین سریعتر، ذخیره وضعیت فعلی برنامه است و اگر شخصی ویژگیهای ویژهای را که Event Sourcing ارائه میدهد میخواهد، آن قابلیت اضافی در بالا ساخته میشود.
حالت های برنامه را می توان در حافظه یا روی دیسک ذخیره کرد. از آنجایی که یک وضعیت برنامه صرفاً از گزارش رویداد قابل استخراج است، میتوان آن را در هر جای دلخواهی ذخیره کرد. سیستمی که در طول یک روز کاری مورد استفاده قرار میگیرد، می تواند در ابتدای روز از یک حالت فوری شروع شود و وضعیت فعلی برنامه را در حافظه نگه دارد. اگر خراب شود، رویدادهای فروشگاه را دوباره پخش می کند. در پایان روز کاری می توان یک حالت فوری جدید تهیه کرد. حالتهای فوری جدید را می توان در هر زمان به صورت موازی بدون پایین آوردن برنامه در حال اجرا ایجاد کرد.
سیستم رسمی ثبت می تواند گزارش رویداد یا وضعیت فعلی برنامه باشد. اگر وضعیت فعلی برنامه در یک پایگاه داده نگهداری شود، گزارش رویدادها ممکن است فقط برای ممیزی و پردازش ویژه وجود داشته باشد. به صورت دورهای، گزارشهای رویداد میتوانند رکورد رسمی باشند و هر زمان که نیاز باشد، میتوان پایگاههای اطلاعاتی را از آنها ساخت.
چندین گزینه در مورد اینکه محل قرار دادن منطق مدیریت رویدادها وجود دارد. انتخاب اولیه این است که منطق را در اسکریپت های تراکنش یا مدل دامنه قرار داد. طبق معمول، اسکریپت های تراکنش برای منطق ساده بهتر هستند و مدل دامنه زمانی بهتر است که همه چیز پیچیده تر شود.
منطق دامنه پردازش، منطق تجاری است که برنامه را تغییر میدهد. منطق انتخاب پردازش، منطقی است که انتخاب میکند کدام بخش از منطق دامنه پردازشی باید بسته به رویداد ورودی اجرا شود. میتوان این ها را با هم ترکیب کرد، اساساً این رویکرد اسکریپت تراکنش است، اما همچنین میتوان با قرار دادن منطق انتخاب پردازش در سیستم پردازش رویداد، آن ها را از هم جدا کرد و متدی را در مدل دامنه فراخوانی میکند که شامل منطق دامنه پردازش است. وقتی این تصمیم گرفته شد، مرحله بعدی این است که آیا منطق انتخاب پردازش را در خود شی رویداد قرار داد یا یک شی پردازشگر رویداد جداگانه باشد.
یکی از مواردی که داشتن یک پردازنده مجزا میتواند منطقی باشد، زمانی است که شی رویداد یک DTO است که با برخی ابزارهای خودکار که مانع از قرار دادن کد در رویداد میشود، سریالسازی و سریالزدایی میشود. در این مورد باید منطق انتخاب رویداد را پیدا کنید.
اگر نیازی به معکوس کردن رویدادها وجود نداشته باشد، میتوان یک مدل دامنه را از گزارش رویداد نادیده گرفت. معکوس کردن منطق این کار را دشوارتر می کند، زیرا مدل دامنه نیاز به ذخیره و بازیابی حالت قبلی دارد، که باعث می شود مدل دامنه از گزارش رویداد آگاه باشد.
معکوس کردن رویدادها
هنگامی که رویداد به شکل یک تفاوت نمایش داده می شود، معکوس کردن ساده ترین راه است. یک مثال از این میتواند «افزودن 10 دلار به حساب مارتین» در مقابل «حساب مارتین روی 110 دلار» باشد. در حالت اول می توان فقط با کم کردن 10 دلار معکوس کرد، اما در مورد دوم اطلاعات کافی برای ایجاد مجدد ارزش گذشته حساب نیست.
اگر رویدادهای ورودی از رویکرد ایجاد تفاوت پیروی نکنند، رویداد باید اطمینان حاصل کند که هر چیزی را که برای معکوس کردن در طول پردازش لازم است ذخیره میکند. می توان این کار را با ذخیره مقادیر قبلی روی هر مقداری که تغییر می کند یا با محاسبه و ذخیره تفاوت ها در رویداد انجام داد.
شایان ذکر است که تمام قابلیتهای معکوس کردن رویدادها را میتوان با بازگشت به یک حالت فوری گذشته و پخش مجدد جریان رویداد انجام داد، ممکن است تفاوت زیادی در کارایی ایجاد نشود، زیرا ممکن است اغلب در موقعیتی باشید که معکوس کردن چند رویداد بسیار کارآمدتر از استفاده از حرکت رو به جلو در بسیاری از رویدادها باشد.
به روز رسانی های خارجی
یکی از عناصر پیچیده در Event Sourcing، نحوه برخورد با سیستم های خارجی است که از این رویکرد پیروی نمی کنند (و اکثر آنها از این رویکرد پیروی نمی کنند). هنگام ارسال پیامهای اصلاحکننده به سیستمهای خارجی و زمانی که درخواستهایی از سیستمهای دیگر دریافت میشود، مشکل ایجاد میشود.
بسیاری از مزایای Event Sourcing از توانایی پخش مجدد رویدادها به دلخواه نشات میگیرد، اما اگر این رویدادها باعث شود پیامهای بهروزرسانی به سیستمهای خارجی ارسال شود، همه چیز اشتباه میشود زیرا آن سیستمهای خارجی تفاوت بین پردازش واقعی و پردازش واقعی را نمیدانند. تکرار می شود.
برای انجام این کار، باید هر سیستم خارجی را با یک Gateway متصل کرد. این به خودی خود خیلی سخت نیست زیرا در هر صورت ایده کاملاً خوبی است. دروازه باید کمی پیچیدهتر باشد تا بتواند با پردازشهای تکراری که سیستم Event Sourcing انجام میدهد مقابله کند. برای بازسازیها و درخواستهای زمانی، معمولاً کافی است که دروازهها در طول پردازش پخش مجدد غیرفعال شوند. اگر از رویداد Retroactive استفاده شود، بهروزرسانیهای خارجی پیچیدهتر میشوند. تاکتیک دیگری که ممکن است در سیستم های خارجی مشاهده شود، بافر کردن اعلان های خارجی بر اساس زمان است.
پرس و جوهای خارجی
مشکل اصلی پرسوجوهای خارجی این است که دادههایی که آنها برمیگردانند روی نتایج مدیریت یک رویداد تأثیر میگذارند. برای مثال "اگر من برای 5 دسامبر نرخ ارز بخواهم و آن رویداد را در 20 دسامبر دوباره پخش کنم، به نرخ ارز در 5 دسامبر نیاز خواهم داشت نه نرخ بعدی."
یکی از رویکردها، طراحی دروازه به سیستم خارجی است به طوری که پاسخهای پرسشهای خود را به خاطر بسپارد و در حین پخش مجدد از آنها استفاده کند. برای کامل شدن این به این معنی است که پاسخ به هر پرس و جو خارجی باید به خاطر سپرده شود. اگر داده های خارجی به کندی تغییر کنند، ممکن است معقول باشد که تغییرات را فقط زمانی که مقادیر تغییر میکنند به خاطر سپرد.
تعامل خارجی
هم پرس و جوها و هم به روز رسانی سیستم های خارجی باعث پیچیدگی های زیادی با Event Sourcing می شود. چنین تعاملی ممکن است یک تماس خارجی باشد که هم نتیجه ای را برمی گرداند (یک پرس و جو) اما همچنین باعث تغییر حالت در سیستم خارجی می شود، مانند ارسال یک سفارش برای تحویل که اطلاعات تحویل آن سفارش را برمیگرداند.
تغییرات کد
میتوان سه نوع تغییر کد در نظر گرفت: ویژگیهای جدید، رفع نقص و منطق زمانی.
ویژگیهای جدید اساساً قابلیتهای جدیدی را به سیستم اضافه میکنند، اما چیزهایی را که قبلاً اتفاق افتاده است باطل نمیکنند. اینها را می توان در هر زمان کاملاً آزادانه اضافه کرد.
رفع اشکال زمانی رخ می دهد که به پردازش گذشته نگاه میشود و متوجه اشتباه شد. تنها کاری که باید انجام شود این است که اصلاح شود و رویدادها دوباره پردازش شود.
مورد سوم جایی است که خود منطق در طول زمان تغییر میکند، برای مثال «هزینه 10 دلاری قبل از 18 نوامبر و 15 دلاری پس از آن». این نوع چیزها باید در واقع وارد مدل دامنه شوند. مدل دامنه باید بتواند رویدادها را در هر زمان با قوانین صحیح برای پردازش رویداد اجرا کند. میتوان این کار را با منطق شرطی انجام داد، اما اگر منطق زمانی زیادی باشد، این کار مناسب نیست. مسیر بهتر این است که اشیاء استراتژی را به یک ویژگی زمانی متصل کنند. برای مثال "این رویداد را طبق قوانین 1 آگوست که در 1 اکتبر داشتیم معکوس کنید و آن را طبق قوانین 1 آگوست که اکنون داریم جایگزین کنید".
از جمله ابزارها میتوان Axon, Kafkaرا نام برد.
این تکنیک مناسب برای زمان خرابی است اگر منبع پایین دستی خراب شود، داده های آن را می توان از منابع رویداد ذخیره شده، بازسازی کرد. این تکنیک همچنین بسیار انعطاف پذیر است و هر نوع پیامی را می توان ذخیره کرد. همچنین بسیار مناسب برای گزارش دهی بلادرنگ است.
اما این تکنیک نیاز به یک زیرساخت شبکه بسیار کارآمد دارد. همچنین به روشی قابل اعتماد برای کنترل قالب های پیام نیاز است. رویدادهای مختلف، حاوی بارهای متفاوتی خواهند بود. برای تعریف و تعیین قالب های پیام برای یک رویداد خاص، باید یک منبع واحد وجود داشته باشد.
این تکنیک برای شرکت هایی مانند دیجیکالا و ترب مناسب است.