Skip to content


Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?

Latest commit


Git stats


Failed to load latest commit information.
Latest commit message
Commit time

CI Deps DevDeps

Simple EventStore

A simple single-aggregate eventstore without any performance optimizations or locking mechanisms but with FSA-compliant actions and a simple projection DSL


I needed a simple persistent storage mechanism for telegram bots with low traffic, but with a fast development pace. An eventstore captures events instead of state and lets me deduce state at runtime by folding over the events it has persisted before, which in turn enables me to build features on top of things that happened before that where recorded.

Reading from the eventstore can happen asynchronously while writing only happens synchronously. Writing also takes into consideration an expected version of the store before writing (a very simple transaction mechanism).


No release yet, but npm test runs the tests and npm run build runs babel to produce ES5-code without flowtype annotations.


The eventStore can either be created by supplying a filename to write to (which is a shorthand for using the JsonFileStorageBackend) or by supplying a custom StorageBackend (such as InMemoryStorageBackend, which is used for testing).

import { EventStore, InMemoryStorageBackend } from 'simple-eventstore';

const persistentEventStore = new EventStore('my-storage.json');
const inMemoryEventStorage = new EventStorage(InMemoryStorageBackend());

After that, it's mainly about defining Events and Projections.

Defining events

Event Factories can be created using the event export,
which is a function (type: string) => (payload: ?Object, meta: ?Object) => Event

import { event } from 'simple-eventstore';

// Declaring events
const UserJoined = event('USER_JOINED');

// Creating an event
eventStore.storeEvent(UserJoined({name: 'Raimo', twitter: '@rradczewski'}));

Projecting State

State is deduced at runtime by replaying all events in the store and folding them over a projection. A projection is a function (events: Event[]) => S. Using the utility functions provided as { projection, on }, it is very easy to write a simple projection for a specific use case:

import { projection, on } from 'simple-eventstore';
import { without } from 'ramda';

const ActiveUsers = projection(
    on('USER_JOINED', (users, event) => users.concat([])),
    on('USER_PARTED', (users, event) => without([], users))

Later, in your application code, you can request a projection by calling EventStore#project and supplying the projection you defined earlier:

    .then(activeUsers => {
        console.log(`All active users: ${activeUsers.join(', ')}`);

In order to project only events where the payload fulfills a precidicate, on is overloaded as (type: String, foldOrPredicate: Fold | Predicate, fold: ?Fold) (altough flow does not like that, see this issue for more info). In the following example, a projection can be done for an individual user

import { projection, on } from 'simple-eventstore';
import { propEq } from 'ramda';

const IsUserActive = username => projection(
    on('USER_JOINED', propEq('name', username), () => true),
    on('USER_PARTED', propEq('name', username), () => false)

eventStore.storeEvent(UserJoined({name: 'Raimo', twitter: 'rradczewski'}));

  .then(isActive => {
     if(isActive) {
       console.log('Raimo is active');
     } else {
       console.log('Raimo is not active');

// Will print: Raimo is active


No description, website, or topics provided.






No releases published


No packages published