2.0.0 • Published 7 years ago

eventsourced-object v2.0.0

Weekly downloads
564
License
ISC
Repository
github
Last release
7 years ago

eventsourced-object

A minimal eventsourcing helper for objects and classes.
In case you don't know eventsourcing, here's some reading material

var Aggregate = require('eventsourced-object')

Api

Aggregate.setup(obj, reducer, events)

var user = {version: 0}
var initialEvents = [{name: 'CreateUser', fullName: 'Marc Bachmann'}]
Aggregate.setup(user, reducer, initialEvents)

function reducer (state, evt) {
    state.version += 1
    if (evt.name == 'CreateUser') state.name = evt.fullName
}

Aggregate.event(obj, reducer, event)

var user = Aggregate.setup({version: 0})
var event = {name: 'CreateUser', fullName: 'Marc Bachmann'}
Aggregate.event(user, reducer, event)
// executes the reducer with the event and queues the event so you can save it
function reducer (state, evt) {
    state.version += 1
    if (evt.name == 'CreateUser') state.name = evt.fullName
}

Aggregate.drain(obj)

var obj = {}
Aggregate.setup(obj)
Aggregate.event(obj, reducer, {foo: 'bar'})
Aggregate.event(obj, reducer, {foo: 'test'})
Aggregate.drain(obj) // returns [{foo: 'bar'}, {foo: 'test'}]

Aggregate.isDirty(obj)

var obj = {}
Aggregate.setup(obj)
Aggregate.event(obj, reducer, {foo: 'bar'})
Aggregate.isDirty(obj) // returns true

Example using a class

var user = User.create({email: 'foo@example.com', fullName: 'Foo Example'})
user.rename('Example')
// user == {
//    id: 'iuxswpqw',
//    email: 'foo@example.com',
//    fullName: 'Example',
//    createdAt: Mon Oct 31 2016 09:26:02 GMT+0100 (CET),
//    updatedAt: Mon Oct 31 2016 09:26:03 GMT+0100 (CET)
//}


function User (events) {
  this.version = 0
  Aggregate.setup(this, reducer, events)
}

User.create = function (params) {
  var user = new User()
  return Aggregate.event(user, reducer, {
    aggregateId: params.id || Date.now().toString(36),
    name: 'UserCreated',
    time: new Date(),
    data: {
      email: params.email,
      fullName: params.fullName
    }
  })
}

User.prototype.rename = function (fullName) {
  return Aggregate.event(this, reducer, {
    aggregateId: this.id,
    name: 'UserRenamed',
    time: new Date(),
    data: {
      fullName: fullName
    }
  })
}

User.prototype.save = function () {
  var events = Aggregate.drain(this)
  console.log(events) // Save events to some storage/repository
}

User.prototype.isDirty = function () {
  return Aggregate.isDirty(this)
}

function reducer (state, evt) {
  state.version += 1
  state.updatedAt = evt.time
  if (evt.name === 'UserCreated') {
    state.id = evt.aggregateId
    state.createdAt = evt.time
    state.fullName = evt.data.fullName
    state.email = evt.data.email
  } else if (evt.name === 'UserRenamed') {
    state.fullName = evt.data.fullName
  }
}

Benchmarks

Here are some benchmarks with 10'000'000 iterations per function

NANOBENCH version 1

# raw object creation (for comparison)
  end ~97 ms (0 s + 97465524 ns)
# .setup(obj)
  end ~228 ms (0 s + 227954858 ns)
# .event(obj, reducer, event)
  end ~473 ms (0 s + 473181583 ns)
# .isDirty(obj)
  end ~86 ms (0 s + 86072337 ns)

# total ~885 ms (0 s + 884674302 ns)

# ok

[Finished in 1.0s]

Event Sourcing

Videos

Reading material