تا قبل از آشنایی با مفاهیم event sourcing، به داده های خود به شکل last state نگاه می کردیم. یک سفارش را در سیستم خود در نظر بگیرید که اتفاقات مختلفی مانند ثبت، تایید و ویرایش روی آن افتاده است. این داده ها در دیتابیس همیشه به صورت آخرین وضعیت مشاهده می شد و تغییراتی که روی اطلاعات سیستم ما اتفاق افتاده بود را معمولا از طریق لاگ ها بررسی می کردیم. برای مثال چه کسی این کاربر را تایید کرده، چه وقتی تایید شده، وضعیت قبل آن چه بوده و غیره. از طریق event sourcing نگاه ما به نگهداری داده ها تغییر می کند.
در event sourcing، داده های سیستم را به شکل یک سری از رویدادهایی که در سیستم اتفاق افتاده است نگهداری می کنیم. در نظر داشته باشید که هدف ما در event sourcing، نگهداری لاگ نیست. برای مثال یک کاربر را در نظر بگیرید که در سیستم ما ثبت نام کرده، بعد از آن توسط مدیر سیستم فعال شده و بعد از آن کاربر آدرس خود را ویرایش کرده است. در مدل event sourcing، داده ها به جای اینکه به شکل آخرین وضعیت ذخیره شوند، به شکل یک سلسله از تغییرات ذخیره می شوند.
ما در نرم افزار خود دو مفهوم Commands و Events را داریم. commandها، دستوراتی هستند که از طریق سیستم ها یا کاربران دیگر، برای تغییرات بر روی سیستم ما اجرا می شوند. eventها، در نتیجه اجرای commandها اتفاق می افتند.
در event sourcing از مفهوم eventها برای مدل کردن و نگهداری داده ها استفاده می کنیم.
نکته ای که در event sourcing وجود دارد، موضوع last state یا وضعیت آخر است. از طریق اجرای eventهایی که در سیستم و بر روی یک رکورد اتفاق افتاده، می توانیم به وضعیت فعلی آن رکورد برسیم.
اما چه دلایلی وجود دارد که از event sourcing استفاده کنیم؟ در ادامه یک مثال را بررسی می کنیم که ارزش داده ها را در یک سیستم نشان می دهد.
در سمت چپ تصویر، یک سیستم ticketing را در یک RDBMS مدل کردیم. در جدول Ticket، سحر به امید یک تیکت زده که جواب آن را گرفته و تیکت بسته شده است. در جدول TicketPosts هم پست های مربوط به آن تیکت را می بینید.
در سمت راست مدل event source این جریان را می بینید. تیکت باز شده، یک پست ارسال شده، امید جواب داده و تیکت را بسته است. سحر مجدد آن را باز کرده و اعلام کرده که هنوز مشکل دارد، امید مجدد جواب داده و تیکت را بسته است.
زنجیره اتفاقات را در این دو حالت ملاحظه می کنید. سوالی مطرح شده که آیا مدل سمت چپ و سمت راست با هم برابر هستند یا نه؟ اگر به داده های ذخیره شده دقت کنید، در مدل سمت چپ، last state ذخیره می شود و خیلی از داده ها را از دست دادیم. هر update و delete که روی سیستم انجام می شود، یکسری از داده ها را از بین می برد. در event source نگاه به این صورت هست که ما از update و delete استفاده نمی کنیم و بر روی eventها عملیات append اتفاق می افتد.
در نظر بگیرید که تعدادی از پشتیبان ها، بدون اینکه منتظر جواب مشتری بمانند، تیکت ها را می بندند و از این طریق قصد دارند rate خود را بالا ببرند. فرض کنید مدیر مجموعه به این نتیجه رسیده و از برنامه نویسان خواسته است که در قالب یک گزارش، پشتیبان هایی که بدون گرفتن جواب از مشتری، تیکت ها را می بندند را مشخص کنند.
در مدل سمت چپ، به عنوان یک برنامه نویس باید یک تاریخ اضافه کنیم و یک کوئری هم ایجاد کنیم و از این لحظه به بعد می توانیم متوجه این اتفاق شویم. اما تا این لحظه که این امکان را نداشتیم یکسری از اطلاعات را از دست دادیم و نمی توانیم خروجی را بدهیم.
اما در مدل event source در این لحظه تمام این داده ها را داریم و می توانیم گزارش را بدست آوریم و همچنین می توانیم ویژگی هایی اضافه کنیم که در سیستم قبل قابل پیاده سازی نیست. برای مثال می توانیم نشان دهیم که دو سال قبل در یک تاریخ و ساعت مشخص چه اتفاقی افتاده است. از طریق replay کردن eventها، می توانیم داده های مدنظر خود را بدست آوریم.
سوالی که پیش میاد این است که ما در سیستم خود لاگ داریم و می توانیم در آن تمام این اتفاقات را ببینیم. تفاوت در چیست؟ در جواب این سوال، آیا شما حاضرید تمام داده های موجود خود را پاک کنید و مجدد آن ها را از روی لاگ بازیابی کنید؟ قطعا جواب منفی است. به این دلیل که ما به لاگ ها اعتماد نداریم و این احتمال را می دهیم که یکسری از داده ها ممکن است در لاگ نباشد.
مدل event source، مجموعه تغییرات را به عنوان داده های اصلی نگهداری می کند. بنابراین اولین موضوعی که به آن می پردازیم Data Loss می باشد. در event sourcing هیچ داده ای را از دست نمی دهیم و از دست ندادن داده ها باعث ایجاد business value بسیاری می باشد که برای تیم ما ایجاد می کند.
موضوع دومی که به آن می پردازیم Modeling می باشد. وقتی که به خیلی از domainها نگاه می کنیم که از قدیم وجود داشتند، مثل حسابداری، حمل و نقل، بیمه، انبارداری و غیره، به این موضوع می رسیم که این سیستم ها ذاتا مفهومی به نام current state ندارند و مدل آنها نگهداری تغییرات می باشد. بنابراین این سیستم ها ماهیتا event source هستند.
موضوع بعدی Domain Analysis و شناخت یک سامانه است. زمانی که event sourcing وارد پروژه می شود، اولین موردی که در تحلیل سیستم حذف می شود، Data Structure و چگونگی نگهداری داده ها است. و بیشتر به این مساله فکر می کنیم که چه eventهایی در سیستم داریم. روش هایی برای موضوع شناخت domainها مثل EventStorming داریم که از مفهوم event modeling استفاده می کنند.
یکی از قابلیت های event sourcing موضوع Time Traveling می باشد. اگر داده ها در سیستم را یک رشته ای از اتفاقات در نظر بگیریم، می تونیم تا یک تاریخ مشخص eventها را replay کنیم و در زمان سفر کنیم. این کار دو مزیت functionality و debugging را برای ما به همراه خواهد داشت. برای مثال می توانیم به git-bisect اشاره کنیم. قابلیت functionality به ما کمک می کند متوجه شویم چه کسی سورس را تغییر داده و از طریق debugging در شرایطی که سورس ما باگ داشته باشد می توانیم به وضعیت خطا برسیم و آن را رفع کنیم.
از اولین موضوعاتی که باعث بوجود آمدن event sourcing شده است موضوع performance بوده بنابراین event sourcing دارای performance خوبی است. سوالی که پیش میاد این است که چطور ممکنه replay کردن eventها دارای performance خوبی باشد؟ event sourcing در حوزه performance به دو موضوع Write و Read می پردازد. نوشتن داده ها در event sourcing به شکل append می باشد و رکوردی update نمی شود و سرعت بالایی دارد. اما عملیات خواندن در زمینه performance بیشتر مورد توجه قرار می گیرد به همین دلیل موضوع CQRS را اکثر اوقات در کنار event sourcing می بینیم. در CQRS، تفکیک Command و Query در دستور کار قرار می گیرد. اگر CQRS در event sourcing مورد استفاده قرار بگیرد ما در زمینه خواندن داده ها نیز performance بالایی را خواهیم داشت.
در event sourcing ما می توانیم از eventهایی که اتفاق می افتند تفسیرهای متفاوتی داشته باشیم. برای مثال، با نوشتن یک projection، کاربران فعال را در جایی مانند دیتابیس یا مموری نگهداری می کنیم. یا دیتاها را به گونه ای که برای نمایش در تب یا گراف مناسب باشد نگهداری کنیم. event sourcing به ما این قابلیت را می دهد که در هر لحظه یک projection جدید ایجاد کنیم و داده ها را به شکلی که می خواهیم نگهداری کنیم.
در event sourcing، نرم افزار ما به دو بخش Command و Query تقسیم می شود. هر آن چیزی که باعث تغییر در وضعیت سیستم ما شود، یک command محسوب می شود. یک command می تواند از سمت کاربر، سیستم بیرونی و یا در ازای رخ دادن یک event ارسال شود. قانون اولی که برای commandها وجود دارد این است که باید به صورت امری نوشته شوند.
یکی از anti patternهایی که در event sourcing اتفاق می افتد، نگاه CRUD روی commandها و queryها می باشد. بعد از مدتی ممکن است ما با تعداد زیادی از commandها و queryهای CRUD روبرو شویم. قانون دوم این است که برای ایجاد commandها باید بر روی Business تمرکز کنیم.
در مقابل commandها، اجزایی داریم که به آن ها Command Handler گفته می شود و وظیفه آنها دریافت و اجرای commandها است. در حوزه event sourcing، تمام commandها از طریق CommandBus به command hadlerها می رسند.
همانطور که گفته شد، از طریق replay کردن eventها، می توانیم به وضعیت فعلی aggregate خود برسیم. اما در لحظاتی ممکن است تعداد eventهای ما زیاد شود و خواندن و اجرای آنها کار زمان بری شود. در این مواقع می توانیم از snapshot استفاده کنیم. در زمان استفاده از snapshot، تعداد زیادی از eventها خوانده و اجرا می شود و وضعیت aggregate به شکل snapshot نگهداری می شود. این کار کمک می کند که load کردن های بعدی، از snapshot انجام شود. برای مثال در سیستم های مالی، بستن سال مالی می تواند یک snapshot باشد.
در چه مسائلی بهتر است از event sourcing استفاده کنیم؟
به طور کلی برای استفاده از event sourcing باید به اهمیت تاریخچه داده ها، performance ،regulatory، اهمیت داده ها و ماهیت دامین ها توجه شود. نکته ای که باید به آن توجه شود این است که event sourcing را به عنوان یک system architecture در نظر نمی گیریم. استفاده از event sourcing برای کل سامانه، over engineering تلقی می شود.
پایان