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

domain-events

v0.2.0

Published

A js library to create and asyncronously dispatch Domain Events

Downloads

245

Readme

Domain Events npm version Build Status Coverage Status

domain-events is a js library to create and asyncronously dispatch Domain Events.

Guide

Installation

$ npm install domain-events

Usage

const domainEvents = require('domain-events');

const EventBus = domainEvents.EventBus;
const DomainEvent = domainEvents.DomainEvent;

let eventBus = new EventBus();

let aEvent = new DomainEvent('user.created', { userId: '12' });
let anotherEvent = new DomainEvent('action.happened', { actionId: 'ACTION' });

// A event handler could be a simple function
let aFunctionHandler = function(aDispatchedEvent) {
  console.log(`[[ANY]] ${aDispatchedEvent.name()}`);
}

// A event handler could be a object with a "handle" method
let aObjectHandler = {
  handle: (aDispatchedEvent) => {
    console.log(`[[user.created]] user id: ${aDispatchedEvent.payload().userId}`);
  }
};

eventBus.register(aGlobalEventsHandler); // Handle every event
eventBus.register(aSingleEventHandler, 'user.created'); // Handle only 'user.created' events

eventBus.dispatch(aEvent);
eventBus.dispatch(anotherEvent);

console.log('Other syncrounous code...');

// --> "Other sincrounous code..."
// --> "[[ANY]] user.created"
// --> "[[user.created]] user id: 12"
// --> "[[ANY]] action.happened"

Documentation

DomainEvent

Create domain events with the DomainEvent class.

constructor(aEventName, /*optional*/ aEventPayload, /*optional*/ aEventCreationTimestamp)

let event = new DomainEvent('event.name');
let event = new DomainEvent('event.name', { a: 12 });
let event = new DomainEvent('event.name', { a: 12 }, 1502530224425);
aEventName

A String which will be the name of the event (use the same string to register handlers for that event). Usually, because a domain event represents something that happened in the past, is preferrable to name events with an action in the past tense. Because the event name is just a plain string any naming convention flavour is possible, according to your coding style.

Examples:

  • 'user.created'
  • 'purchaseCompleted'
  • 'ACTION_HAPPENED'
aEventPayload (optional)

Any serializable value which will be provided to the handlers as the payload of the event. Any value that can be serialized as a JSON string could be provided as the paylod and will have an expected behaviour.

// Valid values examples:
'A string'
12
{ a: 23 }
null
aEventCreationTimestamp (optional)

Newly created domain events are automatically assigned the creation timestamp by the constructor, so it is usually not necessary to provide a specific timestamp. Anyway it is possible to provide a different event creation timestamp to adjust the default behaviour to any particular needs.

.name()

Returns the event name (a String).

.payload()

Returns the (immutable) event payload, or null if the event has no payload.

.createdAt()

Returns the event creation timestamp.

.serialize()

Returns a serialized JSON string representing the event. This could be provided to the static DomainEvent.deserialize() method to restore the original event instance.

static .deserialize(aSerializedEvent)

Returns the original instance of the previously serialized event.

aSerializedEvent

A JSON string representing the event, previously created with the DomainEvent.serialize() method.

let event = new DomainEvent('event.name');
let serializedEvent = event.serialize(); // --> event JSON
let deserializedEvent = DomainEvent.deserialize(serializedEvent) // --> event

EventBus

A EventBus instance lets you register handlers for an event and dispatch events to the registered handlers. Create a new EventBus with its constructor:

let eventBus = new EventBus();

.register(aEventHandler, /*optional*/ aEventName)

Pass a event handler to the register method to register actions to be executed any time a Event is dispatched.

aEventHandler

The registered handler could be either:

  • a plain function accepting the dispatched event as the only argument
  • a object instance with a handle method, which accepts the dispatched event as the only argument
let handler = (aDispatchedEvent) => { ... };
let anotherHander = { handle: (aDispatchedEvent) => { ... } };

The handler could be registered to a specific event (providing the event name) or to any event (ommitting the aEventName parameter):

eventBus.register(aEventHandler); // Every dispatched event will be dispatched to the handler
eventBus.register(aEventHandler, 'event.name'); // Only events with name 'event.name' will be dispatched to the handler
aEventName (optional)

A string which is the name of the handled event. Omit the parameter to register the handler to any event (ex. for logging or persisting the events in a database).

.dispatch(aDomainEvent)

Pass a DomainEvent to the dispatch method to execute the registered handler asyncronously.

aDomainEvent

A DomainEvent instance.

static .getInstance()

Returns a Singleton EventBus instance. The first call actually creates a new EventBus instance while any further call will return the same object. Useful if you don't want to (or can't) inject the eventBus instance wherever it is needed.

// File1.js
let eventBus = EventBus.getInstance(); // --> new EventBus();
eventBus.register(aHandler);

// File2.js
let eventBus = EventBus.getInstance(); // --> the same event bus instance created in File1.js;
eventBus.dispatch(aEvent); // The handler registered in File1.js will be called

Events Immutability

Every handler will be invoked asyncronously and will be provided the original DomainEvent object, which is immutable. For example, mutating the payload in a Handler will not affect the original event and those modifications won't be provided to the next registered Handler.

let event = new DomainEvent('name', { a: 12 });

let aHandler = function(aDispatchedEvent) {
  let eventPayload = aDispatchedEvent.payload();
  console.log(eventPayload)    
  
  eventPayload.a = 23;
  console.log(eventPayload)    
};

let anotherHandler = function(aDispatchedEvent) {
  console.log(aDispatchedEvent.payload());
}

eventBus.register(aHandler, 'name');
eventBus.register(anotherHandler, 'name');

eventBus.dispatch(event);

// First handler
// --> { a: 12 }
// --> { a: 23 }

// Second handler
// --> { a: 12 }

Extending DomainEvent

The DomainEvent Class could be extended to easily create new events with the same event name, or the same fixed payload. Here is an example of a new DomainEvent Class with a fixed event name:

// === UserCreatedEvent.js
const DomainEvent = require('domain-events').DomainEvent;
const EVENT_NAME = 'user.created';

class UserCreatedEvent extends DomainEvent {

  static eventName() {
    return EVENT_NAME;
  }
  
  constructor(aEventPayload) {
    super(EVENT_NAME, aEventPayload);
  }
}

// === SomewhereElse.js
const UserCreatedEvent = require('./UserCreatedEvent');

// ... some code ...

let userCreatedEvent = new UserCreatedEvent({ id: 127 }); // No need to pass event name to the DomainEvent constructor
eventBus.publish(userCreatedEvent);

// === SomewhereElseAgain.js
const UserCreatedEvent = require('./UserCreatedEvent');

// ... some code ...

eventBus.register(
  () => { ... },
  UserCreatedEvent.eventName()
);