Skip to content

Instantly share code, notes, and snippets.

@mgenov
Last active May 24, 2018 14:41
Show Gist options
  • Save mgenov/98b72f3a6b08528a6991eaf36acdf553 to your computer and use it in GitHub Desktop.
Save mgenov/98b72f3a6b08528a6991eaf36acdf553 to your computer and use it in GitHub Desktop.
const createEventStore = () => {
let data = {}
let subscribers = []
let store = {
saveEvents: (aggregateId, events) => {
if (!data[aggregateId]) {
data[aggregateId] = []
}
events.forEach(event => {
data[aggregateId].push(event)
})
subscribers.forEach(sub => {
events.forEach(event => {
sub(event)
})
})
},
getEvents: async aggregateId => {
//TODO: load data from firestore
return Promise.resolve(data[aggregateId])
},
subscribe: subscriber => {
subscribers.push(subscriber)
}
}
return store
}
class AggregateRootBase {
constructor() {
this.uncommittedEvents = []
}
applyChange = (event) => {
this.uncommittedEvents.push(event)
}
getUncommittedEvents = () => {
return this.uncommittedEvents
}
markEventsAsCommitted = () => {
this.uncommittedEvents = []
}
loadFromEvents = (events) => {
const names = Object.getOwnPropertyNames(this)
let applyFunc =names.filter(name => name === 'apply')
if (applyFunc.size === 0) {
return
}
let applyCall = this[applyFunc[0]]
events.forEach(event => {
applyCall(event)
})
}
}
class AggregateRepository {
constructor(eventStore) {
this.eventStore = eventStore
}
getById = async (id, aggregateType) => {
let t = new aggregateType()
let events = await this.eventStore.getEvents(id)
t.loadFromEvents(events)
return t
}
save = (id, aggregate) => {
let events = aggregate.getUncommittedEvents()
this.eventStore.saveEvents(id, events)
aggregate.markEventsAsCommitted()
}
}
class Customer extends AggregateRootBase {
constructor(id, name, age) {
super()
this.name = ''
this.age = 0
this.applyChange({type: 'CustomerCreatedEvent', id: id, payload: {name: name, age: age}})
}
changeName = (newName) => {
this.applyChange({type: 'CustomerNameUpdatedEvent', id: this.id, payload: {name: newName}})
}
apply = (event) => {
if (event.type === 'CustomerCreatedEvent') {
this.name = event.payload.name
this.age = event.payload.age
}
if (event.type === 'CustomerNameUpdatedEvent') {
this.name = event.payload.name
}
}
}
let eventStore = createEventStore()
let aggregateRepository = new AggregateRepository(eventStore)
const putAndGet = async () => {
let savingCustomer = new Customer(1, 'Peter', 30)
aggregateRepository.save(1, savingCustomer)
savingCustomer.changeName('John')
aggregateRepository.save(1, savingCustomer)
let c2 = await aggregateRepository.getById(1, Customer)
console.log(c2.name + ', ' + c2.age)
}
putAndGet()
import configureStore from 'redux-mock-store' //ES6 modules
const createEventStore = () => {
let data = {}
let subscribers = []
let store = {
saveEvents: (aggregateId, events) => {
data[aggregateId] = events
subscribers.forEach(sub => {
events.forEach(event => {
sub(event)
})
})
},
getEvents: async aggregateId => {
//TODO: load data from firestore
return Promise.resolve(data[aggregateId])
},
subscribe: subscriber => {
subscribers.push(subscriber)
}
}
return store
}
const createOfflineMiddleware = (eventStore, handlers) => {
return store => next => action => {
if (handlers[action.type]) {
let event = handlers[action.type](action)
eventStore.saveEvents(action.aggregateId, [event])
}
let result = next(action)
return result
}
}
const eventStore = createEventStore()
const offlineMiddleware = createOfflineMiddleware(eventStore, {
CreateOrder: action => {
return {
type: 'OrderPlacedEvent',
payload: { customer: action.payload.customer }
}
}
})
const mockStore = configureStore([offlineMiddleware])
describe('Middleware', () => {
it('should handle received event', async () => {
const initialState = {}
const store = mockStore(initialState)
store.dispatch({
type: 'CreateOrder',
aggregateId: 123,
payload: { customer: 'John', lines: [] }
})
let events = await eventStore.getEvents(123)
expect(events).toEqual([
{ type: 'OrderPlacedEvent', payload: { customer: 'John' } }
])
})
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment