npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@storevent/storevent

v0.0.16

Published

Storevent is a framework that simplify event sourcing.

Downloads

956

Readme

Storevent

test

Storevent is a framework that simplify event sourcing. It makes it easy to build an entity reducer to aggregate your events into a state.

This package @storevent/stovevent provides interfaces that you can use to build custom implementation for your event store.

You can also decide to use a packages that provides an implementation for Postgres, MongoDB, etc.... See Available Packages List

Documentation

📕 Types Documentation Here 📕

Examples

Storevent provides examples that takes the case of an entity Account that represents a simple bank account. It can be created, credited with some money or debited. Each of these action produce an event: AccountCreated, AccountCredited, AccountDebited.

See Account Example Here

Example shows:

  • How to create your:

    • Entity Events
    • Entity State
    • Entity Reducer that will calculate your entity state from your events.
  • How to use the In Memory implementation:

    • event store
    • snaphshot store
    • hybrid store
  • How to use the Postgres implementation:

    • event store
    • snaphshot store
    • hybrid store

Event

To create your events you just need to extends the Storevent interface.

import { Storevent } from "@storevent/storevent";

type AccountEvent = AccountCreated | AccountCredited | AccountDebited;

interface AccountCreated extends Storevent {
  name: "AccountCreated";
  payload: {
    accountId: string;
    status: "OPEN";
    balance: number;
    currency: string;
  };
}

interface AccountCredited extends Storevent { ... }
interface AccountDebited extends Storevent { ... }

Entity State

Your entity state just need to extends JsonSerializable type.

import { JsonSerializable } from "@storevent/storevent";

interface AccountState extends JsonSerializable {
  accountId: string;
  status: "VOID" | "OPEN";
  balance: number;
  currency: string;
}

Entity Reducer

The entity reducer is the component that calculates the state of your entity. For this it takes an initial state and a list of event to apply on top of this state.

To create an entity reducer you just need to extends the EntityReducer class. Then mount your event reducers using the mountEventReducer method.

Reducer Declaration

import { AccountEvent, AccountState } from "../AccountEntity";

class AccountReducer extends EntityReducer<AccountState, AccountEvent> {
  constructor() {
    super("Account");

    this.mountEventReducer("AccountCreated", applyAccountCreatedEvent);
    this.mountEventReducer("AccountCredited", applyAccountCreditedEvent);
    this.mountEventReducer("AccountDebited", applyAccountDebitedEvent);
  }
}

function applyAccountCreditedEvent(params: {
  state: AccountState;
  event: AccountCredited;
}): AccountState {
  const { state, event } = params;

  return {
    accountId: state.accountId,
    balance: state.balance + event.payload.amount,
    currency: state.currency,
    status: state.status,
  };
}

function applyAccountDebitedEvent() { ... }

function applyAccountCreatedEvent() { ... }

Reducer usage

const initialState = { ... }

const events: AccountEvent[] = [ accountCreated, accountCredited, accountDebited, ...]

const newState = new AccountReducer().reduceEvents({
  state: initialState,
  stateVersion: 0,
  events,
});

Event Store interface

The event store interface provides an interface to append new events in your event store and a method to retrieve your events.

const accountEventStore = new AccountInMemoryEventStore()

// Appending events
await accountEventStore.append({
  entityId: accountId,
  events: [accountCreatedEvent],
});

// Retrieve all events
const events = await accountEventStore.getEventsFromSequenceNumber({
  entityId: accountId,
});


// Retrieve event from a given sequence
const events = await accountEventStore.getEventsFromSequenceNumber({
  entityId: accountId,
  sequenceNumber: 45, // optional default to 0
});

Snapshot Store interface

The SnapshotStore interface provides methods to help you save your entity state for a given version.

// Saving a snapshot
await accountSnapshotStore.saveSnapshot({
  entityId: '123',
  snapshot: {status: 'My entity state'},
  version: 234, // this indicate that this state is the one we obtain after applying event wih sequence 234
});

// Retrieving the last snapshot for an entity
const snapshot = await accountSnapshotStore.getLastSnapshot(accountId);

// REtrieving a specific snapshot version if it exists.
const snapshotVersion = await accountSnapshotStore.getSnapshot({
  entityId: accountId,
  version: 234,
});

You can check this for more details Account Example Here

Hybrid Store interface

The HybridStore interface is here to allow saving your entity events and also persist a snapshot in a transactionnal way. It provide the same methods as EventStore and SnapshotStore. The only difference is the append method signature that can take a snaphost and an array of events.

const snapshotToSave = {
  state: { content: 'My entity state' },
  version: 349
}

await myHybridStore.append({
  entityId,
  events: [eventA, eventB],
  snapshot: snapshotToSave,
});

You can check this for more details Account Example Here

Implement a custom store

You can implement your own event, snapshot and hybrid store. For this just implement @storevent/storevent interfaces. You are of course free to enrich those interfaces with some specific methods related to your project.

See examples section to see how in memory, postgres and mongodb implementation are made.

Available implementations

Storevent Error

Storevent implementations will always try to throw a StoreventError.

Properties

A StoreventError has the following properties:

  • name: equals to StoreventError.

  • code: Unique storevent error code.

  • details: Generic type that contains the error context. You can discriminate the type with the error.code.

  • cause: Original error object if the error is wrapped. Mostly used when the error comes from the underlying layer (postgres, mongo, etc...)

Specific errors

  • ConcurrencyError: Use this error in your implementation to prevent events from being appended concurrently for the same entity.
  • WrongSequenceError: Use this error in your implementation when you detect inconsistency in your event sequence.
  • UnknownReducerError: This error is thrown when an EntityReducer cannot find a reducer for a given event name.