@libs-for-dev/nestjs-ddd-library 1.0.0

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 ADDED
@@ -0,0 +1,154 @@
1
+ # NestJS DDD Library
2
+
3
+ A comprehensive library providing Domain-Driven Design (DDD) building blocks for NestJS applications. This library helps you implement DDD patterns such as Value Objects, Entities, and Aggregates in your NestJS projects with TypeScript.
4
+
5
+ ## Features
6
+
7
+ - **Entities**: Objects that have a distinct identity that runs through time and different states
8
+ - **Aggregates**: Cluster of domain objects that can be treated as a single unit
9
+ - **Result types**: Using oxide.ts for type-safe error handling
10
+ - **Value Objects**: Immutable objects that contain attributes but have no conceptual identity with examples:
11
+ - **UUID**: Built-in UUID v7 generation and validation
12
+ - **Email**: Email value object with validation
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ # Using npm
18
+ npm install @libs-for-dev/nestjs-ddd-library
19
+
20
+ # Using yarn
21
+ yarn add @libs-for-dev/nestjs-ddd-library
22
+
23
+ # Using pnpm
24
+ pnpm add @libs-for-dev/nestjs-ddd-library
25
+ ```
26
+
27
+ ### Peer Dependencies
28
+
29
+ This library requires the following peer dependencies:
30
+
31
+ ```bash
32
+ yarn add @nestjs/cqrs class-validator fast-equals oxide.ts ui7
33
+ ```
34
+
35
+ ## Usage Examples
36
+
37
+ ### Value Objects
38
+
39
+ #### UUID
40
+
41
+ ```typescript
42
+ import { Uuid } from '@libs-for-dev/nestjs-ddd-library';
43
+
44
+ // Generate a new UUID (v7)
45
+ const uuid = Uuid.generate();
46
+ console.log(uuid.value); // Generated UUID v7 string
47
+
48
+ // Create from existing UUID string
49
+ const uuidResult = Uuid.create('de49eb8d-9199-4046-a784-d015148f95a7');
50
+
51
+ if (uuidResult.isOk()) {
52
+ const uuid = uuidResult.unwrap();
53
+ console.log(uuid.value); // de49eb8d-9199-4046-a784-d015148f95a7
54
+ } else {
55
+ // Handle error
56
+ console.error(uuidResult.unwrapErr());
57
+ }
58
+ ```
59
+
60
+ #### Email
61
+
62
+ ```typescript
63
+ import { Email } from '@libs-for-dev/nestjs-ddd-library';
64
+
65
+ // Create an Email value object
66
+ const emailResult = Email.create('user@example.com');
67
+
68
+ if (emailResult.isOk()) {
69
+ const email = emailResult.unwrap();
70
+ console.log(email.value); // user@example.com
71
+ } else {
72
+ // Handle error
73
+ console.error(emailResult.unwrapErr());
74
+ }
75
+ ```
76
+
77
+ ### Entities
78
+
79
+ ```typescript
80
+ import { AbstractEntity, Uuid } from '@libs-for-dev/nestjs-ddd-library';
81
+
82
+ // Define entity properties
83
+ interface UserProps {
84
+ email: string;
85
+ name: string;
86
+ }
87
+
88
+ // Create an entity class
89
+ class User extends AbstractEntity<Uuid, UserProps> {
90
+ public static create(id: Uuid, props: UserProps): User {
91
+ return new User(id, props);
92
+ }
93
+
94
+ // Add domain methods here
95
+ public updateName(name: string): void {
96
+ this.propsData = {
97
+ props: {
98
+ ...this.props,
99
+ name
100
+ }
101
+ };
102
+ }
103
+ }
104
+
105
+ // Usage
106
+ const userId = Uuid.generate();
107
+ const user = User.create(userId, {
108
+ email: 'user@example.com',
109
+ name: 'John Doe'
110
+ });
111
+
112
+ // Access properties
113
+ console.log(user.id.value); // UUID
114
+ console.log(user.props.name); // John Doe
115
+
116
+ // Update properties using domain methods
117
+ user.updateName('Jane Doe');
118
+ console.log(user.props.name); // Jane Doe
119
+ ```
120
+
121
+ ## Domain Events
122
+
123
+ The library integrates with NestJS CQRS to support domain events:
124
+
125
+ ```typescript
126
+ import { AbstractEntity, Uuid } from '@libs-for-dev/nestjs-ddd-library';
127
+ import { IEvent } from '@nestjs/cqrs';
128
+
129
+ // Define a domain event
130
+ class UserCreatedEvent implements IEvent {
131
+ constructor(public readonly userId: string, public readonly email: string) {}
132
+ }
133
+
134
+ // Entity with domain events
135
+ class User extends AbstractEntity<Uuid, UserProps> {
136
+ public static create(id: Uuid, props: UserProps): User {
137
+ const user = new User(id, props);
138
+ user.apply(new UserCreatedEvent(id.value, props.email));
139
+ return user;
140
+ }
141
+ }
142
+
143
+ // In your NestJS application
144
+ @EventsHandler(UserCreatedEvent)
145
+ export class UserCreatedHandler implements IEventHandler<UserCreatedEvent> {
146
+ handle(event: UserCreatedEvent) {
147
+ console.log(`User created: ${event.userId} with email ${event.email}`);
148
+ }
149
+ }
150
+ ```
151
+
152
+ ## License
153
+
154
+ MIT
@@ -0,0 +1,12 @@
1
+ import type { IEvent } from '@nestjs/cqrs';
2
+ import { AggregateRoot } from '@nestjs/cqrs';
3
+ export interface EntityPropsInterface<T> {
4
+ props: T;
5
+ }
6
+ export declare abstract class AbstractEntity<Id, Props, Event extends IEvent = IEvent> extends AggregateRoot<Event> {
7
+ readonly id: Id;
8
+ get props(): Props;
9
+ protected propsData: EntityPropsInterface<Props>;
10
+ protected constructor(id: Id, props: Props);
11
+ equals(object: AbstractEntity<Id, Props>): boolean;
12
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractEntity = void 0;
4
+ const cqrs_1 = require("@nestjs/cqrs");
5
+ class AbstractEntity extends cqrs_1.AggregateRoot {
6
+ id;
7
+ get props() {
8
+ return Object.freeze(this.propsData.props);
9
+ }
10
+ propsData;
11
+ constructor(id, props) {
12
+ super();
13
+ this.id = id;
14
+ this.propsData = { props };
15
+ }
16
+ equals(object) {
17
+ if (this.constructor.name !== object.constructor.name) {
18
+ return false;
19
+ }
20
+ return this.id === object.id;
21
+ }
22
+ }
23
+ exports.AbstractEntity = AbstractEntity;
@@ -0,0 +1,11 @@
1
+ export interface PrimitiveInterface<T extends Date | Primitives> {
2
+ value: T;
3
+ }
4
+ export type Primitives = boolean | null | number | string;
5
+ type ValueObjectProps<T> = T extends Date | Primitives ? PrimitiveInterface<T> : T;
6
+ export declare abstract class AbstractValueObject<T> {
7
+ readonly props: Readonly<ValueObjectProps<T>>;
8
+ protected constructor(props: Readonly<ValueObjectProps<T>>);
9
+ equals(valueObject: AbstractValueObject<T>): boolean;
10
+ }
11
+ export {};
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractValueObject = void 0;
4
+ const fast_equals_1 = require("fast-equals");
5
+ class AbstractValueObject {
6
+ props;
7
+ constructor(props) {
8
+ this.props = props;
9
+ }
10
+ equals(valueObject) {
11
+ if (this.constructor.name !== valueObject.constructor.name) {
12
+ return false;
13
+ }
14
+ return (0, fast_equals_1.deepEqual)(valueObject.props, this.props);
15
+ }
16
+ }
17
+ exports.AbstractValueObject = AbstractValueObject;
@@ -0,0 +1,3 @@
1
+ export * from './value-objects';
2
+ export * from './abstract-entity';
3
+ export * from './abstract-value-object';
package/dist/index.js ADDED
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./value-objects"), exports);
18
+ __exportStar(require("./abstract-entity"), exports);
19
+ __exportStar(require("./abstract-value-object"), exports);
@@ -0,0 +1,7 @@
1
+ import type { Result } from 'oxide.ts';
2
+ import { AbstractValueObject } from '../abstract-value-object';
3
+ export declare class Email extends AbstractValueObject<string> {
4
+ get value(): string;
5
+ static create(email: string): Result<Email, Error>;
6
+ private static isValid;
7
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Email = void 0;
4
+ const class_validator_1 = require("class-validator");
5
+ const oxide_ts_1 = require("oxide.ts");
6
+ const abstract_value_object_1 = require("../abstract-value-object");
7
+ const not_an_email_error_1 = require("../value-objects/errors/not-an-email.error");
8
+ class Email extends abstract_value_object_1.AbstractValueObject {
9
+ get value() {
10
+ return this.props.value;
11
+ }
12
+ static create(email) {
13
+ if (!this.isValid(email)) {
14
+ return (0, oxide_ts_1.Err)(new not_an_email_error_1.NotAnEmailError(email));
15
+ }
16
+ return (0, oxide_ts_1.Ok)(new Email({ value: email }));
17
+ }
18
+ static isValid(email) {
19
+ return (0, class_validator_1.isEmail)(email);
20
+ }
21
+ }
22
+ exports.Email = Email;
@@ -0,0 +1,2 @@
1
+ export * from './not-an-email.error';
2
+ export * from './not-an-uuid.error';
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./not-an-email.error"), exports);
18
+ __exportStar(require("./not-an-uuid.error"), exports);
@@ -0,0 +1,4 @@
1
+ export declare class NotAnEmailError extends Error {
2
+ static readonly name = "NotAnEmailError";
3
+ constructor(email: string);
4
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NotAnEmailError = void 0;
4
+ class NotAnEmailError extends Error {
5
+ static name = 'NotAnEmailError';
6
+ constructor(email) {
7
+ super(`${email} is not a valid email`);
8
+ }
9
+ }
10
+ exports.NotAnEmailError = NotAnEmailError;
@@ -0,0 +1,4 @@
1
+ export declare class NotAnUuidError extends Error {
2
+ static readonly name = "NotAnUuidError";
3
+ constructor(uuid: string);
4
+ }
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.NotAnUuidError = void 0;
4
+ class NotAnUuidError extends Error {
5
+ static name = 'NotAnUuidError';
6
+ constructor(uuid) {
7
+ super(`${uuid} is not a valid UUID`);
8
+ }
9
+ }
10
+ exports.NotAnUuidError = NotAnUuidError;
@@ -0,0 +1,3 @@
1
+ export * from './errors';
2
+ export * from './email';
3
+ export * from './uuid';
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./errors"), exports);
18
+ __exportStar(require("./email"), exports);
19
+ __exportStar(require("./uuid"), exports);
@@ -0,0 +1,7 @@
1
+ import type { Result } from 'oxide.ts';
2
+ import { AbstractValueObject } from '../abstract-value-object';
3
+ export declare class Uuid extends AbstractValueObject<string> {
4
+ get value(): string;
5
+ static create(uuid: string): Result<Uuid, Error>;
6
+ static generate(): Uuid;
7
+ }
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Uuid = void 0;
4
+ const class_validator_1 = require("class-validator");
5
+ const oxide_ts_1 = require("oxide.ts");
6
+ const ui7_1 = require("ui7");
7
+ const abstract_value_object_1 = require("../abstract-value-object");
8
+ const not_an_uuid_error_1 = require("../value-objects/errors/not-an-uuid.error");
9
+ class Uuid extends abstract_value_object_1.AbstractValueObject {
10
+ get value() {
11
+ return this.props.value;
12
+ }
13
+ static create(uuid) {
14
+ if (!(0, class_validator_1.isUUID)(uuid)) {
15
+ return (0, oxide_ts_1.Err)(new not_an_uuid_error_1.NotAnUuidError(uuid));
16
+ }
17
+ return (0, oxide_ts_1.Ok)(new Uuid({ value: uuid }));
18
+ }
19
+ static generate() {
20
+ return new Uuid({ value: (0, ui7_1.v7)() });
21
+ }
22
+ }
23
+ exports.Uuid = Uuid;
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@libs-for-dev/nestjs-ddd-library",
3
+ "version": "1.0.0",
4
+ "description": "NestJS DDD library",
5
+ "keywords": [
6
+ "ddd-library",
7
+ "nestjs"
8
+ ],
9
+ "repository": "https://gitlab.com/libs-for-dev/nestjs/nestjs-ddd-library",
10
+ "license": "MIT",
11
+ "author": {
12
+ "name": "libs-for-dev"
13
+ },
14
+ "main": "dist/index.js",
15
+ "types": "dist/index.d.ts",
16
+ "files": [
17
+ "dist"
18
+ ],
19
+ "scripts": {
20
+ "build": "rm -rf ./dist && cti create ./src && tsc --project tsconfig.build.json && tsc-alias --project tsconfig.build.json && cti clean ./src",
21
+ "dev:quality": "yarn concurrently --timings --kill-others-on-fail 'yarn lint' 'yarn test'",
22
+ "dev:test:watch": "vitest",
23
+ "lint": "yarn concurrently --timings 'yarn:lint:*'",
24
+ "lint:audit": "yarn npm audit",
25
+ "lint:depcheck": "depcheck",
26
+ "lint:eslint": "eslint --cache --cache-location /tmp .",
27
+ "lint:scriptlint": "scriptlint",
28
+ "lint:tsc": "tsc --noEmit --pretty --project .",
29
+ "test": "yarn concurrently --timings 'npm:test:*'",
30
+ "test:mutation": "yarn stryker run",
31
+ "test:unit": "vitest run"
32
+ },
33
+ "devDependencies": {
34
+ "@libs-for-dev/eslint-rules": "2.3.3",
35
+ "@nestjs/common": "^11.1.0",
36
+ "@nestjs/core": "^11.1.0",
37
+ "@nestjs/cqrs": "^11.0.3",
38
+ "@stryker-mutator/core": "^8.7.1",
39
+ "@stryker-mutator/vitest-runner": "^8.7.1",
40
+ "@types/node": "^22.15.3",
41
+ "@vitest/coverage-v8": "^3.1.2",
42
+ "class-validator": "^0.14.1",
43
+ "concurrently": "^9.1.2",
44
+ "create-ts-index": "^1.14.0",
45
+ "depcheck": "^1.4.7",
46
+ "eslint": "^9.25.1",
47
+ "fast-equals": "^5.2.2",
48
+ "jiti": "^2.4.2",
49
+ "oxide.ts": "^1.1.0",
50
+ "reflect-metadata": "^0.2.2",
51
+ "scriptlint": "^3.0.0",
52
+ "tsc-alias": "^1.8.15",
53
+ "typescript": "^5.8.3",
54
+ "ui7": "^0.2.3",
55
+ "vitest": "^3.1.2",
56
+ "yarn-audit-fix": "^10.1.1"
57
+ },
58
+ "peerDependencies": {
59
+ "@nestjs/cqrs": ">=9.0.0 <12.0.0",
60
+ "class-validator": ">=0.12.0 <0.15.0",
61
+ "fast-equals": ">=5.0.0 <6.0.0",
62
+ "oxide.ts": ">=1.0.0 <2.0.0",
63
+ "ui7": ">=0.2.0 <0.3.0"
64
+ },
65
+ "packageManager": "yarn@4.9.1",
66
+ "engines": {
67
+ "node": "^23.11.0"
68
+ }
69
+ }