Event Sourcing در Node.js
ﺯﻣﺎﻥ ﻣﻄﺎﻟﻌﻪ: 3 دقیقه

Event Sourcing در Node.js

Event Sourcing یک الگوی معماری قدرمتند برای مدیریت حالات اپلیکیشن های پیچیده که ممکنه نیاز به بازسازی, اجرای مجدد یا دیباگ کردن داشته باشند.

در این مقاله می خواهیم درباره ی این الگو در Node.js یاد بگیریم, اینکه چه چیزی هست و کجا میتوان ازش استفاده کرد. همچنین نگاهی به چند مثال با کدهای کوتاه خواهیم داشت.

Event Sourcing

Event Sourcing یک الگوی ساختاری نرم افزار است که قابلیت بازسازی حالات (states) قبلی (و حتی آخرین حالات) رو میده. بدین صورت که هر حالتی که تغییر میکنه بعنوان دنباله ای از رویدادها ذخیره میشه.

State یا حالت اپلیکیشن شما مثل شارژ حساب کاربری یا اشتراک در یک زمان خاص هست. این حالت فعلی فقط میتونه در حافظه وجود داشته باشد.

مثال خوبی از Event Sourcing میشه به سیستم های version control اشاره کرد که حالت فعلی رو ذخیره می کنند. حالت فعلی, معادل آخرین Source code و رویدادها هم معادل commit های شماست.

چرا Event Sourcing مفید است؟

بعنوان یک مثال و فرضیه شما دارید در یک سایت تبدیل پول آنلاین کار می کنید, که هر مشتری یک موجودی حساب داره. فرض کنید شما اول هفته سرکار می آیید و متوجه میشید که شما اشتباهی کردید و این اشتباه باعث شده از یک تبدیل پول اشتباه در طول هفته قبل استفاده کنید. در این مورد هر حساب که در طول هفته پیش پولی ارسال یا دریافت کرده در حالت اشتباه قرار دارد.

با وجود Event Sourcing نیازی به وحشت کردن نیست !

اگر سایت شما از event sourcing استفاده کنه شما میتونید موجودی حساب ها رو به حالت قبلی برگردونید, مقدار تبدیل پول رو درست کنید و تمام تغییرات هفته پیش رو بدرستی مدیریت کنید. 

سایر کاربردها

شما میتونید از رویدادها برای حسابرسی یا دیباگ تغییرات حالت در سیستم استفاده کنید. این همچنین برای مدیریت اشتراک های SaaS هم مفید است. در یک سیستم پایه اشتراکی, کاربران شما می توانند یک طرح رو بخرند, آپگرید کنند, دانگرید کنند, متوقف کنند یا یک کد تخفیف اضافه کنند. یک log رویداد خوب میتونه برای رفع مشکلات احتمالی بسیار مفید باشه.

پس با event resourcing شما میتونید :

  • حالات رو بصورت کامل بازسازی کنید.
  • حالات رو در یک زمان خاص دوباره اجرا کنید.
  • حالتی از یک لحظه خاص رو برای یک کوئری موقت بازسازی کنید.

رویداد (Event) چیست ؟

یک رویداد چیزیه که در گذشته اتفاق افتاده. یک رویداد تکه ای از حالتی در زمان خاص نیست; رویداد یک عمله با تمام اطلاعاتی که برای دوباره اجرا کردنش نیاز داره.

رویدادها باید یک آبجکت ساده باشند که عمل انجام شده رو شرح میده. اونها باید تغییرناپذیر بوده و در حالتappend-only (طوری که فقط بشه اضافه کرد) ذخیره بشه. این حالت تغییرناپذیر و append-only باعث میشه مناسب گزارش گیری و حسابرسی هم باشه.

این باعث میشه حالت undo و redo برای رویدادها ممکن بشه و از هرزمان خاصی که بخواهیم اجرای مجدد بگیریم.

مراقب سیستم های خارجی باشید

سیستم های خارجی که اپلیکیشن شما باهاشون ارتباط داره معمولا برای event sourcing آمادگی ندارند, پس وقتی دارید رویدادها رو اجرای مجدد می کنید مراقب این قضیه باشید. من مطئنم شما نمی خواهید موجودی کاربرانتون دوبرابر شارژ بشه یا دوباره ایمیل خوش آمدگویی به همه فرستاده بشه!

مثال هایی از Event Sourcing

مثال ساده برای اعمال این ویژگی در حساب های کاربری :

// current account states (how it looks in our DB now)
const accounts = {  
  account1: { balance: 100 },
  account2: { balance: 50 }
}

// past events (should be persisted somewhere, for example in a DB)
const events = [  
  { type: 'open', id: 'account1', balance: 150, time: 0 },
  { type: 'open', id: 'account2', balance: 0, time: 1 },
  { type: 'transfer', fromId: 'account1', toId: 'account2': amount: 50, time: 2 }
]

بیایید با استفاده از event log آخرین حالت رو بازسازی کنیم :

// complete rebuild
const accounts = events.reduce((accounts, event) => {  
  if (event.type === 'open') {
    accounts[event.id].balance = event.balance
  } else if (event.type === 'transfer') {
    accounts[event.fromId].balance -= event.amount
    accounts[event.toId].balance += event.amount
  }
  return accounts
}, {})

آخرین رویداد را undo کنیم :

// undo last event
const accounts = events.splice(-1).reduce((accounts, event) => {  
  if (event.type === 'open') {
    delete accounts[event.id]
  } else if (event.type === 'transfer') {
    accounts[event.fromId].balance += event.amount
    accounts[event.toId].balance -= event.amount
  }
  return accounts
}, {})

کوئری حساب ها در زمان خاص :

// query specific time
function getAccountsAtTime (time) {  
  return events.reduce((accounts, event) => {
    if (time > event.time {
      return accounts
    }

    if (event.type === 'open') {
      accounts[event.id].balance = event.balance
    } else if (event.type === 'transfer') {
      accounts[event.fromId].balance -= event.amount
      accounts[event.toId].balance += event.amount
    }
    return accounts
  }, {})
}

const accounts = getAccountsAtTime(1)  

منبع 

چه امتیازی برای این مقاله میدهید؟

خیلی بد
بد
متوسط
خوب
عالی
در انتظار ثبت رای

دیدگاه و پرسش

برای ارسال دیدگاه لازم است وارد شده یا ثبت‌نام کنید ورود یا ثبت‌نام

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

در حال دریافت نظرات از سرور، لطفا منتظر بمانید

مقالات برگزیده

مقالات برگزیده را از این قسمت میتوانید ببینید

مشاهده همه مقالات