Skip to content

Code examples for implementing robust, immutable Value Objects in TypeScript/Node.js using DDD principles. This repository serves as the official companion code for the article: https://poschuler.com/blog/implementing-value-objects-in-nodejs

License

Notifications You must be signed in to change notification settings

poschuler/nodejs-ddd-value-objects

Repository files navigation

Disclaimer: This README.md file was generated by an LLM (Large Language Model).

Node.js DDD Value Objects

Project Overview

This project provides a foundational implementation of Domain-Driven Design (DDD) Value Objects in TypeScript, tailored for Node.js applications. It demonstrates how to create immutable value objects that encapsulate data, ensure data integrity through validation, and provide value-based equality comparisons.

The primary goal is to illustrate best practices for designing robust and maintainable domain models using Value Objects, focusing on concepts like immutability, self-validation, and proper equality semantics.

Core Concepts Illustrated

1. Value Objects

Value Objects are objects that measure, quantify, or describe a thing in the domain. They are characterized by:

  • Immutability: Once created, their state cannot change.
  • Value-Based Equality: Two value objects are considered equal if all their constituent attributes are equal, not by their memory reference.
  • Self-Validation: They enforce their own invariants upon creation.
  • No Side Effects: Operations on value objects return new instances rather than modifying the original.

2. The ValueObject Abstract Class

The src/domain/abstractions/value-object.abstract.ts file defines an abstract ValueObject class. This class serves as the base for all concrete value objects in the system, providing:

  • A generic equals(other: ValueObject) method that performs deep value-based comparison.
  • An abstract getEqualityComponents(): any[] method, which concrete subclasses must implement to specify the properties that define their unique value for comparison.

Implemented Value Objects

The project includes several examples of practical value objects:

Email (src/domain/email.vo.ts)

  • Represents an email address.
  • Validates the email format and normalizes it (e.g., converts to lowercase).
  • Demonstrates basic string-based value object principles.

Amount (src/domain/amount.vo.ts)

  • Represents a monetary amount.
  • Utilizes bignumber.js to handle precise decimal arithmetic, avoiding common floating-point inaccuracies.
  • Enforces non-negative values.
  • Provides methods for arithmetic operations like add() and times().

Currency (src/domain/currency.vo.ts)

  • Represents a currency code (e.g., "USD", "EUR").
  • Ensures that only predefined, supported currency codes can be used.
  • Provides static factory methods (fromCode) and predefined instances (Currency.None, Currency.All).

Money (src/domain/money.vo.ts)

  • A composite value object combining Amount and Currency.
  • Demonstrates how to compose smaller value objects into more complex ones.
  • Includes business logic, such as ensuring that add() operations are only performed between Money objects of the same currency.
  • Provides static factory methods, including zero() for creating zero-value money instances.

Project Structure

├───src/
│   ├───app.ts                 # Example usage and demonstration of value objects
│   └───domain/
│       ├───abstractions/
│       │   └───value-object.abstract.ts # Base class for all Value Objects
│       ├───types/
│       │   └───currency.type.ts         # Defines available currency codes
│       ├───amount.vo.ts           # Value Object for monetary amounts
│       ├───currency.vo.ts         # Value Object for currency codes
│       ├───email.vo.ts            # Value Object for email addresses
│       └───money.vo.ts            # Composite Value Object for money (Amount + Currency)
├───package.json               # Project dependencies and scripts
├───tsconfig.json              # TypeScript configuration
├───biome.json                 # Biome configuration for linting and formatting
├───.gemini.md                 # Context for the Gemini AI assistant
└───...                        # Other configuration and development files

Architecture & Diagrams

This project uses Structurizr to create diagrams based on the C4 model. The diagrams are defined as code in the architecture/workspace.dsl file.

Running Structurizr Lite

You can explore the diagrams locally using Structurizr Lite, which can be run with Docker.

  1. Start the Structurizr Lite container:

    docker-compose up
  2. Access the diagrams: Open your web browser and navigate to http://localhost:8081.

The docker-compose.yml file is configured to mount the local ./architecture directory into the container, so any changes you make to the .dsl file will be reflected in the Structurizr Lite web interface.

Diagrams Overview

Note: The diagrams below are SVG exports of the architecture. For the most up-to-date and interactive versions, please run Structurizr Lite locally.

The following diagrams are defined in the workspace:

  • Domain-Layer-Overview: This component diagram shows all the value objects within the "Domain Layer" container and their relationships to each other and to the abstract ValueObject base class. It provides a high-level view of the entire value object system.

    Domain Layer Overview

  • Email-Value-Object: A focused component diagram showing only the Email value object and its inheritance from the ValueObject base class. This is useful for understanding a single, simple value object in isolation.

    Email Value Object

  • Money-Value-Object: A more detailed component diagram that illustrates the composite Money value object. It shows how Money is composed of the Amount and Currency value objects, their inheritance structure, and the external dependency on the bignumber.js library.

    Money Value Object

Technologies Used

  • TypeScript: For type safety and better code organization.
  • Node.js: The JavaScript runtime environment.
  • pnpm: A fast, disk space efficient package manager.
  • bignumber.js: A JavaScript library for arbitrary-precision decimal and non-decimal arithmetic.
  • tsx: A TypeScript execution environment for Node.js, enabling direct execution of TypeScript files.
  • Biome: A fast formatter and linter for web projects.

Getting Started

To set up the project and run the examples:

Prerequisites

  • Node.js (v18 or higher recommended)
  • pnpm (install globally: npm install -g pnpm)

Installation

  1. Clone the repository:

    git clone https://github.com/your-username/nodejs-ddd-value-objects.git
    cd nodejs-ddd-value-objects
  2. Install dependencies:

    pnpm install

Running Examples

The app.ts file contains various examples demonstrating the usage of the implemented value objects.

To run the examples:

pnpm dev

This command will run src/app.ts in watch mode, automatically restarting if you make changes.

To run the compiled version:

pnpm start

Development

Building the Project

To compile the TypeScript code into JavaScript:

pnpm build

This will output compiled JavaScript files into the dist directory.

Linting and Formatting

This project uses Biome for linting and formatting. To check for linting and formatting issues:

pnpm biome check .

To fix linting and formatting issues automatically:

pnpm biome format --write .

(Note: The actual biome commands might be slightly different based on package.json scripts, but these are general examples.)

License

This project is licensed under the MIT License. See the LICENSE file for more details.

About

Code examples for implementing robust, immutable Value Objects in TypeScript/Node.js using DDD principles. This repository serves as the official companion code for the article: https://poschuler.com/blog/implementing-value-objects-in-nodejs

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published