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

@halcyon-agile/node-event-sourcing

v1.1.6

Published

> Opinionated OOP based node.js event sourcing library inspired by laravel-event-sourcing

Downloads

176

Readme

NODE EVENT SOURCING

Opinionated OOP based node.js event sourcing library inspired by laravel-event-sourcing

Features

  • [x] Reliable event publishing
  • [x] Built for microservices
  • [x] CQRS

Current Limitations

  • Currently supports only DynamoDB as Event Store
  • Currently supports only Kafka as Message Broker

Requirements

  • Kafka
  • DynamoDB

Concepts

AggregateRoot

Class that handles the business logic and stream of events of a specific item such as a single Product and Order.

  • Events are created via AggregateRoot.createEvent method
  • Events created are automatically published into Kafka topic via DynamoDB Stream (Production) or Polling (Dev)

Listener

Class that handles event after being fired

Projector

Class that handles the update to read table after event is fired

Getting Started

Configuration

process.env.KAFKA_BROKERS = "localhost:9092,localhost:9093"; // default: localhost:9092
process.env.KAFKA_GROUP_ID = "group"; // default: example-group

Creating Event

interface Item {
  productId: string | null;
  quantity: number;
}

export default class CartItemAdded {
  event = "CartItemAdded";

  payload: Item = {
    productId: null,
    quantity: 0,
  };

  constructor(payload: Item) {
    this.payload = payload;
  }
}

Creating AggregateRoot

import { AggregateRoot } from "@halcyon-agile/node-event-sourcing";
import CartItemAdded from "./Events/CartItemAdded";

interface Item {
  productId: string;
  quantity: number;
}

export default class CartAggregateRoot extends AggregateRoot {
  public items: Item[] = [];
  public snapshotIn = 0;

  constructor() {
    super();
  }

  public async addItemToCart(item: Item): Promise<void> {
    await this.createEvent(new CartItemAdded(item));
  }

  public async applyCartItemAdded(item: Item): Promise<void> {
    const existingItem = this.items.find((i) => i.productId === item.productId);

    if (existingItem) {
      existingItem.quantity = existingItem.quantity + item.quantity;
    } else {
      this.items.push(item);
    }
  }

  public async applySnapshot(currentState: { items: Item[] }): Promise<void> {
    this.items = currentState.items;
  }
}

Creating Event Listener

import { Listener } from "@halcyon-agile/node-event-sourcing";

export default CartItemAddedListener implements Listener {
  public async handle() {
    // ...
  }
}

Creating Projections

import { Projector } from "@halcyon-agile/node-event-sourcing";

export default class HotProductsProjector implements Projector {
  public async onCartItemAdded() {
    // ...
  }
}

Running Event Listener

import { Runner } from "@halcyon-agile/node-event-sourcing";
import * as path from "path";

Runner.registerListeners([path.resolve("./Listeners/CartListener")]);
Runner.registerProjectors([path.resolve("./Projectors/HotProductsProjector")]);

Runner.run()
  .then(() => {
    console.log("Event Sourcing is running...");
  })
  .catch((error) => {
    console.log(error.message);
  });

Running the Event Publisher

Development

poll-publisher.js

const Publisher = require("@halcyon-agile/node-event-sourcing/Publisher");

Publisher.run();
node poll-publisher.js

Production

Upload the @halcyon-agile/node-event-sourcing/lambda-event-publisher.zip in AWS Lambda and use that lambda function as a DynamoDB stream source. Make sure that the Lambda connects to Kafka server.

Handling failures

Dead letter queue
Circuit breaker
Distributed circuit breaker

Handling transient failures

Immediate retry
Exponential Backoff

What does the business say about the failure?

If you can make it fail fast, do it instead of using exponential backoff or dead letter queues.

Replaying Events

replay-events.js

const Replay = require("@halcyon-agile/node-event-sourcing/ReplayEvents");

// Replay all events and use all existing projectors
Replay.run();

// Replay specific projectors and event
Replay.run(
  [path.resolve("./Projectors/HotProductsProjector")],
  ["CartItemAdded"]
);
node replay-events.js
node