@event-nest/core 0.0.3 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Event Nest
2
2
  A collection of [nest.js](https://nestjs.com/) libraries to help you build applications based on event sourcing architecture.
3
3
 
4
+ ![build status](https://github.com/NickTsitlakidis/event-nest/actions/workflows/checks.yml/badge.svg)
5
+ [![npm version](https://badge.fury.io/js/@event-nest%2Fcore.svg)](https://badge.fury.io/js/@event-nest%2Fcore)
6
+ [![Coverage Status](https://coveralls.io/repos/github/NickTsitlakidis/event-nest/badge.svg?branch=master)](https://coveralls.io/github/NickTsitlakidis/event-nest?branch=master)
7
+
4
8
  ## Description
5
9
  Event Nest is a collection of libraries based on nest.js that assists in implementing the core concepts of event sourcing:
6
10
  * Saving events in a persistent storage
@@ -56,18 +60,39 @@ Use any collection you want but be sure to create it before running the applicat
56
60
 
57
61
 
58
62
  ## Concepts
59
- ### Aggregate Root
60
- An [aggregate root](https://stackoverflow.com/questions/1958621/whats-an-aggregate-root) is a concept from Domain Driven Design. It is a domain object that is responsible for maintaining the consistency of the aggregate.
61
- To achieve this, the aggregate root should expose methods which encapsulate its behaviour. Any method that needs to update the state, should also append an event to the aggregate root's event stream.
62
-
63
63
  ### Event
64
64
  An event is a representation of something that has happened in the past. It is identified by a unique name, and it may contain additional data that will be persisted with the event.
65
65
 
66
- Each event can be used for three purposes :
66
+ Each event serves three purposes :
67
67
  * It will be persisted so that it can be used to reconstruct the state of an aggregate root
68
68
  * It will be passed to any internal subscribers that need to react to this event (e.g. updating the read model)
69
69
  * When it's time to recreate the aggregate root, the event will be processed by the correct method in the aggregate root
70
70
 
71
+ There is no specific requirement for the structure of an event, but it is recommended to keep it simple and immutable. The [class-transformer](https://github.com/typestack/class-transformer) library is utilized under the hood to save and read the events from the database. Therefore, your event classes should adhere to the rules of class-transformer to be properly serialized and deserialized.
72
+
73
+ To register a class as an event, use the `@RegisteredEvent` decorator. The decorator accepts a string parameter which is the unique name of the event.
74
+
75
+
76
+ ### Aggregate Root
77
+ An [aggregate root](https://stackoverflow.com/questions/1958621/whats-an-aggregate-root) is a fundamental concept in Domain-Driven Design (DDD).
78
+ It represents a cluster of domain objects that are treated as a single unit. The aggregate root is responsible for maintaining the consistency and enforcing business rules within the aggregate.
79
+ While explaining the concept of an aggregate root in depth is beyond the scope of this documentation, it's important to understand how such an object interacts with the event sourcing system.
80
+
81
+ In the context of event sourcing, the aggregate root plays a crucial role. Each aggregate root maintains its own set of events, forming an event stream.
82
+ These events capture the changes or actions that have occurred within the aggregate. The event stream serves as the historical record of what has happened to the aggregate over time.
83
+
84
+ Let's consider an example to illustrate the concept of an aggregate root. Suppose we have a user management system where we need to create new users and update existing users. In this case, the `User` entity serves as the aggregate root.
85
+
86
+ The `User` class encapsulates the user-specific behavior and maintains the internal state of a user. It provides methods for creating a new user, updating user details, and performing any other operations relevant to the user domain. These methods are called from Nest.js services or other parts of the application responsible for user-related operations.
87
+
88
+ Each instance of the `User` class has its own event stream, which records the events specific to that user. For example, when a new user is created, an event called `UserCreatedEvent` is appended to the event stream. Similarly, when a user's details are updated, an event called `UserUpdatedEvent` is appended.
89
+
90
+ When loading a user from the event store, the event stream is replayed, and each event is processed by the corresponding method in the `User` class. This allows the user object to be reconstructed and updated to its most recent state based on the events.
91
+
92
+ To ensure that all modifications to the user's state are properly recorded, any method that changes the state should also append the corresponding event to the event stream. By doing so, the event is persisted and can be used for reconstructing the state in the future.
93
+ If an event is not appended, the changes will not be saved in the database, and the consistency of the user object will be compromised.
94
+
95
+
71
96
  Enough with the theory, let's see an example that includes all of the above.
72
97
 
73
98
  #### Example
@@ -97,8 +122,8 @@ import { AggregateRoot, AggregateRootName, EventProcessor, StoredEvent } from "@
97
122
 
98
123
  @AggregateRootName("User")
99
124
  export class User extends AggregateRoot {
100
- private _name: string;
101
- private _email: string;
125
+ private name: string;
126
+ private email: string;
102
127
 
103
128
  private constructor(id: string) {
104
129
  super(id);
@@ -126,13 +151,13 @@ export class User extends AggregateRoot {
126
151
 
127
152
  @EventProcessor(UserCreatedEvent)
128
153
  private processUserCreatedEvent = (event: UserCreatedEvent) => {
129
- this._name = event.name;
130
- this._email = event.email;
154
+ this.name = event.name;
155
+ this.email = event.email;
131
156
  };
132
157
 
133
158
  @EventProcessor(UserUpdatedEvent)
134
159
  private processUserUpdatedEvent = (event: UserUpdatedEvent) => {
135
- this._name = event.newName;
160
+ this.name = event.newName;
136
161
  };
137
162
 
138
163
  }
@@ -203,12 +228,12 @@ import { AggregateRootAwareEvent, DomainEventSubscription, OnDomainEvent } from
203
228
  @Injectable()
204
229
  @DomainEventSubscription(UserCreatedEvent, UserUpdatedEvent)
205
230
  export class UserEventSubscription implements OnDomainEvent<UserCreatedEvent | UserUpdatedEvent> {
206
-
231
+
207
232
  onDomainEvent(event: AggregateRootAwareEvent<UserCreatedEvent | UserUpdatedEvent>): Promise<unknown> {
208
233
  //Here you can create/update your read model based on the event and your custom logic.
209
234
  return Promise.resolve(undefined);
210
235
  }
211
-
236
+
212
237
  }
213
238
  ```
214
239
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@event-nest/core",
3
- "version": "0.0.3",
3
+ "version": "1.0.1",
4
4
  "license": "MIT",
5
5
  "author": "Nick Tsitlakidis",
6
6
  "description": "Event sourcing module for NestJS. It provides a set of decorators and interfaces to help you build your event sourcing application.",
@@ -10,13 +10,20 @@
10
10
  "cqrs",
11
11
  "ddd"
12
12
  ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/NickTsitlakidis/event-nest.git"
16
+ },
17
+ "engines": {
18
+ "node": ">= 16"
19
+ },
13
20
  "type": "commonjs",
14
21
  "peerDependencies": {
15
22
  "@nestjs/common": "^9.0.0",
16
23
  "@nestjs/core": "^9.0.0",
17
24
  "reflect-metadata": "0.1.13",
18
25
  "rxjs": "^7.2.0",
19
- "tslib": "2.5.3"
26
+ "tslib": "1.14.1"
20
27
  },
21
28
  "dependencies": {
22
29
  "class-transformer": "^0.5.1"
@@ -2,10 +2,11 @@ import { Module } from "@nestjs/core/injector/module";
2
2
  import { AggregateRootAwareEvent } from "./aggregate-root-aware-event";
3
3
  import { OnModuleDestroy } from "@nestjs/common";
4
4
  export declare class DomainEventEmitter implements OnModuleDestroy {
5
- private _runParallelSubscriptions;
6
- private _handlers;
7
- private _logger;
5
+ private readonly _runParallelSubscriptions;
6
+ private readonly _handlers;
7
+ private readonly _logger;
8
8
  constructor(_runParallelSubscriptions?: boolean);
9
+ get runsParallelSubscriptions(): boolean;
9
10
  onModuleDestroy(): void;
10
11
  bindSubscriptions(injectorModules: Map<string, Module>): void;
11
12
  emit(withAggregate: AggregateRootAwareEvent<object>): Promise<unknown>;
@@ -11,6 +11,9 @@ class DomainEventEmitter {
11
11
  this._handlers = new Map();
12
12
  this._logger = new common_1.Logger(DomainEventEmitter.name);
13
13
  }
14
+ get runsParallelSubscriptions() {
15
+ return this._runParallelSubscriptions;
16
+ }
14
17
  onModuleDestroy() {
15
18
  this._handlers.clear();
16
19
  }
@@ -1 +1 @@
1
- {"version":3,"file":"domain-event-emitter.js","sourceRoot":"","sources":["../../../../../libs/core/src/lib/domain-event-emitter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"domain-event-emitter.js","sourceRoot":"","sources":["../../../../../libs/core/src/lib/domain-event-emitter.ts"],"names":[],"mappings":"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"}