New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@serialized/serialized-client

Package Overview
Dependencies
Maintainers
2
Versions
97
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@serialized/serialized-client

Client library for Serialized APIs.

  • 8.3.0
  • latest
  • Source
  • npm
  • Socket score

Version published
Maintainers
2
Created
Source

Serialized Typescript client

The official Typescript client for Serialized.

✨ Features

  • Built with Typescript
  • Client for Event Sourcing & CQRS APIs provided by Serialized
  • Promise-based API that supports async/await
  • Provides an easy way to implement DDD Aggregates using Event Sourcing.

💡 Getting Started

Register for a free account at https://serialized.io to get your access keys to the API (if you haven't already).

Install the Serialized TS/JS client via the npm package manager:

npm install @serialized/serialized-client

Import the library and initialize the client instance:

import {Serialized} from "@serialized/serialized-client"

const serialized = Serialized.create({
  accessKey: "<YOUR_ACCESS_KEY>",
  secretAccessKey: "<YOUR_SECRET_ACCESS_KEY>"
});

Create our domain

State

The state type holds the assembled state from the events during the load of the aggregate.

// The different statuses our game can be in
enum GameStatus {
  UNDEFINED = 'UNDEFINED',
  CREATED = 'CREATED',
  STARTED = 'STARTED',
  CANCELED = 'CANCELED',
  FINISHED = 'FINISHED'
}

type GameState = {
  readonly gameId?: string
  readonly status?: GameStatus
}

Events

Define your domain events as types

type GameCreated = DomainEvent<'GameCreated', { gameId: string, creationTime: number }>
type GameStarted = DomainEvent<'GameStarted', { gameId: string, startTime: number }>
type GameCanceled = DomainEvent<'GameCanceled', { gameId: string, cancelTime: number }>
type GameFinished = DomainEvent<'GameFinished', { gameId: string, endTime: number }>
type GameEvent = GameCreated | GameStarted | GameCanceled | GameFinished;

Next, we create the StateBuilder implementation, which can handle loading events one-by-one to create the current state. Each method should have apply as a prefix and the event type as the suffix and return the new state.

const stateBuilder: StateBuilder<GameState, GameEvent> = {
  initialState: () => {
    return {gameId: '', status: GameStatus.UNDEFINED}
  },
  applyGameCreated: (state, event) => {
    return {...state, gameId: event.data.gameId, status: GameStatus.CREATED}
  },
  applyGameStarted: (state, event) => {
    return {...state, status: GameStatus.STARTED}
  },
  applyGameCanceled: (state, event) => {
    return {...state, status: GameStatus.CANCELED}
  },
  applyGameFinished: (state, event) => {
    return {...state, status: GameStatus.FINISHED}
  }
}

Aggregate

The aggregate contains the domain logic and each method should return 0..n events that should be stored for a successful operation. The aggregate takes the state as a constructor argument and should be immutable.

Any unsuccessful operation should throw an error.

class Game {
  constructor(private readonly state: GameState) {
  }

  create(gameId: string, creationTime: number): GameCreated[] {
    const currentStatus = this.state.status;
    if (currentStatus == GameStatus.UNDEFINED) {
      return [{
        eventType: 'GameCreated',
        eventId: uuidv4(),
        data: {
          gameId,
          creationTime
        }
      }];
    } else if (currentStatus == GameStatus.CREATED) {
      return [];
    } else {
      throw new InvalidGameStatusException(GameStatus.UNDEFINED, currentStatus);
    }
  }

  start(startTime: number): GameStarted[] {
    const currentStatus = this.state.status;
    if (this.state.status == GameStatus.STARTED) {
      return [];
    } else if (this.state.status == GameStatus.CREATED) {
      return [{
        eventType: 'GameStarted',
        eventId: uuidv4(),
        data: {
          gameId: this.state.gameId,
          startTime
        }
      }];
    }
    throw new InvalidGameStatusException(GameStatus.CREATED, currentStatus);
  }

...

}

Test the client by creating a Game:

const gameClient = serialized.aggregateClient({aggregateType: 'game'}, stateBuilder, (state: GameState) => new Game(state));
await gameClient.create(gameId, (game) => (game.create(gameId, Date.now())));

To perform an update operation, which means loading all events, performing business logic and then appending more events

await gameClient.update({aggregateId: gameId}, (game: Game) => game.start(startTime))

📄 More resources

❓ Troubleshooting

Encountering an issue? Don't feel afraid to add an issue here on Github or to reach out via Serialized.

Keywords

FAQs

Package last updated on 01 Apr 2023

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc