Domain-Driven Design Value Objects library for NestJS
npm install @nestjslatam/ddd-valueobjects-libbash
nest g library ddd-valueobjects --prefix @nestjslatam
`
๐ Example Application
This repository includes a complete example application that demonstrates how to use the library in a real NestJS project!
๐ View Example App Documentation
Run the example app:
`bash
npm run start:dev
Visit http://localhost:3000/api for Swagger documentation
`
The example app includes:
- Complete CRUD operations using Email and UUID value objects
- Result pattern for error handling
- REST API with Swagger documentation
- Domain entities with value objects
- Practical usage examples
Features
- ๐๏ธ NestJS Module: Full NestJS integration with DddValueObjectsModule
- ๏ฟฝ๏ธ Based on @nestjslatam/ddd-lib: Built on enterprise DDD patterns with validators and broken rules
- ๐ฏ Rich Value Objects: 13+ pre-built value objects for common domain scenarios
- โ
Validation System: Built-in validators with broken rules management
- ๐ Text VOs: Name, Description, Password, URL
- ๐ Identification VOs: Email, PhoneNumber, DocumentId (DNI, Passport, SSN), Uuid
- ๐ฐ Numeric VOs: Age, Money, Percentage
- ๐
Date VOs: DateRange, BirthDate
- ๐ Type-Safe: Full TypeScript support with strict typing
- ๐งช Well-Tested: 99% code coverage with over 680 tests.
- ๐ฆ NestJS CLI: Built with NestJS CLI following official library guidelines
- ๐๏ธ Organized Structure: Modular architecture with clear separation (core/implementations/module) - See Structure Guide
- ๐ Complete Docs: Value Objects Reference Guide
- CI/CD: Continuous integration and deployment pipeline with GitHub Actions.
- Git Hooks: Pre-commit and pre-push hooks with Husky to ensure code quality.
- Release Management: Automated release and changelog generation with standard-version.
Design and Architecture
$3
The primary objective of this library is to provide a robust and easy-to-use set of Domain-Driven Design (DDD) Value Objects for NestJS applications. It aims to promote best practices in domain modeling by offering immutable, self-validating objects that encapsulate business logic and ensure data integrity.
$3
This library is built on the following core concepts:
- Value Object: A fundamental DDD pattern representing a descriptive aspect of the domain with no conceptual identity. Value Objects are immutable and are compared by their values, not their identity.
- Immutability: Once a Value Object is created, it cannot be changed. Any operation that would change its value results in a new Value Object instance.
- Self-Validation: Each Value Object is responsible for its own validation. It cannot be created in an invalid state.
- Broken Rules: When validation fails, the Value Object provides a list of "broken rules" that explain what is wrong.
$3
The architecture is guided by the following principles:
- Based on @nestjslatam/ddd-lib: The library extends the core abstractions from @nestjslatam/ddd-lib, ensuring a solid foundation based on proven DDD patterns.
- Modularity: The project is organized into a core module (for base abstractions) and an implementations module (for concrete Value Objects), allowing for clear separation of concerns and easy extension.
- NestJS Integration: The library is provided as a NestJS module, allowing for seamless integration into any NestJS application.
$3
`
+------------------------------------------------+
| Your NestJS Application |
+------------------------------------------------+
| - Uses DddValueObjectsModule |
| - Creates and uses Value Objects (Name, Age) |
+------------------------^-----------------------+
|
+------------------------|-----------------------+
| @nestjslatam/ddd-valueobjects (This Library) |
+------------------------|-----------------------+
| - DddValueObjectsModule |
| - Concrete VOs (Name, Age, Money, etc.) |
| - Concrete Validators (NameValidator, etc.) |
+------------------------^-----------------------+
|
+------------------------|-----------------------+
| @nestjslatam/ddd-lib (Base Library) |
+------------------------|-----------------------+
| - DddValueObject (Abstract) |
| - AbstractRuleValidator (Abstract) |
| - Result Pattern |
+------------------------------------------------+
`
Installation
`bash
npm install @nestjslatam/ddd-valueobjects @nestjslatam/ddd-lib
`
NPM Package Overview


- Package: @nestjslatam/ddd-valueobjects
- Registry: https://www.npmjs.com/package/@nestjslatam/ddd-valueobjects
- Changelog: see CHANGELOG.md
- License: MIT
- Repository: https://github.com/nestjslatam/ddd-valueobjects
- Access: Public
$3
- @nestjs/common: ^10.0.0 || ^11.0.0
- @nestjs/core: ^10.0.0 || ^11.0.0
- reflect-metadata: ^0.1.13 || ^0.2.0
- rxjs: ^7.2.0
$3
`ts
import {
Description,
Url,
Name,
Age,
Money,
Percentage,
Email,
UUID,
} from '@nestjslatam/ddd-valueobjects';
// Description with options
const desc = Description.create('Short', { minLength: 3, maxLength: 10 });
// URL without protocol when allowed
const url = Url.create('example.com', { requireProtocol: false });
// Other VOs
const name = Name.create('John', 'Doe');
const age = Age.fromBirthDate(new Date('2000-01-01'));
const amount = Money.create(100, 'USD');
const pct = Percentage.create(15);
const emailOrError = Email.create('user@example.com');
const userId = UUID.generate();
`
$3
`ts
import { Module } from '@nestjs/common';
import { DddValueObjectsModule } from '@nestjslatam/ddd-valueobjects';
@Module({
imports: [DddValueObjectsModule.forRoot()],
})
export class AppModule {}
`
$3
- Conventional Commits
- GitHub tags (e.g., v1.0.1)
- Published package reflects latest tag
Quick Start
$3
`typescript
import { Module } from '@nestjs/common';
import { DddValueObjectsModule } from '@nestjslatam/ddd-valueobjects';
@Module({
imports: [DddValueObjectsModule.forRoot()],
})
export class AppModule {}
`
$3
`typescript
import { Injectable, BadRequestException } from '@nestjs/common';
import { Name, Email, Money, Age } from '@nestjslatam/ddd-valueobjects';
@Injectable()
export class UserService {
async createUser(firstName: string, lastName: string, emailStr: string, birthDate: Date) {
try {
// Create value objects with validation
const name = Name.create(firstName, lastName);
const age = Age.fromBirthDate(birthDate);
// Check broken rules
if (!name.isValid) {
throw new BadRequestException(name.brokenRules.getBrokenRulesAsString());
}
return {
name: name.getFullName(),
age: age.getValue(),
isAdult: age.isAdult(),
};
} catch (error) {
throw new BadRequestException(error.message);
}
}
async calculatePrice(basePrice: number, discountPercent: number) {
const price = Money.create(basePrice, 'USD');
const discount = Percentage.create(discountPercent);
const finalPrice = discount.decrease(price.amount);
return Money.create(finalPrice, 'USD').format();
}
}
`
$3
`typescript
import { Email, UUID, Result } from '@nestjslatam/ddd-valueobjects';
// Email with Result pattern
const emailOrError = Email.create('user@example.com');
if (emailOrError.isFailure) {
console.log(emailOrError.getError());
} else {
const email = emailOrError.getValue();
console.log(email.value); // "user@example.com"
}
// UUID generation
const userId = UUID.generate();
console.log(userId.value); // "550e8400-e29b-41d4-a716-446655440000"
`
Available Value Objects
$3
- Name: Full name with first, middle, and last names
- Description: Text descriptions with length constraints
- Url: URL validation with protocol and domain checking
$3
- PhoneNumber: Phone numbers with country codes and formatting
- DocumentId: Government IDs (DNI, Passport, SSN, Tax ID, Driver License)
$3
- Age: Age with category classification (child, teenager, adult, senior)
- Money: Monetary amounts with currency and arithmetic operations
- Percentage: Percentages with ratio conversion and calculations
$3
- DateRange: Date ranges with overlap detection and duration calculation
- BirthDate: Birth dates with age calculation and zodiac signs
$3
- Email: Simple email validation (Result pattern)
- UUID: UUID generation and validation (Result pattern)
๐ Complete Value Objects Reference
Usage
$3
`typescript
import { ValueObject, Result } from '@nestjslatam/ddd-valueobjects';
interface NameProps {
value: string;
}
export class Name extends ValueObject {
get value(): string {
return this.props.value;
}
private constructor(props: NameProps) {
super(props);
}
public static create(name: string): Result {
if (!name || name.trim().length === 0) {
return Result.fail('Name cannot be empty');
}
if (name.length < 2) {
return Result.fail('Name is too short');
}
if (name.length > 100) {
return Result.fail('Name is too long');
}
return Result.ok(new Name({ value: name.trim() }));
}
}
`
$3
`typescript
import { Email, UUID } from '@nestjslatam/ddd-valueobjects';
// Email
const emailOrError = Email.create('user@example.com');
if (emailOrError.isSuccess) {
const email = emailOrError.getValue();
console.log(email.value); // 'user@example.com'
}
// UUID
const uuidOrError = UUID.create('550e8400-e29b-41d4-a716-446655440000');
if (uuidOrError.isSuccess) {
const uuid = uuidOrError.getValue();
console.log(uuid.value);
}
// Generate UUID
const newUuid = UUID.generate();
`
$3
The library is designed as a NestJS module and automatically integrates with your application:
`typescript
import { Module } from '@nestjs/common';
import { DddValueObjectsModule } from '@nestjslatam/ddd-valueobjects';
import { UserService } from './user.service';
@Module({
imports: [DddValueObjectsModule.forRoot()],
providers: [UserService],
})
export class UserModule {}
`
Description and URL Options
These value objects support customizable options that are merged with sensible defaults at creation time. Validators always derive the current options from the subject at validation time, and create() forces revalidation to apply merged options.
- Description
- Defaults: minLength = 10, maxLength = 500, allowEmpty = false
- Customizable: { minLength?: number; maxLength?: number; allowEmpty?: boolean }
- Behavior:
- Trims input
- If allowEmpty is true, empty/whitespace-only values are accepted; other rules are skipped
- Enforces min/max length on trimmed text
- Requires at least one alphanumeric character
- Files: libs/ddd-valueobjects/src/implementations/description/description.value-object.ts, libs/ddd-valueobjects/src/implementations/description/description.validator.ts
- Url
- Defaults: requireProtocol = true, allowedProtocols = ['http', 'https']
- Customizable: { requireProtocol?: boolean; allowedProtocols?: string[] }
- Behavior:
- If requireProtocol is true, validates via URL parsing, protocol presence, allowed protocol list, and domain format
- If requireProtocol is false, accepts bare domains (e.g., example.com) and validates format; if a protocol is present, it must be in allowedProtocols
- Enforces maximum length from URL_CONSTRAINTS
- Files: libs/ddd-valueobjects/src/implementations/url/url.value-object.ts, libs/ddd-valueobjects/src/implementations/url/url.validator.ts
On error, create() throws with a detailed message including broken rules to aid diagnostics.
Result Pattern
The Result pattern helps handle errors functionally without throwing exceptions:
`typescript
const emailResult = Email.create('invalid-email');
if (emailResult.isFailure) {
console.log(emailResult.getError()); // 'Email format is invalid'
}
if (emailResult.isSuccess) {
const email = emailResult.getValue();
// Work with email
}
`
$3
`typescript
const results = [
Email.create('user1@example.com'),
Email.create('user2@example.com'),
Email.create('invalid'),
];
const combinedResult = Result.combine(results);
if (combinedResult.isFailure) {
console.log('At least one email is invalid');
}
`
Development
`bash
Install dependencies
npm install
Build the library with NestJS CLI
npm run build
Build and watch for changes
npm run build:watch
Run tests
npm test
Run tests in watch mode
npm run test:watch
Check coverage
npm run test:cov
Lint code
npm run lint
Format code
npm run format
`
Project Structure (NestJS Library)
This project follows the official NestJS library structure:
`
ddd-valueobjects/
โโโ apps/ # Example applications
โ โโโ example-app/ # Complete usage example
โ โโโ src/
โ โโโ users/ # Users module
โ โโโ main.ts
โโโ libs/
โ โโโ ddd-valueobjects/
โ โโโ src/
โ โ โโโ core/ # Base abstractions
โ โ โ โโโ value-object.base.ts # Base ValueObject class
โ โ โ โโโ result.ts # Result pattern
โ โ โ โโโ index.ts
โ โ โโโ implementations/ # Concrete value objects
โ โ โ โโโ email/ # Email VO
โ โ โ โ โโโ email.value-object.ts
โ โ โ โ โโโ index.ts
โ โ โ โโโ uuid/ # UUID VO
โ โ โ โ โโโ uuid.value-object.ts
โ โ โ โ โโโ index.ts
โ โ โ โโโ index.ts
โ โ โโโ module/ # NestJS integration
โ โ โ โโโ ddd-value-objects.module.ts
โ โ โ โโโ index.ts
โ โ โโโ index.ts # Public API
โ โโโ tsconfig.lib.json
โ โโโ STRUCTURE.md # ๐ Detailed structure guide
โโโ dist/ # Compiled output
โโโ coverage/ # Test coverage reports
โโโ nest-cli.json # NestJS CLI configuration
โโโ package.json
โโโ tsconfig.json # TypeScript base config
โโโ tsconfig.build.json
โโโ .eslintrc.js
โโโ .prettierrc
`
๐ Read the complete structure guide to understand the organization and how to extend it.
Using as a NestJS Library
This library is built following NestJS CLI library guidelines. You can:
$3
`typescript
// app.module.ts
import { Module } from '@nestjs/common';
import { DddValueObjectsModule } from '@nestjslatam/ddd-valueobjects';
@Module({
imports: [DddValueObjectsModule.forRoot()],
})
export class AppModule {}
`
$3
`typescript
import { Email, UUID, ValueObject, Result } from '@nestjslatam/ddd-valueobjects';
`
$3
`typescript
import { ValueObject, Result } from '@nestjslatam/ddd-valueobjects';
export class CustomValueObject extends ValueObject {
// Your implementation
}
`
Publishing
This library is published under the @nestjslatam organization on npm.
`bash
npm run build
npm publish
``