8.3.0 • Published 1 year ago

@serialized/serialized-client v8.3.0

Weekly downloads
56
License
MIT
Repository
github
Last release
1 year ago

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.

8.1.0

1 year ago

8.1.1

1 year ago

8.2.0

1 year ago

8.3.0

1 year ago

7.3.0

1 year ago

7.4.0

1 year ago

5.1.0

1 year ago

6.1.0

1 year ago

7.1.0

1 year ago

6.0.0

1 year ago

6.2.0

1 year ago

7.0.0

1 year ago

7.2.0

1 year ago

4.12.0

1 year ago

5.0.0

1 year ago

4.11.0

1 year ago

4.9.0

2 years ago

4.10.0

2 years ago

4.8.1

2 years ago

4.8.0

2 years ago

4.7.0

2 years ago

4.6.0

2 years ago

4.4.0

2 years ago

4.5.0

2 years ago

4.2.0

2 years ago

4.3.0

2 years ago

4.1.0

2 years ago

3.17.0

3 years ago

4.0.0

3 years ago

3.16.0

3 years ago

3.15.2

3 years ago

3.15.1

3 years ago

3.15.0

3 years ago

3.14.0

3 years ago

3.13.0

3 years ago

3.12.5

3 years ago

3.12.4

3 years ago

3.12.3

3 years ago

3.12.1

3 years ago

3.12.2

3 years ago

3.12.0

3 years ago

3.11.0

3 years ago

3.10.1

3 years ago

3.10.0

3 years ago

3.9.0

3 years ago

3.8.0

3 years ago

3.6.0

3 years ago

3.4.0

3 years ago

3.5.0

3 years ago

3.3.0

3 years ago

3.2.0

3 years ago

3.1.0

3 years ago

3.0.0

3 years ago

2.2.0

4 years ago

2.1.0

4 years ago

2.0.0

4 years ago

1.0.4

4 years ago

1.0.3

4 years ago

1.0.0

4 years ago

0.9.4

4 years ago

0.9.3

4 years ago

0.9.2

4 years ago

0.9.1

4 years ago

0.9.0

4 years ago

0.7.0

4 years ago

0.6.0

4 years ago

0.5.0

4 years ago

0.4.0

4 years ago

0.2.26

4 years ago

0.3.0

4 years ago

0.2.25

4 years ago

0.2.24

4 years ago

0.2.23

4 years ago

0.2.22

4 years ago

0.2.21

4 years ago

0.2.20

4 years ago

0.2.19

4 years ago

0.2.18

4 years ago

0.2.16

4 years ago

0.2.15

4 years ago

0.2.17

4 years ago

0.2.14

4 years ago

0.2.13

4 years ago

0.2.12

4 years ago

0.2.11

4 years ago

0.2.10

4 years ago

0.2.9

4 years ago

0.2.8

4 years ago

0.2.7

4 years ago

0.2.6

4 years ago

0.2.5

4 years ago

0.2.3

4 years ago

0.2.4

4 years ago

0.2.2

4 years ago

0.2.1

4 years ago

0.2.0

4 years ago

0.1.1

4 years ago

0.1.0

4 years ago