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

lagan

v0.0.9

Published

Simple module for event sourcing in node.js

Downloads

13

Readme

Lagan

Event sourcing and CQRS module for Node.js.

Usage

// const database = your-database-layer;

const Lagan = require('lagan');
const lagan = new Lagan({
    logFile: './my-data-storage.log'  // Path to persistent data storage file
});


// Event class:

class UserSignedUp extends lagan.Event {

    validate() {
        if (typeof this.props.name !== 'string') throw new Error('Invalid name.');

        email = this.props.email.toLowerCase().trim();
        if (!this.props.email.match(/^[^@]+@[^@]$/)) throw new Error('Invalid email.');

        if (database.userExists(this.props.email))
            throw new Error('Subscriber already in database.');
    }

    project() {
        database.addToUserTable(this.props.name, this.props.email);
    }
}

lagan.registerEvent(UserSignedUp);


// Command function:

function signUpUser(name, email) {
    return new UserSignedUp({ name, email }).apply();
}


// Now, we can use the command:

signupUser('John Doe', '[email protected]')
    .then(() => {
        // Event successfully added to event stream, and projected to database.
    })
    .catch(err => {
        // Event was not created, or there was an error in validation/projection
        // after the event was created.
    });

Using Lagan's state object

Your .validate() and .project() functions can use any state handler you like.

If you want to just keep the application state in a Javascript object in memory, you can use Lagan's state object.

Just instantiate Lagan with an initialState (which should be the state object before the first event has been projected):

const lagan = new Lagan({
    initialState: { users: [] }
});

Your .validate() and .project() functions will get a state object in the arguments object. What you return from the .project() function will be the new state after projection:

class UserSignedUp extends lagan.Event {

    validate({ state }) {
        if (state.users.filter(user => user.email === this.props.email).length > 0)
            throw new Error('Subscriber already in database.');
    }

    project({ state }) {
        return { ...state, users: { ...state.users, { name: this.props.name, email: this.props.email } } };
    }

}

It is good practice to use immutable coding pattern using spread operators in the projector.

Validation

Validation is the process of checking that the event is properly formatted and valid before it is added to the event stream.

Synchronous or asynchronous

Validation can be synchronous or asynchronous. If it is asynchronous, the validate() function must return a Promise.

Pre-validation and post-validation

Your validate() function will run twice when adding a new event to the event stream.

Pre-validation

First, when you create a new event, validate() will run with whatever state Lagan has at the moment. This is called "pre-validation". If the pre-validation does not throw an exception or returns a rejected promise, the event will be added to the event stream.

Post-validation

Then, after the event has been stored to the stream, and it is time to for projection, validate() will run again. This is called "post-validation". If the post-validation does not throw an exception or returns a rejected promise, the event will be projected to the state.

Distinguish pre-validation from post-validation

The validate() function will be called with three aruments: state, position and context.

During pre-validation, state is just the latest state that Lagan currently knows of, and position is null, since the event has not been written to the event stream yet, so it does not have a position so far. If an argument was passed to the .apply() function of your event object (the Event class object), that argument is in the context argument of your validate() function.

During post-validation, state is the state when all event before this event has been projected, and position is the position number of this event in the event stream. The context is always null during post-validation.

A good way to distinguish pre-validation from post-validation is to check if position is null.

class UserSignedUp extends lagan.Event {

    validate({ position }) {
        if (position === null) {
            // Pre-validation
        } else {
            // Post-validation
        }
        
        ...

    }

    ....

Event ordering

Lagan guarantees that the event projection will be in the same order as you do .apply() on your event objects, as long as your validate function is synchronous during pre-validation.

Curiosities

This module got it's name from the river Lagan, which flows through Swedish hicksvilles like Vaggeryd, Ljungby and Alla Har En Ko I Värnamo.