Event Sourcing- بخش اول
لیست مطالب آموزشی Event Sourcing:
- بخش اول مقدمهای بر Event Sourcing
- بخش دوم آشنایی مقدماتی با ساختار داخلی Event Store
- مقایسه رویکردهای State-Oriented و State-Transition
- مزیتهای Event Sourcing
- سلام به دنیا به روش Event Sourcing
- سلام به دنیا به روش Event Sourcing-بخش دوم
- بخش هفتم Projection
- بخش هشتم ویرایش ایونت ها در EventSourcing
- بخش نهم Message، Command یا Event
مقدمهای بر Event Sourcing
-
۱-۱ مقدمه
ایدهی Event Sourcing ایدهی جدیدی نیست. در اصل نیازمندیها و همچنین پیادهسازیهای سیستمهای Event Sourcing به سالهای بسیار بسیار دور بر میگردد. چه در دنیای نرمافزار و چه خارج از آن. اما چه چیزی ما را به سمت سیستمهای Event Sourcing سوق داده است؟ مسئلهی اصلی در تمامی این سیستمهای یکسان است و از یک الگوی ساده نشات میگیرد. ما نیازمند این هستیم که یک لاگ از تمامی تغییرات و وقایع رخداده در دامنهی مسئله به ترتیب زمان رخداد آنها، داشته باشیم. و سپس بتوانیم بر این لاگ پردازشهای مختلف انجام دهیم.
این همان چیزی است که ما را به سمت ایدههای آن چیزی که به اسم Event Sourcing میشناسیم سوق داد.
اگر به اکثر دامنههای مسائل نگاه کنیم خواهیم دید که اکثر آنها ذاتا EventSourced هستند. به عنوان مثال میتوان به دامین بانک، بیمه، حسابداری، پیرول، گمبلینگ، مالی، فروش، بورس و .. اشاره کرد.
ناگفته پیداست که این یک راهحل برای همه مسائل نیست. برخی سیستمها ماهیتا نیازمند همچنین نیازمندی هستند، و برخی دیگر سیستمها خیر.
اما این موضوع باعث بوجود آمدن نیازمندیهای بسیار دیگر شد. همچنین هدیه و البته هزینهی طراحی سیستم به این طریق شامل مزیتها و چالشهای فراوانی میباشد که باید قبل از پیادهسازی سیستم به این روش، از آنها مطلع باشیم. اگر برایتان جذاب است که بدانید Event Sourcing دقیقا چیست؟ طراحی سیستم به این روش چه مزیتهایی برای ما به همراه دارد؟ چالشها و نقاط کور طراحی سیستم به این روش کدامها هستند و چگونه میتوان بر آنها غلبه کنیم؟ در سری مقالات Event Sourcing به این سوالات و موارد بسیار دیگر که در عمل و بصورت واقعی با آنها مواجه خواهید شد خواهم پرداخت.
-
۱-۲ Event Sourcing چیست؟
Event Sourcing مجموعهای از الگوهای طراحی است که مطمئن میشود که بجای ذخیره آخرین وضعیت هر موجودیت در برنامه تمامی تغییرات بوجود آمده در state برنامه، صرفنظر از نوع تغییرات بصورت یک لاگ که به ترتیب زمانی مرتب شده است، ذخیره خواهد شد. این یک تغییر بزرگ در طراحی برنامه خواهد بود که مزیتهای اغوا کنندهای برای ما به دنبال خواهد داشت. در ادامه نه تنها میتوانیم کوئریهای مختلف برای مقاصد گوناگون در این لاگ بگیریم، بلکه میتوانیم time travelling کنیم، و در زمان به عقب برگردیم. میتوانیم state های گذشته برنامه را براحتی reconstitute کنیم.
فرض کنید که شما یک حساب بانکی دارید. طی بازههای زمانی شما با حساب بانکی خود کار میکنید. پول به آن واریز میکنید. از حساب خود پول برداشت میکنید. به حساب دیگری پول انتقال میکنید.
منبع تصویر: https://www.equitybank.com/debit-card/
در اینجا کد سادهی شده حساب بانکی در C# را مشاهده میکنید.
public class BankAccount
}
public decimal Balance { get; set; } = 0;
public Stack<AccountTransaction> Transactions { get; set; } = new();
public void Debit(decimal amount)
{
Balance -= amount;
Transactions.Push(Transaction.NewDebited(on: DateTime.Now, amount));
{
public void Credit(decimal amount)
}
Balance += amount;
Transactions.Push(Transaction.NewCredited(on: DateTime.Now, amount));
{
{
public record Transaction(DateTime On, decimal Amount)
}
; public static Credited NewDebited(DateTime on, decimal amount) => new(on, amount)
; public static Debited NewCredited(DateTime on, decimal amount) => new(on, amount)
; public record Credited(DateTime On, decimal Amount) : Transaction(On , Amount)
; public record Debited(DateTime On, decimal Amount) : Transaction(On, Amount)
{
طراحی بالا میتواند بصورت ساده به جدول زیر نگاشت شود. این وضعیت برنامه در زمان t0 می باشد.
فرض کنید در زمان t1 شما ۱۰۰ دلار به حساب خود واریز کردهاید. در اینحالت وضعیت برنامه بصورت خواهد شد.
حال فرض کنید در زمان t1 شما ۱۰۰ دلار دیگر به حساب خود واریز کردهاید. در اینحالت وضعیت برنامه بصورت خواهد شد.
در نهایت تصور کنید که در زمان t3 50 دلار از حساب خود برداشت کردهاید. وضعیت برنامه پس از این برداشت از حساب بصورت زیر خواهد شد.
همانطور که در مثال بالا مشاهده کردید، پس از هر بار تغییر در وضعیت برنامه، آخرین تغییرات نگهداری میشود. اما ببینیم در یک سیستم Event Sourced این سناریو به چه صورت هندل میشود.
همانطور که اشاره شد در یک سیستم Event Sourced ما تمامی تغییرات رخداده بر روی state برنامه را در فایل لاگ ذخیره میکنیم. مثال بالا را دوباره در نظر بگیریم. (در ارتباط با نامگذاری ایونتها در پستهای دیگر بصورت مفصلتر صحبت خواهم کرد.)
ابتدا ۱۰۰ دلار به حساب افزوده میشود. پس از پردازش شدن این سناریو در دومین برنامه، نتیجه کار در قالب یک ایونت به اسم Credited در فایل لاگ ذخیره میشود.
سپس کاربر مورد نظر ۱۰۰ دلار دیگر نیز به حساب خود اضافه میکند. این درخواست نیز پس از پردازش در دومین، بصورت یک اوینت دیگر به اسم Credited در فایل لاگ ذخیره خواهد شد.
در نهایت کاربر، ۵۰ دلار از حساب خود برداشت میکند. این درخواست بصورت یک ایونت دیگر به اسم Debited در فایل لاگ و در ادامهی سایر ایونتهای قبلی ذخیره خواهد شد. در نهایت نتیجه بصورت زیر تبدیل خواهد شد.
همانطور که ملاحضه میکند بر خلاف طراحی قبلی، ما هیچوقت آخرین state برنامه را آپدیت(override) نمیکنیم. و در عوض هر تغییری در وضعیت برنامه را در قالب یک ایونت به انتهای فایل لاگ خود اضافه میکنیم. همانطور که مشاهده میکنید حتی برداشت از حساب که نوعی از عملیات حذف یا deletion است، نیز در این روش بصورت یک تغییر افزایشی یا additive در فایل لاگ نمود پیدا میکند.
به عنوان مثالی دیگر فرض کنید که شما یک application سفارش آنلاین غذا دارید. مشتری آیتمهای زیر را به ترتیب انتخاب کرده است. زرشک پلو با مرغ، سالاد و نوشابه. وضعیت برنامه در Event Store در اینحابت بصورت زیر خواهد بود.
حال اگر مشتری، به عنوان سالاد حذف کند، این حذف بصورت یک ایونت دیگر به انتهای فایل لاگ افزوده خواهد شد.
-
۱-۳ شمای کلی Event Store
مکانیزم کلی Event Store ها بصورت زیر میباشد. یک لاگ شامل دنبالهای از رخدادهایی که در دومین رخ داده است، بصورت Event مدل شده، و سپس این Eventها بصورت زیر ذخیره خواهند شد.
این فایل همانطور که در مثال بالا مشاهده کردید، بصورت Append-Only است، بدین معنی که هر تغییری جدید به انتهای این فایل append شده و چیزی از این فایل لاگ حذف نخواهد شد(همیشه استثناهایی وجود دارد! در باره این استثناها صحبت خواهم کرد.).