Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Event Sourcing Pattern in JS
class AggregateRoot {
apply(event) {
this.handle(event)
return this
}
handle(event) {
var eventName = event.constructor.name
var eventMethod = `apply${eventName}`
if (! this[eventMethod]) {
throw new TypeError(`${eventMethod} is not a function`)
}
this[eventMethod](event)
}
}
class OpenedEvent {
constructor(id: number, name: string) {
this.id = id
this.name = name
}
}
class BankAccount extends AggregateRoot {
static open(id: number, name: string) {
var bankAccount = new BankAccount
bankAccount.apply(new OpenedEvent(id, name))
return bankAccount
}
applyOpenedEvent(event) {
this.id = event.id
this.name = event.name
this.closed = false
this.balance = 0
}
}
var bankAccount = BankAccount.open(123456, 'Koala')
console.log(bankAccount.id, bankAccount.name) // 123456, 'Koala'
class AggregateRoot {
apply(event) {
this.handle(event)
return this
}
handle(event) {
var eventName = event.constructor.name
var eventMethod = `apply${eventName}`
if (! this[eventMethod]) {
throw new TypeError(`${eventMethod} is not a function`)
}
this[eventMethod](event)
}
}
class OpenedEvent {
constructor(id: number, name: string) {
this.id = id
this.name = name
}
}
class WithdrawnEvent {
constructor(id: number, amount: number) {
this.id = id
this.amount = amount
}
}
class DepositedEvent {
constructor(id: number, amount: number) {
this.id = id
this.amount = amount
}
}
class ClosedEvent {
constructor(id: number) {
this.id = id
}
}
class BankAccount extends AggregateRoot {
static open(id: number, name: string) {
var bankAccount = new BankAccount
bankAccount.apply(new OpenedEvent(id, name))
return bankAccount
}
withdraw(amount) {
if (this.closed) {
throw new Error(`${this.id} account is closed.`)
}
this.apply(new WithdrawnEvent(this.id, amount))
return this
}
deposit(amount) {
if (this.closed) {
throw new Error(`${this.id} account is closed.`)
}
this.apply(new DepositedEvent(this.id, amount))
return this
}
close() {
if (!this.closed) {
this.apply(new ClosedEvent(this.id))
}
return this
}
applyOpenedEvent(event) {
this.id = event.id
this.name = event.name
this.closed = false
this.balance = 0
}
applyWithdrawnEvent(event) {
this.balance -= event.amount
}
applyDepositedEvent(event) {
this.balance += event.amount
}
applyClosedEvent(event) {
this.closed = true
}
}
var bankAccount = BankAccount.open(123456, 'Koala')
.deposit(10000)
.withdraw(1000)
.deposit(3000)
.close()
console.log(bankAccount.name, bankAccount.balance, bankAccount.closed ? 'closed' : 'opened')
// Koala 12000 closed
var events = [
new OpenedEvent(123456, 'Koala'),
new DepositedEvent(123456, 10000),
new WithdrawnEvent(123456, 1000),
new DepositedEvent(123456, 3000),
new ClosedEvent(123456),
]
var bankAccount2 = new BankAccount
events.forEach(event => bankAccount2.apply(event))
console.log(bankAccount2.name, bankAccount2.balance, bankAccount2.closed ? 'closed' : 'opened')
// Koala 12000 closed
class AggregateRoot {
uncommittedEvents = []
apply(event) {
this.handle(event)
this.uncommittedEvents.push(event)
return this
}
handle(event) {
var eventName = event.constructor.name
var eventMethod = `apply${eventName}`
if (! this[eventMethod]) {
throw new TypeError(`${eventMethod} is not a function`)
}
this[eventMethod](event)
}
initializeState(events) {
events.forEach(event => this.handle(event))
}
getUncommittedEvents() {
var events = this.uncommittedEvents
this.uncommittedEvents = []
return events
}
}
class OpenedEvent {
constructor(id: number, name: string) {
this.id = id
this.name = name
}
}
class WithdrawnEvent {
constructor(id: number, amount: number) {
this.id = id
this.amount = amount
}
}
class DepositedEvent {
constructor(id: number, amount: number) {
this.id = id
this.amount = amount
}
}
class ClosedEvent {
constructor(id: number) {
this.id = id
}
}
class BankAccount extends AggregateRoot {
static open(id: number, name: string) {
var bankAccount = new BankAccount
bankAccount.apply(new OpenedEvent(id, name))
return bankAccount
}
withdraw(amount) {
if (this.closed) {
throw new Error(`${this.id} account is closed.`)
}
this.apply(new WithdrawnEvent(this.id, amount))
return this
}
deposit(amount) {
if (this.closed) {
throw new Error(`${this.id} account is closed.`)
}
this.apply(new DepositedEvent(this.id, amount))
return this
}
close() {
if (!this.closed) {
this.apply(new ClosedEvent(this.id))
}
return this
}
applyOpenedEvent(event) {
this.id = event.id
this.name = event.name
this.closed = false
this.balance = 0
}
applyWithdrawnEvent(event) {
this.balance -= event.amount
}
applyDepositedEvent(event) {
this.balance += event.amount
}
applyClosedEvent(event) {
this.closed = true
}
}
class EventSourcingRepository {
constructor(eventStore, aggregateType) {
this.eventStore = eventStore
this.aggregateType = aggregateType
}
load(id) {
var events = this.eventStore.load(id)
var aggregate = Object.create(this.aggregateType.prototype)
aggregate.initializeState(events)
return aggregate
}
save(aggregate) {
var uncommittedEvents = aggregate.getUncommittedEvents()
this.eventStore.append(uncommittedEvents)
}
}
class EventStoreData {
constructor(rootId, event, createdAt) {
this.rootId = rootId
this.event = event
this.createdAt = createdAt
}
}
class InMemoryForTestingEventStore {
constructor(events) {
this.data = events ? this.convertEvents(events) : []
}
load(rootId) {
return this.data
.filter(data => data.rootId === rootId)
.map(data => data.event)
}
append(events) {
var newData = this.convertEvents(events)
this.data = this.data.concat(newData)
}
convertEvents(events) {
return events.map(event => this.convertEventToData(event))
}
convertEventToData(event) {
var createdAt = new Date().getTime()
return new EventStoreData(event.id, event, createdAt)
}
}
var eventStore = new InMemoryForTestingEventStore()
var repository = new EventSourcingRepository(eventStore, BankAccount)
var bankAccount = BankAccount.open(654321, 'Edward')
.deposit(20000)
.withdraw(1000)
.withdraw(1000)
repository.save(bankAccount)
var loaded = repository.load(654321)
console.log(bankAccount.name, bankAccount.balance, bankAccount.closed ? 'closed' : 'opened')
// Edward 18000 opened
console.log(eventStore.data)
@haruair

This comment has been minimized.

Copy link
Owner Author

haruair commented Oct 18, 2017

detail of eventStore.data

@haruair

This comment has been minimized.

Copy link
Owner Author

haruair commented Oct 18, 2017

This code is an example of my post about event-sourcing pattern example in js (kor).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.