{"id":25838099,"url":"https://github.com/evyweb/simple-ddd-toolkit","last_synced_at":"2025-06-16T09:32:52.202Z","repository":{"id":262134305,"uuid":"766607496","full_name":"Evyweb/simple-ddd-toolkit","owner":"Evyweb","description":"A simple Typescript Domain Driven Design Toolkit to help you create your aggregates, domain events but also not directly related stuff like command handlers or results.","archived":false,"fork":false,"pushed_at":"2025-04-24T07:28:04.000Z","size":284,"stargazers_count":15,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-05-27T16:06:24.406Z","etag":null,"topics":["ddd","ddd-architecture","ddd-patterns","tdd","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Evyweb.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-03-03T18:16:21.000Z","updated_at":"2025-04-28T00:21:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"906a2f7f-9b77-4219-bb39-ec87a96f055a","html_url":"https://github.com/Evyweb/simple-ddd-toolkit","commit_stats":null,"previous_names":["evyweb/simple-ddd-toolkit"],"tags_count":36,"template":false,"template_full_name":null,"purl":"pkg:github/Evyweb/simple-ddd-toolkit","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Evyweb%2Fsimple-ddd-toolkit","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Evyweb%2Fsimple-ddd-toolkit/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Evyweb%2Fsimple-ddd-toolkit/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Evyweb%2Fsimple-ddd-toolkit/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Evyweb","download_url":"https://codeload.github.com/Evyweb/simple-ddd-toolkit/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Evyweb%2Fsimple-ddd-toolkit/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260133701,"owners_count":22963772,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ddd","ddd-architecture","ddd-patterns","tdd","typescript"],"created_at":"2025-03-01T03:40:34.574Z","updated_at":"2025-06-16T09:32:52.191Z","avatar_url":"https://github.com/Evyweb.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Simple DDD Toolkit 🛠️\n\n[![NPM Version](https://img.shields.io/npm/v/%40evyweb%2Fsimple-ddd-toolkit.svg?style=flat)]()\n![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/evyweb/simple-ddd-toolkit/main.yml)\n[![codecov](https://codecov.io/gh/Evyweb/simple-ddd-toolkit/graph/badge.svg?token=A3Z8UCNHDY)](https://codecov.io/gh/Evyweb/simple-ddd-toolkit)\n\n![NPM Downloads](https://img.shields.io/npm/dm/%40evyweb%2Fsimple-ddd-toolkit)\n[![NPM Downloads](https://img.shields.io/npm/dt/%40evyweb%2Fsimple-ddd-toolkit.svg?style=flat)]()\n\nA simple Domain Driven Design Toolkit created to help developers understand how to implement DDD concepts. It also contains some useful stuff not directly related to DDD, like a command bus or result pattern.\n\nThis toolkit is not a library, it's just a set of classes and interfaces that you can use, and it has no dependency.\n\n🚧🦺\n**WARNING: This toolkit is still under development and not intended to be used in production. All classes and interfaces are subject to change and may provoke breaking changes**.\n🦺🚧\n## Installation\n\n```bash\nnpm install --save @evyweb/simple-ddd-toolkit\n```\n\n## Features\n\n- [x] Aggregate - A cluster of domain objects that can be treated as a single unit\n- [x] Command - A request to perform an action or change the state of the system\n- [x] Command handler - Processes commands and updates the system's state\n- [x] Command bus - Routes commands to the appropriate command handlers\n- [x] Domain events - Notifications about changes in the domain\n- [x] Entity - An object with a distinct identity that runs throughout its lifecycle\n- [x] Errors - Custom error types for domain and technical errors\n- [x] Event bus - Decouples components by allowing them to communicate through events\n- [x] Middleware - Adds additional behavior to commands and queries\n- [x] Query - A request for data from the system\n- [x] Query handler - Processes queries and returns the requested information\n- [x] Query bus - Routes queries to the appropriate query handlers\n- [x] Result - A pattern to handle errors and success cases in a more explicit way\n- [x] Use case - Encapsulates application-specific business rules\n- [x] Value object - An object that measures, quantifies, or describes aspects of a domain\n\n# Value Object\n\n## What is a value object?\n\nIn the book \"**Implementing Domain-Driven Design**\" by **Vaughn Vernon**, a `Value Object` is described as an object that measures, quantifies or describes certain aspects of a domain without having a conceptual identity.\n\nThe key characteristics of a value object include:\n\n- **Immutability**: Once created, it cannot be altered. Any change to the properties of a value object should result in the creation of a new object.\n- **Equality Based on Attributes**: Two instances of value objects are considered equal not based on their identity, but if all their properties have the same values.\n- **Replaceability**: They can be replaced by other instances with the same values without disrupting the integrity of the domain.\n- **Lack of Identity**: Value objects do not have a distinct identity that distinguishes them.\n- **Design by Contract**: They can validate conditions that must be true throughout the lifetime of the object (e.g., an email address must contain an \"@\" symbol).\n\n## Some examples of value objects\n\n-   `Address`\n-   `Email`\n-   `PhoneNumber`\n-   `DateRange`\n-   `Color`\n-   `Weight`\n-   `Height`\n-   `Temperature`\n-   `Money`\n-   etc...\n\n## How is a value object different from an entity?\n\nAn `Entity` is an object that has a distinct identity that runs throughout its lifecycle. It is defined by its attributes and its identity. An entity can be mutable, and its identity is not based on its attributes.\n\nA `Value Object`, on the other hand, is defined by its attributes and not by its identity. It is immutable and can be replaced by another instance with the same values without disrupting the integrity of the domain.\n\n## Let's consider an example\n\nWe can say that a `Color` is defined by its `red`, `green`, and `blue` values which are numbers.\n\n### Immutability\n\nOnce a `Color` object is created, it cannot be altered. Any change to the properties of a `Color` object should result in the creation of a new color.\n\n### Equality Based on Attributes\n\nThat's the combination of the `red`, `green`, and `blue` values that define a `color`.\n\nTwo colors are considered equal if they have the same amount of `red`, `green`, and `blue`.\n\n### Replaceability\n\nA `Color` object can be replaced by another instance with the same values.\n\n### Lack of Identity\n\nA `Color` object does not have a distinct identity that distinguishes it.\n\nNote that it can depend on the context. For example, in a graphic design application, a color may have an identity if it is used to represent a specific color in a palette.\nBut let's consider the `Color` object as a value object for this example.\n\n### Design by Contract\n\nA `Color` object can validate conditions that must be true throughout its lifetime. For example, the `red`, `green`, and `blue` values must be between 0 and 255.\n\n## How to implement this value object\n\nWe can create the color value object by extending the `ValueObject` class provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { ValueObject } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class Color extends ValueObject\u003c{ red: number; green: number; blue: number }\u003e {}\n```\n\nYou can also create a type or an interface to define the `red`, `green`, and `blue` values.\n\n```typescript\ninterface RGBColor {\n  red: number;\n  green: number;\n  blue: number;\n}\n\nexport class Color extends ValueObject\u003cRGBColor\u003e {}\n```\n\nBy default, you will not be able to create an instance of the `Color` class because its constructor is protected.\n\n```typescript\nconst color = new Color({ red: 255, green: 0, blue: 0 }); // Error\n```\n\nTo create a new instance of the `Color` class, you need to create a **static factory method** that will validate the `red`, `green`, and `blue` values before creating the instance.\n\nA possible implementation to do that can be:\n\n```typescript\nimport { ValueObject } from \"@evyweb/simple-ddd-toolkit\";\n\ninterface RGBColor {\n  red: number;\n  green: number;\n  blue: number;\n}\n\nexport class Color extends ValueObject\u003cRGBColor\u003e {\n  static create({ red, green, blue }: RGBColor): Color {\n    // Validate the red, green, and blue values here\n    Color.validateRGBColorFormat(red);\n    Color.validateRGBColorFormat(green);\n    Color.validateRGBColorFormat(blue);\n\n    return new Color({ red, green, blue });\n  }\n\n  private static validateRGBColorFormat(value: number): void {\n    if (value \u003c 0 || value \u003e 255) {\n      throw new Error(\"RGB color value must be between 0 and 255.\");\n    }\n  }\n}\n```\n\nNow you can create a new instance of the `Color` class using the `create` method.\n\n```typescript\nconst color = Color.create({ red: 255, green: 0, blue: 0 });\n```\n\nBy using a factory method, you can ensure that the `Color` object is created with valid values.\n\nYou can also easily create different static factory methods to create colors based on different criteria.\n\n```typescript\nconst color1 = Color.fromRGB({ red: 255, green: 0, blue: 0 });\nconst color2 = Color.fromHEX(\"#FF0000\");\n```\n\nNote that the `fromRGB` and `fromHEX` methods are just static method names, you can choose any name that makes sense for you.\nThe important thing is that they are static factory methods that create a `Color` object and validate the input values before creating the object.\n\nThe word 'from' is a common convention to indicate that the method creates an object from a specific format.\n\nNow that the value object is created, you can use the `equals` method provided by the `ValueObject` class, you can compare two `Color` objects.\n\n```typescript\nconst color1 = Color.fromRGB({ red: 255, green: 0, blue: 0 });\nconst color2 = Color.fromHEX(\"#FF0000\");\n\nconsole.log(color1.equals(color2)); // true\n```\n\nHere is the full implementation of the `Color` class:\n\n```typescript\nimport { ValueObject } from \"@evyweb/simple-ddd-toolkit\";\n\ninterface RGBColor {\n  red: number;\n  green: number;\n  blue: number;\n}\n\nexport class Color extends ValueObject\u003cRGBColor\u003e {\n  static fromRGB({ red, green, blue }: RGBColor): Color {\n    Color.validateRGBColorFormat(red);\n    Color.validateRGBColorFormat(green);\n    Color.validateRGBColorFormat(blue);\n\n    return new Color({ red, green, blue });\n  }\n\n  static fromHEX(hexValue: string): Color {\n    Color.validateHexColorFormat(hexValue);\n\n    return Color.fromRGB({\n      red: parseInt(hexValue.substring(1, 3), 16),\n      green: parseInt(hexValue.substring(3, 5), 16),\n      blue: parseInt(hexValue.substring(5, 7), 16),\n    });\n  }\n\n  private static validateRGBColorFormat(value: number): void {\n    if (value \u003c 0 || value \u003e 255) {\n      throw new Error(\"RGB color value must be between 0 and 255.\");\n    }\n  }\n\n  private static validateHexColorFormat(hex: string) {\n    if (!/^#[0-9A-F]{6}$/i.test(hex)) {\n      throw new Error(\"Invalid HEX color format.\");\n    }\n  }\n}\n```\n\nWhen the color object is created, it is automatically immutable. You will not be able to change the `red`, `green`, and `blue` values of the color object.\n\n## Get the value(s) of the value object\n\nYou can retrieve the `red`, `green`, and `blue` values of the color object using the `get` method provided by the `ValueObject` class.\n\n```typescript\nconst color = Color.fromRGB({ red: 255, green: 255, blue: 255 });\n\ncolor.get(\"red\"); // 255\ncolor.get(\"green\"); // 255\ncolor.get(\"blue\"); // 255\n```\n\nYou will get autocomplete suggestions for the `get` method based on the properties of the `Color` class.\n\n## Update the value(s) of the value object\n\nAs mentioned earlier, a value object is immutable. You cannot change the `red`, `green`, and `blue` values of the color object directly.\nYou will need to create a new color object with the updated values.\n\n```typescript\nconst color = Color.fromRGB({ red: 255, green: 255, blue: 255 });\nconst newColor = color.removeRed();\n\nclass Color extends ValueObject\u003cRGBColor\u003e {\n  // ...\n  removeRed(): Color {\n    return Color.fromRGB({\n      red: 0,\n      green: this.get(\"green\"),\n      blue: this.get(\"blue\"),\n    });\n  }\n}\n```\n\nIn this example, the `removeRed` method creates a new color object with the `red` value set to 0 and the `green` and `blue` values copied from the original color object.\n\n## Using directly the constructor (not recommended)\n\nIf you want to use a constructor instead of a static factory method, you can simply make the constructor public.\n\n```typescript\nexport class Color extends ValueObject\u003cRGBColor\u003e {\n  constructor({ red, green, blue }: RGBColor) {\n    // Validate the red, green, and blue values here\n    super({ red, green, blue });\n  }\n\n  // Other methods\n}\n```\n\nNow you can create a new instance of the `Color` class using the constructor directly.\n\n```typescript\nconst color = new Color({ red: 255, green: 0, blue: 0 });\n```\n\nHowever, it is highly recommended to use static factory methods instead of constructors to create value objects. This way, you can ensure that the value object is always created with valid values.\n\n## Nested values (not recommended)\n\nIt is recommended to avoid nested values in the value object.\nTry to keep the value object as flat as possible and simple to use.\nIf you need to store complex data, consider creating a separate value object for that data.\n\n# Result Pattern\n\nThe `Result` pattern is a way to handle errors and success cases in a more explicit way.\n\nIt is a simple pattern that consists of two possible outcomes: `Ok` and `Fail`.\n\nThe `Ok` outcome represents a successful operation and contains the result of the operation.\n\nThe `Fail` outcome represents a failed operation and contains an error object that describes the reason for the failure.\n\n## How to use the Result pattern\n\nThe `Result` class provided by the `simple-ddd-toolkit` package can be used to create `Ok` and `Fail` outcomes.\n\n```typescript\nimport { Result } from \"@evyweb/simple-ddd-toolkit\";\n\nconst successResult = Result.ok(\"Operation successful\");\nconst errorResult = Result.fail(new Error(\"Operation failed\"));\n```\n\nYou can check if the result is successful using the `isOk` method.\n\n```typescript\nif (currentResult.isOk()) {\n  console.log(currentResult.getValue());\n}\n```\n\nYou can check if the result is a failure using the `isFail` method.\n\n```typescript\nif (currentResult.isFail()) {\n  console.log(currentResult.getError());\n}\n```\n\nNote that you cannot have both a value and an error in the same result object. It is either an `Ok` outcome with a value or a `Fail` outcome with an error.\n\n## Combine factory methods with result pattern\n\nNote that you can `combine the value object factory method with the result pattern` to return a result object that contains the created value object or an error.\nIt can be useful to handle validation errors when creating the value object.\n\n```typescript\ninterface RGBColor {\n  red: number;\n  green: number;\n  blue: number;\n}\n\nclass InvalidRGBColorError extends Error {\n  constructor() {\n    super(\"Invalid RGB color format.\");\n  }\n}\n\nexport class Color extends ValueObject\u003cRGBColor\u003e {\n  static fromRGB(rgbColor: RGBColor): Result\u003cColor, InvalidRGBColorError\u003e {\n    if (Color.isInvalidRGBColor(rgbColor)) {\n      return Result.fail(new InvalidRGBColorError());\n    }\n\n    return Result.ok(new Color(rgbColor));\n  }\n\n  private static isInvalidRGBColor(rgbColor: RGBColor): boolean {\n      return rgbColor.red \u003c 0 || rgbColor.red \u003e 255 ||\n             rgbColor.green \u003c 0 || rgbColor.green \u003e 255 ||\n             rgbColor.blue \u003c 0 || rgbColor.blue \u003e 255;\n  }\n}\n\nconst colorCreation = Color.fromRGB({ red: 255, green: 255, blue: 255 });\nif (colorCreation.isOk()) {\n  // Do something with the color\n  console.log(colorCreation.getValue())\n} else {\n  // Handle the error\n  console.error(colorCreation.getError())\n}\n```\n\nNote the return type of the `fromRGB` method: `Result\u003cColor, InvalidRGBColorError\u003e`.\n\nThat means that the `fromRGB` method can return an `Ok` outcome with a `Color` object or a `Fail` outcome with an `InvalidRGBColorError` object.\n\n# Errors\n\nIn the `simple-ddd-toolkit`, errors are represented as classes that extend the `Error` class.\n\nTo help you create more explicit errors, we gave you 3 classes `TechnicalError`, `DomainError` and `CustomError` that you can extend to create your custom errors.\nNote: `TechnicalError` and `DomainError` extend the `CustomError` class.\n\nThis way, you can create different types of errors based on the context in which they occur and react differently if the error is a technical error or a domain error.\n\n## How to create a domain error\n\nTo create a domain error, you can extend the `DomainError` class provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { DomainError } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class AnyDomainError extends DomainError {\n  public readonly __TAG = \"AnyDomainError\";\n\n  constructor() {\n    super(\"Any domain related error message\"); // Can be also a translation key\n  }\n}\n```\n\nWhen you will create the error object, you will have access to two helpers methods `isDomainError` and `isTechnicalError` to check the type of the error more easily.\n\n```typescript\nconst error = new AnyDomainError();\n\nif (error.isDomainError()) {\n  // Handle domain error\n} else if (error.isTechnicalError()) {\n  // Handle technical error\n}\n```\n\nThese are helper methods that allow you to quickly determine if an error is a domain error or a technical error.\n\n## How to create a technical error\n\nTo create a technical error, you can extend the `TechnicalError` class provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { TechnicalError } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class AnyTechnicalError extends TechnicalError {\n  public readonly __TAG = \"AnyTechnicalError\";\n\n  constructor() {\n    super(\"Any technical related error message\"); // Can be also a translation key\n  }\n}\n```\n\nWhen you will create the error object, you will have access to two helpers methods `isDomainError` and `isTechnicalError` to check the type of the error more easily.\n\n```typescript\nconst error = new AnyTechnicalError();\n\nif (error.isDomainError()) {\n  // Handle domain error\n} else if (error.isTechnicalError()) {\n  // Handle technical error\n}\n```\n\n## How to create a custom error\n\nThe `CustomError` class provided by the `simple-ddd-toolkit` package can be used to create custom errors.\n\n```typescript\nimport { CustomError } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class AnyCustomError extends CustomError {\n  public readonly __TAG = \"AnyCustomError\";\n\n  constructor() {\n    super(\"Any custom error message\"); // Can be also a translation key\n  }\n\n  isDomainError(): boolean {\n    return false;\n  }\n\n  isTechnicalError(): boolean {\n    return true;\n  }\n}\n```\n\nWhen you define the custom error class, you will need to:\n1. Define a `__TAG` property to identify the error type\n2. Override the `isDomainError` and `isTechnicalError` methods to specify the type of error\n\n# Entity\n\nAn `Entity` is an object encapsulating domain logic and data, and it has a distinct identity that runs throughout its lifecycle.\n\n\"We design a domain concept as an `Entity` when we care about its individuality, when distinguishing it from all other objects in a system is a mandatory constraint.\nAn entity is a unique thing and is capable of being changed continuously over a long period of time\" - Vaughn Vernon\n\n### Difference between `Entity` and `Value Object`\n\nIt is the unique identity and mutability that distinguishes an `Entity` from a `Value Object`.\n\n### Identity\n\nValue objects can serve as holders of unique identity. They are immutable, which ensures identity stability, and any behavior specific to the kind of identity is centralized.\n\n## Some examples of entities\n\n- `User`\n- `Order`\n- `Product`\n- `Customer`\n- `Account`\n- `Invoice`\n\n## How to implement an entity\n\nTo create an entity, you can extend the `Entity` class provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { Entity } from \"@evyweb/simple-ddd-toolkit\";\nimport { UUID } from \"@evyweb/simple-ddd-toolkit\";\n\ninterface UserData {\n  id: UUID;\n  name: string;\n}\n\nexport class User extends Entity\u003cUserData\u003e {\n  static create(userData: UserData): User {\n    // Validation rules here\n    return new User(userData);\n  }\n}\n```\n\nHere `UUID` is a type that represents a universally unique identifier. It is exposed by the `simple-ddd-toolkit` package as a ready to use Value Object.\n\nJust like the `ValueObject` class, the `Entity` class has a protected constructor, which means you cannot create an instance of the `User` class directly.\n\n```typescript\nconst user = new User({ id: UUID.create(), name: \"John Doe\" }); // Error\n```\n\nTo create a new instance of the `User` class, you need to use a static factory method.\n\n```typescript\nconst user = User.create({ id: UUID.create(), name: \"John Doe\" });\n```\n\nThis way, you can ensure that the entity is created with valid data.\n\nSimilarly to the `ValueObject` class, the `Entity` factory functions can be combined with the `Result` pattern to handle validation errors.\n\n```typescript\nimport { Result, DomainError } from \"@evyweb/simple-ddd-toolkit\";\nimport { UUID } from \"@evyweb/simple-ddd-toolkit\";\n\ninterface UserData {\n  id: UUID;\n  name: string;\n}\n\nclass InvalidUserNameError extends DomainError {\n  constructor() {\n    super(\"Username cannot contain special characters.\");\n  }\n}\n\nexport class User extends Entity\u003cUserData\u003e {\n  static create(userData: UserData): Result\u003cUser, InvalidUserNameError\u003e {\n    if (User.isInvalidUserName(userData.name)) {\n      return Result.fail(new InvalidUserNameError());\n    }\n\n    return Result.ok(new User(userData));\n  }\n\n  private static isInvalidUserName(name: string): boolean {\n    return /[^a-zA-Z0-9]/.test(name);\n  }\n}\n```\n\nIn this example, the `create` method returns a `Result` object that contains either a `User` entity or an `InvalidUserNameError` error.\n\n## Get the properties of an entity\n\nYou can retrieve the `id` and `name` values of the user entity using the `get` method provided by the `Entity` class.\n\n```typescript\nconst user = User.create({ id: UUID.create(), name: \"John Doe\" });\n\nuser.get(\"id\"); // UUID\nuser.get(\"name\"); // 'John Doe'\n```\n\nNote that the `id` returned by the `get` method is a `UUID` value object.\nTo get the actual value of the `UUID` object, you can use the `get('value')` method provided by the `UUID` class.\n\n```typescript\nconst userId = user.get(\"id\").get(\"value\");\n```\n\nYou can also use the shortcut method `id()` to get the `id` value directly.\n\n```typescript\nconst userId = user.id(); // Similar to user.get('id').get('value')\n```\n\n## Update entity properties\n\nAn entity is mutable, which means you can change its properties. \nNote that the setter method is protected, so you can only update the entity properties using the methods provided by the entity class.\n\n```typescript\nclass User extends Entity\u003cUserData\u003e {\n  // ...\n  renameTo(newName: string): void {\n    this.set(\"name\", newName);\n  }\n  // ...\n}\n\nconst user = User.create({ id: UUID.create(), name: \"John Doe\" });\n\nuser.renameTo(\"Jane Doe\");\n```\n\nIn this example, the `name` property of the user entity is updated to 'Jane Doe'.\n\n## Identity comparison\n\nEntities are compared based on their identity, not their attributes.\n\n```typescript\nconst userId = UUID.create();\nconst user1 = User.create({ id: userId, name: \"John Doe\" });\nconst user2 = User.create({ id: userId, name: \"Jane Doe\" });\n\nconsole.log(user1.equals(user2)); // true\n```\n\nIn this example, even though the `name` properties of the two user entities are different, they are considered as the same because they have the same identities.\n\n## toObject() helper method\n\nYou can convert an entity to a plain JavaScript object using the `toObject` method provided by the `Entity` class.\n\n```typescript\nconst user = User.create({ id: UUID.create(), name: \"John Doe\" });\n\nuser.toObject(); // { id: '...', name: 'John Doe' }\n```\n\nThe `toObject` method returns an object with the properties of the entity.\n\n# Aggregate\n\nAn `Aggregate` is a cluster of domain objects that can be treated as a single unit.\n\nIt is an important concept in Domain-Driven Design (DDD) that helps to maintain consistency and integrity in the domain model.\n\nAn aggregate has the following characteristics:\n\n- **Root Entity**: An aggregate has a root entity that acts as the entry point to the aggregate. The root entity is responsible for maintaining the consistency of the aggregate.\n- **Boundary**: An aggregate defines a boundary within which all domain objects are consistent with each other. The root entity enforces the consistency of the aggregate by controlling access to its internal objects.\n- **Transaction**: An aggregate is treated as a single unit in a transaction. All changes to the aggregate are made atomically, ensuring that the aggregate remains in a consistent state.\n- **Identity**: An aggregate has a unique identity that distinguishes it from other aggregates in the system.\n- **Encapsulation**: An aggregate encapsulates its internal objects and exposes only the root entity to the outside world.\n- **Invariants**: An aggregate enforces invariants that must be true for the aggregate to be in a valid state.\n\n## How to implement an aggregate\n\nTo create an aggregate, you can extend the `Aggregate` class provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { Aggregate, UUID } from \"@evyweb/simple-ddd-toolkit\";\n\ninterface OrderItem {\n    productId: string;\n    quantity: number;\n}\n\ninterface OrderData {\n  id: UUID;\n  items: OrderItem[];\n  date: Date;\n}\n\nexport class Order extends Aggregate\u003cOrderData\u003e {\n  static create(orderData: OrderData): Order {\n    // Validation rules here\n    return new Order(orderData);\n  }\n\n  addItem(productId: string, quantity: number): void {\n    if (this.get(\"items\").length \u003e= 10) {\n      throw new Error(\"An order cannot contain more than 10 items.\");\n    }\n    const item = {productId, quantity};\n    this.get(\"items\").push(item);\n  }\n}\n\nconst order = await orderRepository.getById(\"order-id\");\norder.addItem(\"product1\", 2);\norderRepository.save(order);\n```\n\nIn this example, the `Order` class extends the `Aggregate` class and defines a `create` method to create a new order.\n\nThe `addItem` method adds a new item to the order. It checks if the order already contains 10 items and throws an error if the limit is reached.\n\nThe `Order` class can be used to create and manage orders in the domain model.\n\nThe `Order` is then saved to the repository using the `save` method provided by the repository.\n\n## Domain events\n\nAn aggregate can emit domain events to notify other parts of the system about changes in its state.\n\n### How to create a domain event\n\nTo create a domain event, you can extend the `DomainEvent` class provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { DomainEvent } from \"@evyweb/simple-ddd-toolkit\";\nimport { v4 as uuidv4 } from 'uuid';\n\nexport class ProductAddedToOrderEvent extends DomainEvent {\n  public readonly __TAG = \"ProductAddedToOrderEvent\";\n\n  constructor(\n    public readonly orderId: string,\n    public readonly productId: string,\n    public readonly quantity: number\n  ) {\n    super({\n      eventId: uuidv4(),\n      metadata: {\n        orderId,\n        productId,\n        quantity: quantity.toString()\n      }\n    });\n  }\n}\n```\n\nIn this example, the `ProductAddedToOrderEvent` class extends the `DomainEvent` class and defines the metadata for the event.\n\nWhen creating a `DomainEvent`, the following data are available and can be provided in the constructor:\n\n- **eventId**: A unique identifier for the event. Required when creating a domain event.\n- **__TAG**: The type of the event, must be defined as a property in the class.\n- **occurredOn**: The date and time when the event occurred. Generated automatically if not provided.\n- **metadata**: Additional data related to the event. Empty by default.\n- **payload**: The data related to the event. Empty by default\n\n### Emitting domain events\n\nTo emit domain events from an aggregate, you need to add the events to the queue first.\nTo do so, you can use the `addEvent` method provided by the `Aggregate` class.\n\n```typescript\nconst order = await orderRepository.getById(\"order-id\");\norder.addItem(\"product1\", 2);\n\norder.addEvent(new ProductAddedToOrderEvent(order.id(), \"product1\", 2));\n\norderRepository.save(order);\n\neventBus.dispatchEvents(order.getEvents());\n```\n\nThen you need to dispatch the events to the event bus using the `dispatchEvents` method provided by the event bus.\nYou need to inject the event bus into the commandHandler to be able to use it.\n\n```typescript\nimport { CommandHandler, EventBus, Command, DomainEvent } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class AddProductToOrderCommandHandler extends CommandHandler\u003c\n  AddProductToOrderCommand,\n  void\n\u003e {\n  constructor(\n    private readonly orderRepository: OrderRepository,\n    private readonly eventBus: EventBus\n  ) {\n    super();\n  }\n\n  async handle(command: AddProductToOrderCommand): Promise\u003cvoid\u003e {\n    const order = await this.orderRepository.getById(command.orderId);\n    order.addItem(command.productId, command.quantity);\n\n    order.addEvent(\n      new ProductAddedToOrderEvent(order.id(), command.productId, command.quantity)\n    );\n\n    this.orderRepository.save(order);\n    this.eventBus.dispatchEvents(order.getEvents());\n  }\n}\n```\n\nIn this example, the `AddProductToOrderCommandHandler` class injects the event bus and dispatches the events after saving the order.\n\nThe event bus is responsible for dispatching the events to the appropriate event handlers.\n\n# Commands\n\nA `Command` is a request to perform an action or change the state of the system.\n\nIt encapsulates the data required to perform the action and is sent to a `Command Handler` to execute the action.\n\nThe `Command Handler` is responsible for processing the command and updating the system's state accordingly.\n\n## How to create a command\n\nTo create a command, you can extend the `Command` class provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { Command } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class CreateCharacterCommand extends Command {\n  public readonly __TAG = \"CreateCharacterCommand\";\n  public readonly name: string;\n\n  constructor(name: string) {\n    super();\n    this.name = name;\n  }\n}\n```\nIt is important to define the `__TAG` property for each command. This tag is used to identify the command and to register the corresponding command handler.\n\n### How to create a command handler\n\nTo create a command handler, you can extend the `CommandHandler` class provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { CommandHandler } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class CreateCharacterCommandHandler extends CommandHandler\u003c\n  CreateCharacterCommand,\n  void\n\u003e {\n  public readonly __TAG = \"CreateCharacterCommandHandler\";\n\n  async handle(command: CreateCharacterCommand): Promise\u003cvoid\u003e {\n    // Process the command here\n  }\n}\n```\n\nIt is important to define the `__TAG` property for each command handler. This tag is used to identify the command handler and must match the pattern `{CommandName}Handler` where `{CommandName}` is the `__TAG` property of the command.\n\nMost of the time, a command handler will not return anything, so the second type parameter of the `CommandHandler` class is `void`.\nBut it can return a value if needed (e.g., the id of the created element).\n\n### How to dispatch a command\n\nCommands are dispatched to the appropriate command handler using a `Command Bus`.\n\nTo dispatch a command, you can use the `execute` method provided by the command bus.\n\n```typescript\nconst command = new CreateCharacterCommand(name);\nawait commandBus.execute(command);\n```\n\nThe command bus is responsible for routing the command to the correct command handler and executing the handler.\n\n### Registering command handlers\n\nTo register a command handler with the command bus, you can use the `register` method provided by the command bus.\n\n```typescript\nimport { Bus, Command } from \"@evyweb/simple-ddd-toolkit\";\n\nconst commandBus = new Bus\u003cCommand\u003e();\ncommandBus.register(() =\u003e new CreateCharacterCommandHandler());\n```\n\nMake sure to use the `__TAG` property of the command as the key when registering the command handler. For example, if the command's `__TAG` is \"CreateCharacterCommand\", then the key should be \"CreateCharacterCommand\".\n\nYou can also use an ioc container (like inversify or simple-ddd-toolkit) to resolve the command handler.\n\n```typescript\ncommandBus.register(() =\u003e container.get(DI.CreateCharacterCommandHandler));\n```\n\n# Query\n\nA `Query` is a request for data from the system.\n\nIt encapsulates the data required to retrieve information and is sent to a `Query Handler` to fetch the data.\n\nThe `Query Handler` is responsible for processing the query and returning the requested information.\n\n## How to create a query\n\nTo create a query, you can extend the `Query` class provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { Query } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class LoadCharacterCreationDialogQuery extends Query {\n    public readonly __TAG = \"LoadCharacterCreationDialogQuery\";\n}\n```\n\nIt is important to define the `__TAG` property for each query. This tag is used to identify the query and to register the corresponding query handler.\n\n### How to create a query handler\n\nTo create a query handler, you can extend the `QueryHandler` class provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { QueryHandler, IResponse } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class LoadCharacterCreationDialogQueryHandler extends QueryHandler\u003c\n  LoadCharacterCreationDialogQuery,\n  LoadCharacterCreationDialogResponse\n\u003e {\n  public readonly __TAG = \"LoadCharacterCreationDialogQueryHandler\";\n\n  async handle(\n    _query: LoadCharacterCreationDialogQuery\n  ): Promise\u003cLoadCharacterCreationDialogResponse\u003e {\n    // Data can be fetched from a database, an API, or any other source\n    return {\n      title: \"Add a new character\",\n      subTitle: \"Fill out the form to create a new character.\",\n      form: {\n        avatar: {\n          label: \"Avatar\",\n          required: false,\n          value: \"/images/avatars/default.png\",\n        },\n        name: {\n          label: \"Name *\",\n          placeholder: \"Character name\",\n          required: true,\n          value: \"\",\n        },\n        submit: {\n          label: \"Validate\",\n        },\n        cancel: {\n          label: \"Cancel\",\n        },\n      },\n    };\n  }\n}\n```\n\nIt is important to define the `__TAG` property for each query handler. This tag is used to identify the query handler and must match the pattern `{QueryName}Handler` where `{QueryName}` is the `__TAG` property of the query.\n\nThe `LoadCharacterCreationDialogQueryHandler` class extends the `QueryHandler` class and defines the response type as `LoadCharacterCreationDialogResponse`.\nThe response is also known as a ViewModel.\n\n```typescript\ninterface CharacterCreationFormViewModel {\n  avatar: {\n    label: string;\n    required: boolean;\n    value: string;\n  };\n  name: {\n    label: string;\n    placeholder: string;\n    required: boolean;\n    value: string;\n  };\n  submit: {\n    label: string;\n  };\n  cancel: {\n    label: string;\n  };\n}\n\nexport interface LoadCharacterCreationDialogResponse extends IResponse {\n  title: string;\n  subTitle: string;\n  form: CharacterCreationFormViewModel;\n}\n```\n\n### How to dispatch a query\n\nQueries are dispatched to the appropriate query handler using a `Query Bus`.\n\nTo dispatch a query, you can use the `execute` method provided by the query bus.\n\n```typescript\nconst query = new LoadCharacterCreationDialogQuery();\nconst response = await queryBus.execute(query);\n```\n\nThe query bus is responsible for routing the query to the correct query handler and executing the handler.\n\n### Registering query handlers\n\nTo register a query handler with the query bus, you can use the `register` method provided by the query bus.\n\n```typescript\nimport { Bus, Query } from \"@evyweb/simple-ddd-toolkit\";\n\nconst queryBus = new Bus\u003cQuery\u003e();\nqueryBus.register(() =\u003e new LoadCharacterCreationDialogQueryHandler());\n```\n\n**Make sure to use the `__TAG` property of the query as the key when registering the query handler.**\n\nYou can also use an ioc container (like inversify or simple-ddd-toolkit) to resolve the query handler.\n\n```typescript\nqueryBus.register(() =\u003e container.get(DI.LoadCharacterCreationDialogQueryHandler));\n```\n\n# Middleware\n\nMiddleware is a way to add additional behavior to commands and queries without modifying the core logic.\n\nIt allows you to intercept commands and queries before they are processed by the command or query handler.\n\n## How to create middleware\n\nYou can create middlewares for both commands and queries by implementing the `Middleware\u003cCommand\u003e` and `Middleware\u003cQuery\u003e` interfaces provided by the `simple-ddd-toolkit` package.\n\n### Command middleware\n\n```typescript\nimport { Middleware, Command } from \"@evyweb/simple-ddd-toolkit\";\nimport { Logger } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class CommandLoggerMiddleware implements Middleware\u003cCommand\u003e {\n  constructor(\n    private readonly logger: Logger,\n    private readonly middlewareId: string\n  ) {}\n\n  async execute\u003cT\u003e(command: Command, next: (command: Command) =\u003e Promise\u003cT\u003e): Promise\u003cT\u003e {\n    const date = new Date().toISOString();\n    this.logger.log(\n      `[${date}][${this.middlewareId}][${command.__TAG}] - ${JSON.stringify(\n        command\n      )}`\n    );\n    return next(command);\n  }\n}\n```\n\n### Query middleware\n\n```typescript\nimport { Middleware, Query } from \"@evyweb/simple-ddd-toolkit\";\nimport { Logger } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class QueryLoggerMiddleware implements Middleware\u003cQuery\u003e {\n  constructor(\n    private readonly logger: Logger,\n    private readonly middlewareId: string\n  ) {}\n\n  execute\u003cT\u003e(query: Query, next: (query: Query) =\u003e Promise\u003cT\u003e): Promise\u003cT\u003e {\n    const date = new Date().toISOString();\n    this.logger.log(\n      `[${date}][${this.middlewareId}][${query.__TAG}] - ${JSON.stringify(\n        query\n      )}`\n    );\n    return next(query);\n  }\n}\n```\n\nIn these examples, the `CommandLoggerMiddleware` and `QueryLoggerMiddleware` classes log the command or query data before passing it to the next middleware or the command/query handler.\n\n## How to register middleware\n\nTo register middleware with the command bus or query bus, you can use the `use` method provided by the bus.\n\n```typescript\ncommandBus.use(new CommandLoggerMiddleware(logger, \"CommandLoggerMiddleware\"));\nqueryBus.use(new QueryLoggerMiddleware(logger, \"QueryLoggerMiddleware\"));\n```\n\n**Middleware will be executed in the order they are registered.**\n\nBut you can also use an ioc container to resolve the middleware.\n\n```typescript\ncommandBus.use(container.get(DI.CommandLoggerMiddleware));\nqueryBus.use(container.get(DI.QueryLoggerMiddleware));\n```\n\n# Event Bus\n\nThe `Event Bus` is a way to decouple components in a system by allowing them to communicate through events.\n\nIt provides a mechanism for publishing and subscribing to events, allowing different parts of the system to react to changes without being tightly coupled.\n\n## How to register event handlers\n\nSimilarly to the command bus and query bus, the event bus is responsible for routing events to the appropriate event handlers.\n\n```typescript\nimport { EventBus } from \"@evyweb/simple-ddd-toolkit\";\n\nconst eventBus = new EventBus();\neventBus.on(\"ConversationCreatedEvent\", () =\u003e new CreateDefaultPostEventHandler());\n```\n\nYou can also use an ioc container to resolve the event handler.\n\n```typescript\neventBus.on(\n  \"ConversationCreatedEvent\",\n  () =\u003e container.get(DI.CreateDefaultPostEventHandler)\n);\n```\n\nYou can group all the event types in a single file to avoid typos or to group them by domain.\n\n```typescript\nexport const EventTypes = {\n  ConversationCreatedEvent: \"ConversationCreatedEvent\",\n  PostCreatedEvent: \"PostCreatedEvent\",\n  // ...\n};\n\neventBus.on(\n  EventTypes.ConversationCreatedEvent,\n  () =\u003e new CreateDefaultPostEventHandler()\n);\n```\n\n### How to create an event handler\n\nTo create an event handler, you can implement the `IEventHandler` interface provided by the `simple-ddd-toolkit` package.\n\n```typescript\nimport { IEventHandler, DomainEvent, Command, Bus } from \"@evyweb/simple-ddd-toolkit\";\n\nexport class CreateDefaultPostEventHandler implements IEventHandler\u003cConversationCreatedEvent\u003e {\n  public readonly __TAG = \"CreateDefaultPostEventHandler\";\n\n  constructor(private readonly commandBus: Bus\u003cCommand\u003e) {}\n\n  async handle(event: ConversationCreatedEvent): Promise\u003cvoid\u003e {\n    const { conversationId, characterId, postId, userId, participantsIds } =\n      event.metadata;\n    const command = new CreateDefaultPostCommand(\n      conversationId,\n      userId,\n      characterId,\n      postId,\n      participantsIds\n    );\n    await this.commandBus.execute(command);\n  }\n}\n```\n\nIn this example, the `CreateDefaultPostEventHandler` class implements the `IEventHandler` interface and defines the `handle` method to process the event.\n\nThe event handler can execute commands, queries, or any other logic based on the event data.\n\nHere the event handler creates a default post when a conversation is created.\n\n**It's recommended to define a `__TAG` for each event handler to easily identify them. However, it's not mandatory.**\n\n## How to dispatch the events\n\nTo dispatch events to the event bus, you can use the `dispatch` and `dispatchAsync` methods provided by the event bus.\n\n```typescript\nconst event = new ProductAddedToOrderEvent(order.id(), \"product1\", 2);\n\n// Use dispatch if you want to wait for the event to be processed before continuing (also present on aggregates)\nawait eventBus.dispatch(event);\n\n// if you want to dispatch events without blocking the user, prefer dispatchAsync (also present on aggregates)\n// Don't use: \"eventBus.dispatch(event);\" without await\n\n// Use:\neventBus.dispatchAsync(event);\n```\n\nThe `dispatch` method has to be executed with an `await`, as it is synchronous and will wait for the event to be processed before continuing.\n\nThe `dispatchAsync` method is asynchronous, which means it will dispatch the event without waiting for it to be processed.\n\nUsing `dispatchAsync` can be useful when you want to dispatch events without blocking the main thread. For example, when you want to dispatch events in the background.\nYou can use for eventual consistency.\n\n### Notes:\n\nUnder the hood, the `dispatchAsync` method uses the `setImmediate` function to dispatch events asynchronously.\n\n#### Why not use `Promise.resolve().then(() =\u003e eventBus.dispatch(event))` or `process.nextTick(() =\u003e eventBus.dispatch(event))` or remove the `await` from `eventBus.dispatch(event)`?\n\nThe `setImmediate` function is a more reliable way to dispatch events asynchronously because it ensures that the event is dispatched after the current phase of the event loop has completed. Tasks added via `setImmediate` are placed in the **macrotask queue** (specifically in the \"check\" phase), whereas tasks from `Promise.resolve` or `process.nextTick` are placed in the **microtask queue**.\n\nThis distinction is crucial because the **microtask queue** is processed before the event loop moves to the next phase, which means that using `Promise.resolve` or `process.nextTick` can introduce unintended blocking if the tasks are computationally expensive or numerous. In contrast, `setImmediate` ensures that the current phase of the event loop (including microtasks) is entirely finished before the event is dispatched, reducing the risk of blocking or performance degradation.\n\n#### Why not use `setTimeout(() =\u003e eventBus.dispatch(event), 0)`?\n\nWhile `setTimeout` is similar to `setImmediate`, it schedules the task in the **timer queue**, which is processed after a minimum delay and only after the next phase of the event loop. `setImmediate` schedules the task in the **check queue**, allowing it to be executed earlier than a `setTimeout` task.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevyweb%2Fsimple-ddd-toolkit","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fevyweb%2Fsimple-ddd-toolkit","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fevyweb%2Fsimple-ddd-toolkit/lists"}