@nestjslatam/ddd-valueobjects-lib
v1.0.0
Published
Domain-Driven Design Value Objects library for NestJS
Maintainers
Readme
@nestjslatam/ddd-valueobjects-lib
A NestJS library providing Domain-Driven Design (DDD) Value Objects patterns. This library helps you build robust domain models with immutable value objects and proper validation, following the official NestJS library structure.
Generated with:
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:
npm run start:dev
# Visit http://localhost:3000/api for Swagger documentationThe 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
Objective
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.
Core Concepts
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.
Architectural Principles
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
coremodule (for base abstractions) and animplementationsmodule (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.
Architectural Diagram
+------------------------------------------------+
| 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
npm install @nestjslatam/ddd-valueobjects @nestjslatam/ddd-libNPM 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
Peer Dependencies
- @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
Quick Usage
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('[email protected]');
const userId = UUID.generate();NestJS Integration
import { Module } from '@nestjs/common';
import { DddValueObjectsModule } from '@nestjslatam/ddd-valueobjects';
@Module({
imports: [DddValueObjectsModule.forRoot()],
})
export class AppModule {}Versioning & Releases
- Conventional Commits
- GitHub tags (e.g., v1.0.1)
- Published package reflects latest tag
Quick Start
1. Import the Module
import { Module } from '@nestjs/common';
import { DddValueObjectsModule } from '@nestjslatam/ddd-valueobjects';
@Module({
imports: [DddValueObjectsModule.forRoot()],
})
export class AppModule {}2. Use Value Objects with Validators
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. Legacy Value Objects (Result Pattern)
import { Email, UUID, Result } from '@nestjslatam/ddd-valueobjects';
// Email with Result pattern
const emailOrError = Email.create('[email protected]');
if (emailOrError.isFailure) {
console.log(emailOrError.getError());
} else {
const email = emailOrError.getValue();
console.log(email.value); // "[email protected]"
}
// UUID generation
const userId = UUID.generate();
console.log(userId.value); // "550e8400-e29b-41d4-a716-446655440000"Available Value Objects
Text Value Objects
- Name: Full name with first, middle, and last names
- Description: Text descriptions with length constraints
- Url: URL validation with protocol and domain checking
Identification Value Objects
- PhoneNumber: Phone numbers with country codes and formatting
- DocumentId: Government IDs (DNI, Passport, SSN, Tax ID, Driver License)
Numeric Value Objects
- Age: Age with category classification (child, teenager, adult, senior)
- Money: Monetary amounts with currency and arithmetic operations
- Percentage: Percentages with ratio conversion and calculations
Date Value Objects
- DateRange: Date ranges with overlap detection and duration calculation
- BirthDate: Birth dates with age calculation and zodiac signs
Legacy Value Objects
- Email: Simple email validation (Result pattern)
- UUID: UUID generation and validation (Result pattern)
📖 Complete Value Objects Reference
Usage
Creating Custom Value Objects
import { ValueObject, Result } from '@nestjslatam/ddd-valueobjects';
interface NameProps {
value: string;
}
export class Name extends ValueObject<NameProps> {
get value(): string {
return this.props.value;
}
private constructor(props: NameProps) {
super(props);
}
public static create(name: string): Result<Name> {
if (!name || name.trim().length === 0) {
return Result.fail<Name>('Name cannot be empty');
}
if (name.length < 2) {
return Result.fail<Name>('Name is too short');
}
if (name.length > 100) {
return Result.fail<Name>('Name is too long');
}
return Result.ok<Name>(new Name({ value: name.trim() }));
}
}Using Pre-built Value Objects
import { Email, UUID } from '@nestjslatam/ddd-valueobjects';
// Email
const emailOrError = Email.create('[email protected]');
if (emailOrError.isSuccess) {
const email = emailOrError.getValue();
console.log(email.value); // '[email protected]'
}
// 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();Using with NestJS
The library is designed as a NestJS module and automatically integrates with your application:
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
allowEmptyistrue, 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
- Defaults:
Url
- Defaults:
requireProtocol = true,allowedProtocols = ['http', 'https'] - Customizable:
{ requireProtocol?: boolean; allowedProtocols?: string[] } - Behavior:
- If
requireProtocolistrue, validates viaURLparsing, protocol presence, allowed protocol list, and domain format - If
requireProtocolisfalse, accepts bare domains (e.g.,example.com) and validates format; if a protocol is present, it must be inallowedProtocols - Enforces maximum length from
URL_CONSTRAINTS
- If
- Files: libs/ddd-valueobjects/src/implementations/url/url.value-object.ts, libs/ddd-valueobjects/src/implementations/url/url.validator.ts
- Defaults:
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:
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
}Combining Results
const results = [
Email.create('[email protected]'),
Email.create('[email protected]'),
Email.create('invalid'),
];
const combinedResult = Result.combine(results);
if (combinedResult.isFailure) {
console.log('At least one email is invalid');
}Development
# 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 formatProject 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:
1. Use it in your NestJS applications
// app.module.ts
import { Module } from '@nestjs/common';
import { DddValueObjectsModule } from '@nestjslatam/ddd-valueobjects';
@Module({
imports: [DddValueObjectsModule.forRoot()],
})
export class AppModule {}2. Import value objects directly
import { Email, UUID, ValueObject, Result } from '@nestjslatam/ddd-valueobjects';3. Extend and create your own value objects
import { ValueObject, Result } from '@nestjslatam/ddd-valueobjects';
export class CustomValueObject extends ValueObject<YourProps> {
// Your implementation
}Publishing
This library is published under the @nestjslatam organization on npm.
npm run build
npm publishContributing
Contributions are welcome! Please ensure all tests pass and add tests for new features.
License
MIT
Made with ❤️ by NestJS LATAM
