در این مقاله قصد داریم تا الگوی Event Sourcing را معرفی کرده و جزییات آن را با هم برسی کنیم. این الگو در صنعت نرم افزار کاربرد دارد و مانند هر الگوی دیگری، استفاده از آن نیز مزایا و معایبی دارد که در انتهای مقاله به آن خواهیم پرداخت. همانطور که از اسم الگوی Event Sourcing پیداست، تاکید این الگو بر روی ذخیره رویداد ها یا تغییراتی است که بر روی یک موجودیت اتفاق می افتد. در ادامه بیشتر با جزییات این الگوی جالب آشنا می شویم.
این الگو بر ذخیره رخداد هایی که روی داده ها انجام شده اند تاکید دارد. هر زمان وضعیت یک موجودیت تغییر می کند، یک رخداد جدید به محل ذخیره سازی رخداد ها اضافه خواهد شد:
همانطور که در شکل فوق مشخص است، هر گونه رخدادی که در سیستم اتفاق می افتد، در لیست رخدادها یا همان Event Store ذخیره می شود. سپس می توان با مرور لیست رخداد ها، وضعیت کنونی یک موجودیت را فهمید. Event Store جایی است که تمام رخدادهایی که روی هر موجودیتی در سیستم اتفاق افتاده است در آن جا ذخیره می شود. "اضافه شدن" یا "کم شدن" از لیست، مثال هایی از رخدادها هستند که در Event Store ذخیره می شوند. وقتی از این الگو استفاده می کنیم، به جای آنکه وضعیت کنونی یک موجودیت را ذخیره کنیم، لیست تاریخچه رخداد ها و اعمالی که روی آن اتفاق افتاده است را ذخیره می کنیم. سپس به کمک این لیست می توانیم به راحتی به وضعیت کنونی موجودیت نیز پی ببریم.
در ادامه یک مثال از الگوی Event Sourcing می زنیم تا درک آن آسان تر شود:
فرض کنید مشتری یک فروشگاه اینترنتی هستی و قصد دارید تا یک کالا را خریداری کنید. وقتی این کالا را انختاب کردید، اقدام به ثبت سفارش می کنید. پس از اینکه پرداخت موفق هم انجام دادید، سفارش شما ثبت می شود و وضعیت سفارش شما در حالت "در انتظار تایید" قرار می گیرد. در ادامه حالت های مختلفی می توانند برای خرید شما پیش بیایند، مثلا وضعیت سفارش شما به "تایید فروشنده" تبدیل می شود، و سپس پس از اینکه فروشنده کالا را ارسال کرد، وضعیت سفارش شما به "در حال ارسال" تبدیل می شود. در نهایت وقتی شما سفارشتان را تحویل گرفتید، وضعیت سفارش شما "تحویل شده" خواهد شد.
برخی اوقات ما نیاز داریم که به تاریخچه تغییرات وضعیت سفارش دسترسی داشته باشیم. این تاریخچه در Event Store موجود است. به عنوان مثال شما می خواهید ببینید که در چه روزی و چه ساعتی فروشنده سفارش شما ارسال کرده است. با یک نگاه ساده به Event Store این زمان را متوجه خواهیم شد. این یک مثال ساده از زمانی بود که ما به داشتن لیست تغییرات وضعیت سفارش نیاز داریم.
آنچه واضح است این است که Event Store باید دارای یک قالب و چارچوب مشخص باشد:
همان طور که مشاهده می شود، هر خط شامل اطلاعاتی مانند شناسه رخداد، جزییات رخداد، نوع موجودیت حاضر در رخداد و ... می باشد. به طور مفهومی، هر خط از Event Store شکل بالا می تواند بیانگر جملات زیر باشد:
"سفارش ثبت شد"
"سفارش تایید شد"
"سفارش ارسال شد"
"سفارش تحویل شد"
و ...
کاربرد دیگر Event Store، اطلاع یافتن سایر سرویس ها یا سامانه ها از رخداد ها به کمک Subscribe کردن روی آن است. برای این که این امر محقق شود، هر بخش از سامانه باید اتفاقاتی که در موجودیت هایش رخ می دهد را در معرض عموم قرار دهد تا بقیه از بیرون به آن دسترسی داشته باشند. اما این اتفاق به صورت زیبا تری به کمک اضافه کردن الگوی CQRS رخ خواهد داد که در ادامه مقاله آن را خواهیم دید.
در همین مثال حالتی را در نظر بگیرید که چند سرویس دیگر منتظر تغییر وضعیت سفارشات هستند تا اقدامات مربوطه را انجام بدهند. مثلا سرویس اطلاع رسانی منتظر است تا اگر وضعیت سفارش تغییر کرد، به مشتری اطلاع بدهد. در این حالت سرویس اطلاع رسانی می تواند بر روی Event Store یا همان لیست تفییرات وضعیت ها Subscribe کرده و به محض تغییر وضعیت، خودش متوجه تغییر شود و اقدامات مربوطه را انجام دهد. از جمله این اقدامات می توان به این مورد اشاره کرد که برای مشتری پیامک ارسال می شود. همچنین چندین سرویس دیگر مانند سرویس حمل و نقل و ... نیز می توانند Subscribe کنند.
توجه داشته باشید که هنگامی که از الگوی Event Sourcing استفاده می کنیم، در Event Store فقط تغییرات یا همان به روز شدن ها ذخیره نمی شود. بلکه هر نوع عمل <<اضافه شدن>>، <<حذف شدن>> و <<به روز شدن>> که باعث تغییر در موجودیت ها می شوند می توانند در این منبع ذخیره شود.
شاید تا به اینجا متوجه شده باشید که این الگو شباهت زیادی به مکانیزم های Log کردن در پایگاه داده دارد. اکثر پایگاه داده های امروزی (می توان گفت همه ی آن ها) دارای فایل Log یا همان Log File هستند که درون آن ها نیز تغییراتی که در پایگاه داده رخ داده نوشته می شود. پس تفاوت این الگو با Log چیست؟! در پاسخ باید بگوییم اولا فرمت Log های هر پایگاه داده با فرمت Log های پایگاه داده دیگر متفاوت است. ثانیا در برخی موارد Log File های یک نسخه از یک پایگاه داده، با Log File های نسخه پیشین همان پایگاه داده تفاوت هایی دارد! ثالثا ممکن است در Log File ها اطلاعاتی وجود داشته باشد که به کار نیایند و فقط باعث هدر رفت حافظه شوند. چرا که در بسیاری از پایگاه داده ها، به صورت پیش فرض Log ها فقط تا یک مدت مشخصی وجود دارند و پس از اتمام آن مدت به صورت خودگار پاک می شوند.
اما از مزیت ها Log File نسبت به الگوی Event Sourcing می توان به این نکته اشاره کرد که سرعت جستجو در آن بیشتر است. به همین دلیل است که الگوی CQRS را با الگوی Event Sourcing تلفیق میکنند تا سرعت جستجو در بین رکورد ها بیشتر شود. در ادامه تلفیق این دو الگو را برسی خواهیم کرد.
استفاده از این الگو به همراه الگوی CQRS بسیار رایج است (مخصوصا در معماری میکروسرویس). اگر با الگوی CQRS آشنایی داشته باشید، می دانید که در این الگو Command ها (نوشتن، به روز کردن، حذف کردن) از Query ها (خواندن) تفکیک می شوند.
فرض کنیم دو میکروسرویس داریم. هر کدام از آن ها Write DB و Read DB مخصوص خودشان را دارند. (اگر با Write DB و Read DB آشنایی ندارید حتما در مورد CQRS مطالعه کنید). پس از تلفیق این دو الگو، Write DB معادل با همان Event Store است. یعنی هر میکروسرویس یک پایگاه داده دارد که تغییرات اعمال شده بر روی موجودیت ها بر روی آن نوشته می شود و یک پایگاه داده دارد که فقط مخصوص خواندن این تغییرات است (طبق الگوی CQRS). در واقع پایگاه داده نوشتن با خواندن فرق دارد. اگر بر الگوی CQRS مسلط باشید می دانید که برای اینکه Write DB و Read DB با یکدیگر sync باشند، نیاز است تا تغییراتی که بر روی Write DB یک سامانه اتفاق می افتد، به کمک یک سیستم پیام رسانی به گوش Read DB همان سامانه هم هم برسد تا Read DB هم خودش را به روز کند. اما وقتی از الگوی Event Sourcing استفاده می کنیم، از آن جایی که تغییرات موجودیت های یک میکروسرویس می تواند برای سایر میکروسرویس ها هم ارزشمند باشد. وقتی CQRS نیز اضافه می شود، میکروسرویس ها می توانند به جای آن که از Event Store های هم دیگر Subscribe کنند، از Read Storage یا همان Read DB یکدیگر Subscribe کنند. حتی به جای این کار می توانند از یک صف که همه میکروسرویس ها Event Store خود را در آن منتشر کرده اند Subscribe کنند و یا این که یک پایگاه داده کلی داشته باشیم که از آن به عنوان view استفاده شود:
اگر از الگوی Event Sourcing استفاده نمی شد، میکروسرویس ها ناچار بودند تا تغییرات را از روی Log ها یکدیگر بفهمند (که معایب خودش را به همراه داشت). و اگر از الگوی CQRS استفاده نمی شد، دیگر Read Storage نداشتیم و میکروسرویس ها باید تغییرات را از روی Event Store هم دیگر می فهمیدند که این کارآیی را کاهش می داد و سرعت سیستم کمتر می شد.
الگوی Event Sourcing مزایای زیادی دارد که برخی از آن ها عبارت اند از:
در کنار مزایایی که الگوی Event Sourcing دارد، معایبی نیز مطرح است که برخی از آن ها عبارت اند از:
الگوی Event Sourcing الگویی است که مبنای آن ذخیره جزییات رخداد هایی است که بر روی موجودیت ها در یک پایگاه داده اتفاق می افتد. استفاده از این الگو بیشتر در معماری میکروسرویس و به همراه الگوی CQRS رایج است. استفاده از این الگو مزایا و معایبی به همراه دارد که باید با توجه به مزایا و معایب آن دید که استفاده از آن به صرفه است یا خیر. در انتها باید به این نکته اشاره کنیم که این الگو شباهت زیادی به سازوکار Log های پایگاه داده ها دارد که البته تفاوت های آن باعث شده تا هر کدام مزایا و معایب خودشان را داشته باشند.
[1] https://microservices.io/patterns/data/event-sourcing.html
[2] https://codeopinion.com/event-sourcing-example-explained-in-plain-english/
[3] https://martinfowler.com/eaaDev/EventSourcing.html
[4] https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing
[5] https://eventuate.io/whyeventsourcing.html
[6] https://www.lagomframework.com/documentation/1.6.x/java/ESAdvantage.html
[7] https://dzone.com/articles/microservices-with-cqrs-and-event-sourcing
[8] https://www.eventstore.com/blog/event-sourcing-and-cqrs
[9] M. Overeem, M. Spoor, S. Jansen, "The Dark Side of Event Sourcing: Managing Data Conversion", IEEE 24th International Conference on Software Analysis, Evolution and Reengineering (SANER), 2017
این مطلب، بخشی از تمرینهای درس معماری نرمافزار در دانشگاه شهیدبهشتی است
#معماری_نرم_افزار_بهشتی