{"id":19408612,"url":"https://github.com/4lessandrodev/rich-domain","last_synced_at":"2025-04-09T14:06:43.285Z","repository":{"id":45820427,"uuid":"513649117","full_name":"4lessandrodev/rich-domain","owner":"4lessandrodev","description":"A lib to help you create a robust project based on domain driven-design (ddd) principles with typescript and zero dependencies.","archived":false,"fork":false,"pushed_at":"2024-04-28T04:46:08.000Z","size":1330,"stargazers_count":103,"open_issues_count":2,"forks_count":4,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-28T07:22:32.023Z","etag":null,"topics":["aggregate","auto-mapper","ddd","domain","domain-data","domain-driven-design","domain-entity","domain-events","domain-model","results","rich-domain","typescript","typescript-library","value-object"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/rich-domain","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/4lessandrodev.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-07-13T19:37:32.000Z","updated_at":"2024-05-01T19:37:48.268Z","dependencies_parsed_at":"2024-02-27T15:28:43.370Z","dependency_job_id":"709df831-327e-42ef-81f6-2bb924ddc7f3","html_url":"https://github.com/4lessandrodev/rich-domain","commit_stats":{"total_commits":143,"total_committers":2,"mean_commits":71.5,"dds":0.07692307692307687,"last_synced_commit":"39a93cccd6c1d12acce1788e870dd7b9aad18fcf"},"previous_names":[],"tags_count":24,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4lessandrodev%2Frich-domain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4lessandrodev%2Frich-domain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4lessandrodev%2Frich-domain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/4lessandrodev%2Frich-domain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/4lessandrodev","download_url":"https://codeload.github.com/4lessandrodev/rich-domain/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248054227,"owners_count":21039952,"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":["aggregate","auto-mapper","ddd","domain","domain-data","domain-driven-design","domain-entity","domain-events","domain-model","results","rich-domain","typescript","typescript-library","value-object"],"created_at":"2024-11-10T12:06:50.776Z","updated_at":"2025-04-09T14:06:43.260Z","avatar_url":"https://github.com/4lessandrodev.png","language":"TypeScript","funding_links":[],"categories":["TypeScript"],"sub_categories":[],"readme":"# rich-domain\nBuild great app using domain driven design\n\n\u003ca href=\"https://www.npmjs.com/package/rich-domain\" rel=\"nofollow\" class=\"keychainify-checked\"\u003e\n \u003cimg src=\"https://badgen.net/github/checks/4lessandrodev/rich-domain/main\" \n alt=\"checks\" \n style=\"max-width: 100%;\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/rich-domain\" rel=\"nofollow\" class=\"keychainify-checked\"\u003e\n \u003cimg src=\"https://badgen.net/github/stars/4lessandrodev/rich-domain\" \n alt=\"stars\" \n style=\"max-width: 100%;\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/rich-domain\" rel=\"nofollow\" class=\"keychainify-checked\"\u003e\n \u003cimg src=\"https://badgen.net/github/commits/4lessandrodev/rich-domain/main\" \n alt=\"commits\" \n style=\"max-width: 100%;\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/rich-domain\" rel=\"nofollow\" class=\"keychainify-checked\"\u003e\n \u003cimg src=\"https://badgen.net/github/last-commit/4lessandrodev/rich-domain/main\" \n alt=\"last commit\" \n style=\"max-width: 100%;\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/rich-domain\" rel=\"nofollow\" class=\"keychainify-checked\"\u003e\n \u003cimg src=\"https://badgen.net/github/license/4lessandrodev/rich-domain\" \n alt=\"license\" \n style=\"max-width: 100%;\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/rich-domain\" rel=\"nofollow\" class=\"keychainify-checked\"\u003e\n \u003cimg src=\"https://badgen.net/github/dependabot/4lessandrodev/rich-domain\" \n alt=\"dependabot\" \n style=\"max-width: 100%;\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/rich-domain\" rel=\"nofollow\" class=\"keychainify-checked\"\u003e\n \u003cimg src=\"https://badgen.net/github/tag/4lessandrodev/rich-domain\" \n alt=\"tags\" \n style=\"max-width: 100%;\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://www.npmjs.com/package/rich-domain\" rel=\"nofollow\" class=\"keychainify-checked\"\u003e\n \u003cimg src=\"https://badgen.net/github/closed-issues/4lessandrodev/rich-domain\" \n alt=\"issues\" \n style=\"max-width: 100%;\"\u003e\n\u003c/a\u003e\n\u003ca href=\"https://github.com/4lessandrodev/rich-domain?branch=main\" rel=\"nofollow\" class=\"keychainify-checked\"\u003e\n \u003cimg src=\"https://img.shields.io/codecov/c/github/dwyl/hapi-auth-jwt2.svg?maxAge=2592000\" \n alt=\"issues\" \n style=\"max-width: 100%;\"\u003e\n\u003c/a\u003e\n\n---\n\n## About the lib\n\nThis package provide utils file and interfaces to assistant build a complex application as domain driving design and nodeJS with typescript.\n\n### Simple App Example\n\nA simple app example available on [link](https://github.com/4lessandrodev/ddd-app)\n\n---\n## Documentation\n\n### Folders\nFolders structure suggestion\nDivided by\n\n- Domain layer\n- Application layer\n- Infra layer\n\n```shell\n  $ tree\n  .\n  ├── package.json\n  ├── README.md\n  └── src\n       ├── configs\n       │    └── env\n       │\n       ├── shared\n       │    ├── constants\n       │    └── utils\n       │\n       └── modules\n            │ \n            └── [module-name]\n                  │ \n                  │── domain\n                  │     ├── value-objects\n                  │     ├── entities\n                  │     ├── aggregates\n                  │     ├── events\n                  │     ├── adapters\n                  │     ├── interfaces\n                  │     └── domain-services\n                  │ \n                  ├── application\n                  │     ├── services\n                  │     └── use-cases\n                  │ \n                  └── infra\n                        ├── models\n                        ├── adapters\n                        ├── services\n                        ├── factories\n                        └── repository\n\n```\n\n---\n\n## Core\n\n---\n\n\u003cimg src=\"./cover.png\" alt=\"image\" width=\"100%\"\u003e\n\n---\n\n## 1. Ubiquitous language:\n\n- Language and terms agreed upon by both business users and developers, within a bounded context\n- Entities with the same name in a different context can have different behavior and data\n- Bounded context helps in single responsibility for domain models\n\n## 2. Rich domain model:\n\n- Models (entities, value objects, aggregates) with rich behavior are preferred over anemic domain models (entities without behavior, which only keep data and represent the DB tables)\n- Due to single responsibility principle (a class or method should have only one reason to change), non-cohesive behavior should be delegated to other classes (or even handled inside domain services) when necessary\n- Model methods can also delegate the task to domain services by raising domain events\n\n## 3. Thin domain service working on rich domain models:\n\n- Domain services should not hold state (application services are not domain services, they are on the outer layer close to the UI layer, and can hold application/task state)\n- Domain services have very little behavior and only which does not fit cohesively in any domain model\n- Domain services sit in the core domain layer along with entities, value objects, aggregates and domain events, and expose domain models in their interfaces\n\n## 4. Layers in a DDD application:\n\n- Core domain layer (domain services, entities, value objects, aggregates and domain events)\n- Core domain layer is surrounded by the UI/Application layer and Infrastructure layer\n- UI/Application layer (UI and application service facade with messaging, JSON, XML capabilities, session, etc.)\n- Infrastructure layer (persistence, file system, network, mail, logging, etc.)\n\n## 5. Entities:\n\n- Live longer than the application, should endure restarts, and are persisted and read from data sources (DB, file system, network, etc.)\n- Have an id (preferably a GUID rather than a DB generated int because business transactions do not rely on persistence, can be persisted after other operations carried out in model's behavior)\n- Have entity semantics (equality and `GetHashCode()` defined by class name + id)\n- Behavior in an entity mostly orchestrates value objects for a use case\n- Entity class should not have public property setters, setting a property should be a behavior method\n- Entities should not have bidirectional relations (depending on the bounded context, either an egg can have a chicken or a chicken can have eggs, but not both)\n- Entity relations should not reflect the complete set of DB foreign key relationships, should be bare down to the minimum for performing the behavior inside the bounded context\n- Entity relations should not hold a reference to another entity class, it can only keep the id of another entity\n- If a business transaction needs a reference to other entities in relation, aggregates should be used instead (aggregates can hold a reference to other aggregate roots, which are entity classes by definition)\n\n## 6. Value objects:\n\n- Are only identified by their values, not by their ids (for example money is a value object as long as we are not tracking individual banknotes, if we need to track individual banknotes then it should be a banknote entity)\n- Can be used to measure or describe things (name, description, amount, height, date, time, range, address, etc.)\n- You can combine other value types that usually go together into a new value object type, like address (city, street, country, postal code) or ...range, or ...type\n- Prefer to put the behavior on value objects rather than on entities because value objects are immutable and do not have side effects (like changing their state or changing the state of any entity)\n- Can be part of an entity\n- Have value semantics (equality and `GetHashCode()` defined by property values)\n- Should be immutable, behaviors should not change the state of a value object, but can rather create a new value object (should act similar to C# strings, structs, ints, and other value types)\n- Can be persisted but only as part of an entity, not individually\n\n## 7. Factories:\n\n- Create, build aggregates and entities:\n- Static Create...() factory method on a model class is used to guard against the construction of an invalid or incomplete model\n- The model class should not have a public default constructor (however if it is to be persisted, for Entity Framework to work, it can have a protected or private default constructor)\n\n## 8. Aggregates:\n\n- Encapsulate and are composed of entity classes and value objects that change together in a business transaction\n- Aggregates are a transactional graph of model objects\n- Aggregate root should be an entity, an aggregate can even be a single entity\n- Aggregate can keep a reference to other aggregate roots, but not to other entity classes which are not aggregate roots themselves\n- Aggregate should not keep a reference to other aggregate root entity classes if those other entities do not change together with this aggregate root entity\n- Aggregate can also keep the id of another entity, but keeping too many foreign key ids is a code smell (why?)\n- If deleting an entity has a cascade effect on the other entities referenced by class in the object graph, these entities are part of the same aggregate, if not, they should not be inside this aggregate\n\n## 9. Repositories:\n\n- Persist and read aggregates to/from DB or file system\n- Should have an interface close to a collection but should allow only the necessary operations needed for this aggregate (for example an aggregate might not need to be allowed to get updated or deleted)\n- Should not be generic (should be specific for the aggregate type)\n- Can have specific query methods if needed (like `FindByName()` etc.)\n- Do not use lazy loading, instead use eager loading (use Include(...) in Entity Framework), else you can face \"N+1 problem\"s and excessive number of queries sent to DB\n- Can have specific methods that only load some of the columns from a table\n- Repository add/update/remove operation should commit to DB by itself (call Entity Framework ...Context.SaveChanges() at the end), because aggregate operations should be ACID transactions\n- Repository interface sits inside Core domain layer, but implementations are inside Infrastructure layer\n- Repositories are not used inside the domain models (entities, value objects, aggregates)\n\n## 10. Shared kernel:\n\n- Is where cross-cutting concerns or common types shared by all bounded contexts sit (like entity abstract base type, value object abstract base type, common value objects, authorization, etc.)\n\n## 11. Domain events:\n\n- Can be raised when a state change occurs in an entity\n- Decouple models from each other\n- Only used when an event needs to be handled inside a different model than the one raising this event, or handled inside a domain service or even an application service\n- Are immutable classes, that represent past, named in the past tense, and cannot change (...Changed, ...Happened, etc.)\n- Should include the time that this event was raised, as well as any other useful info for handling the event, as well as the id of the entity which raised the event\n- Should not have behavior\n- Domain events are subscribed to with a callback (lambda), or using pub sub interfaces, on a singleton or static event message bus\n- Domain events implemented this way can be subscribed to and handled in the aggregate root of the entity which raised the event, or in domain services, or even in UI/Application layer\n- Domain events are raised synchronously, if an asynchronous task needs to be carried out, it can be done inside the event handler (async-await pattern)\n- Outside applications can also be triggered by using a message queue or an enterprise service bus (ESB) inside the domain event handler\n\n## 12. Anti-corruption layer:\n\n- Used to translate models from outside systems or legacy apps to models inside the bounded context and vice versa, and also to ease the communication with legacy services\n- Can use service facades and model adapters\n\n\n\n---\n\n## 13 - Summary - Basic Usage\n\n### Value Object\n\n\u003e A value object is a small, simple object that represents a single value or characteristic, such as a monetary amount or a date. It is characterized by having no identity of its own, meaning it is equal to another value object if its values are equal, regardless of its reference. Value objects are often used in domain-driven design to represent simple entities in the system.\n\n#### Create a value object with business rules.\n\n```ts\n\nimport { ValueObject, Ok, Fail, Result } from 'rich-domain';\n\ninterface Props {\n    amount: number;\n}\n\n// simple example as monetary value object business behavior. \n// Extends ValueObject to inherit some behaviors, utils and helpers.\nexport default class Money extends ValueObject\u003cProps\u003e {\n    \n    // private constructor. Avoid public new.\n    private constructor(props: Props) {\n        super(props);\n    }\n\n    // any business rule behavior. Check if current amount is greater than x.\n    public isGt(x: Money): boolean {\n        const { number: Check } = this.validator;\n        const xValue = x.get('amount');\n        const currentValue = this.get('amount');\n        return Check(xValue).isGreaterThan(currentValue);\n    }\n\n    // any business rule behavior. Calculate and sum x to current value.\n    public sum(x: Money): Money {\n        const { number: Calc } = this.util;\n        const value = x.get('amount');\n        const current = this.get('amount');\n        const amount = Calc(current).sum(value);\n        return new Money({ amount }); // immutable\n    }\n\n    // any business rule behavior. Calculate and subtract x from current value.\n    public subtract(x: Money): Money {\n        const { number: Calc } = this.util;\n        const value = x.get('amount');\n        const current = this.get('amount');\n        const amount = Calc(current).subtract(value);\n        return new Money({ amount }); // immutable\n    }\n\n    // any business rule to validate state. Value must be greater or equal 0\n    public static isValidProps({ amount }: Props): boolean {\n        const { number: Check } = this.validator;\n        return Check(amount).isPositive();\n    }\n\n    // shortcut to create a zero value.\n    public static zero(): Money {\n        return new Money({ amount: 0 }); // immutable\n    }\n\n    // factory method to create an instance and validate value.\n    public static create(amount: number): Result\u003cMoney | null\u003e {\n\n        const isValid = this.isValidProps({ amount });\n        if(!isValid) return Fail(\"Invalid amount for money\");\n\n        return Ok(new Money({ amount }));\n    }\n\n    // try initialize an instance. throw erro if provide an invalid value\n    public static init(amount: number): Money {\n\n        const isValid = this.isValidProps({ amount });\n        if(!isValid) throw new Error(\"Invalid amount for money\");\n\n        return new Money({ amount });\n    }\n}\n\n```\n\nHow to use value object instance\n\n```ts\n\n// operation result\nconst resA = Money.create(500);\n\n// check if provided a valid value\nconsole.log(resA.isOk());\n\n// \u003e true\n\n\n// money instance\nconst moneyA = resA.value() as Money;\n\nmoneyA.get(\"amount\"); \n\n// 500\n\n// using methods \nmoneyA.isGt(Money.zero());\n\n// \u003e true\n\nconst moneyB = Money.create(100).value() as Money;\n\nconst moneyC = moneyA.sum(moneyB);\n\nconst value = moneyC.get('amount');\n\nconsole.log(value); \n\n// \u003e 600\n\n\n```\n\n---\n\n### Entity\n\n\u003e An entity in domain-driven design is an object that represents a concept in the real world and has a unique identity and attributes. It is a fundamental building block used to model complex business domains.\n\n#### Create an entity with business rules.\n\n```ts\n\nimport { Entity, Ok, Fail, Result, UID } from 'rich-domain';\n\ninterface Props {\n    id?: UID;\n    total: Money;\n    discount: Money;\n    fees: Money;\n}\n\n// simple example as payment entity using money value object\n// Extends Entity to inherit some behaviors, utils and helpers.\nexport default class Payment extends Entity\u003cProps\u003e {\n\n    // private constructor\n    private constructor(props: Props){\n        super(props);\n    }\n\n    // any business rule behavior. Update total. Update object state.\n    public applyFees(fees: Money): Payment {\n        this.props.total = this.props.total.sum(fees);\n        this.props.fees = fees;\n        return this;\n    }\n\n    // any business rule behavior. Update object state.\n    public applyDiscount(discount: Money): Payment {\n        this.props.total = this.props.total.subtract(discount);\n        this.props.discount = discount;\n        return this;\n    }\n\n    // factory method to create a instance. Value must be positive.\n    public static create(props: Props): Result\u003cPayment\u003e {\n        return Ok(new Payment(props));\n    }\n\n    public static init(props: Props): Payment {\n        return new Payment(props);\n    }\n}\n\n```\n\nHow to use entity instance\n\n```ts\n\n// operation result\nconst total = Money.create(500).value() as Money;\nconst discount = Money.zero();\nconst fees = Money.zero();\n\n// create a payment\nconst payment = Payment.create({ total, discount, fees }).value();\n\n// create fee and discount\nconst fee = Money.create(17.50).value() as Money;\nconst disc = Money.create(170.50).value() as Money;\n\n// apply fee and discount\nconst result = payment.applyFees(fee).applyDiscount(disc);\n\n// get object from domain entity\nconsole.log(result.toObject());\n\n{\n    \"id\": \"d7fc98f5-9711-4ad8-aa16-70cb8a52244a\",\n    \"total\": { \n        \"amount\": 347 \n    },\n    \"discount\": { \n        \"amount\": 170.50 \n    },\n    \"fees\": { \n        \"amount\": 17.50 \n    },\n    \"createdAt\":\"2023-01-30T23:11:17.815Z\",\n    \"updatedAt\":\"2023-01-30T23:11:17.815Z\"\n}\n\n```\n\n---\n\n\nSee also how to use Aggregate.\n\n### Aggregate\n\nEncapsulate and are composed of entity classes and value objects that change together in a business transaction\n\n#### Create an aggregate to compose your context.\n\nIn my example, let's use the context of payment. All payment transactions are encapsulated by an order (payment order) that represents a user's purchasing context.\n\n```ts\n\nimport { Aggregate, Ok, Fail, Result, UID, EventHandler } from 'rich-domain';\n\n// Entities and VO that encapsulate context.\ninterface Props {\n    id?: UID;\n    payment: Payment;\n    items: List\u003cItem\u003e;\n    status: OrderStatus;\n    customer: Customer;\n}\n\n// Simple example of an order aggregate encapsulating entities and \n// value objects for context.\nexport default class Order extends Aggregate\u003cProps\u003e {\n\n    // Private constructor to ensure instances creation through static methods.\n    private constructor(props: Props){\n        super(props);\n    }\n\n    // Static method to begin a new order. \n    // Takes a customer as parameter and returns an instance of Order.\n    public static begin(customer: Customer): Order {\n        // Initialize the status of the order as \"begin\".\n        const status = OrderStatus.begin();\n        // Initialize the list of items as empty.\n        const items: List\u003cItem\u003e = List.empty();\n        // Initialize the payment as zero, since the order hasn't been paid yet.\n        const payment = Payment.none();\n        // Create a new instance of Order with the provided parameters.\n        const order = new Order({ status, payment, items, customer });\n\n        // Add an event to indicate that the order has begun.\n        order.addEvent('ORDER_HAS_BEGUN', (order) =\u003e {\n        // Perform some important operation when the order begins.\n            console.log('Do something important...');\n        });\n\n        // Alternatively, add an event by creating an\n        // instance of a class that extends EventHandler.\n        order.addEvent(new OrderBeganEventHandler());\n\n        // Return the created order instance.\n        return order;\n    }\n\n    // Method to add an item to the order. \n    // Takes an item as parameter and returns the Order instance.\n    addItem(item: Item): Order {\n        // Add the item to the order's items list.\n        this.props.items.add(item);\n        // Sum item price to payment amount\n        this.props.payment.sum(item.price);\n        // Return the Order instance itself to allow chained calls.\n        return this;\n    }\n\n    // Method to perform the payment of the order. \n    // Takes a payment object as parameter.\n    pay(payment: Payment): Order {\n        // Set the status of the order to \"paid\".\n        this.props.status = OrderStatus.paid();\n        // Set the provided payment object.\n        this.props.payment = payment;\n        // Add an event to indicate that the order has been paid.\n        // Assuming OrderPaidEvent is a class representing \n        // the event of order payment.\n        this.addEvent(new OrderPaidEventHandler());\n        return this; \n    }\n\n    // Static method to create an instance of Order.\n    // Returns a Result, which can be Ok (success) or Fail (failure).\n    // The value of the Result is an instance of Order, \n    // if creation is successful.\n    public static create(props: Props): Result\u003cOrder\u003e {\n        return Ok(new Order(props));\n    }\n\n    public static init(props: Props): Order {\n        return new Order(props);\n    }\n}\n\n```\n\n#### How to use events\n\nEvent Handler\n\n```ts\n\nimport { Context, EventHandler } from 'rich-domain';\n\n\nclass OrderCreatedEvent extends EventHandler\u003cOrder\u003e {\n\n    constructor() {\n        super({ eventName: 'ORDER_CREATED' });\n    }\n\n    dispatch(order: Order): void {\n        // dispatch event to another context\n        order.context().dispatchEvent('CONTEXT:EVENT', order.toObject());\n    };\n}\n\n```\n\nAggregates domain events\n\n\n```ts\n\norder.addEvent('OTHER_EVENT', (...args) =\u003e {\n    console.log(args);\n});\n\n// Or add an EventHandler instance\norder.addEvent(new OrderCreatedEvent());\n\norder.dispatchEvent('ORDER_HAS_BEGUN');\n\n// dispatch with args\norder.dispatchEvent('OTHER_EVENT', { info: 'custom_args' });\n\n// OR call all added events\nawait order.dispatchAll();\n\n\n```\n\n#### How to subscribe to a global event\n\n```ts\n\nimport { Context } from 'rich-domain';\n\nconst context = Context.events();\n\ncontext.subscribe('CONTEXT:EVENT', (event) =\u003e {\n   const [model] = event.detail;\n   console.log(model);\n});\n\n// dispatch an event to a context with args\ncontext.dispatchEvent('CONTEXT:EVENT', { name: 'Jane' });\n\n\n// Dispatching events to specific contexts\n// Dispatches the SIGNUP event to Context-X\ncontext.dispatchEvent('Context-X:SIGNUP'); \n\n// Dispatches the SIGNUP event to all contexts\ncontext.dispatchEvent('*:SIGNUP'); \n\n// Dispatches all events to all contexts. Not recommended\ncontext.dispatchEvent('*:*'); \n\n// Dispatches all events under Context-Y\ncontext.dispatchEvent('Context-Y:*'); \n\n``` \n\n---\n\n## Features\n\n### Result\n\nWhat is Result:\n\n`Result` is a class that encapsulates the result of an operation and stores the success or failure state without throws the application.\n\n#### Interface and Generic Types\n\n- P = `Payload` optional default `void`\n- E = `Error` optional default `string`\n- M = `MetaData` optional default `{}`\n\n```ts\n\nResult\u003cP, E, M\u003e;\n\n```\n\nYou can import like example below\n\n```ts\n\nimport { Result, Ok, Fail } from \"rich-domain\";\n\n// Success use case\n\nreturn Result.Ok();\n\n// OR\n\nreturn Ok();\n\n// OR\n\nreturn Ok(data);\n\n// OR\n\nreturn Ok\u003cPayload\u003e(data);\n\n// Failure use case\n\nreturn Result.fail('error message here');\n\n// OR\n\nreturn Fail('error message here');\n\n// OR\n\nreturn Fail\u003cMyError\u003e(myCustomError);\n\n\n```\n\nExample how to use generic types.\nFirst let's create our interfaces to use as generic type.\n- The type of data to be retrieved can be any type you want.\n\n\n```tS\n\n// Payload type\ninterface Data { data: string };\n\n// Error type\ninterface Err { message: string };\n\n// MetaData type. Optional\ninterface Meta { arg: number };\n\n```\n\nNow let's implement a function that return the result below\n\n```ts\n\nResult\u003cData, Err, Meta\u003e;\n\n```\nSo let's implement that on a simple function.\n\n```ts\n\nconst isEven = (value: number): Result\u003cData | null, Err, Meta\u003e =\u003e {\n\n\tconst isEvenValue = value % 2 === 0;\n\tconst metaData: Meta = { arg: value };\n\t\n\tif (isEvenValue) {\n\t\t\n\t\t// success payload \n\t\tconst payload: Data = { data: `${value} is even` };\n\n\t\t// return success\n\t\treturn Ok(payload, metaData);\n\t}\n\n\t// failure payload \n\tconst error: Err = { message: `${value} is not even` };\n\n\t// return failure\n\treturn Fail(error, metaData);\n};\n\n\n```\nHere we have a function that returns success if the value informed is even and returns failure if it is odd.\n\nSuccess Case\n\n```ts\n\nconst result = isEven(42);\n\nconsole.log(result.isOk());\n\n\u003e true\n\nconsole.log(result.value());\n\n\u003e 'Object { data: \"42 is even\" }'\n\nconsole.log(result.metaData());\n\n\u003e 'Object { arg: 42 }'\n\nconsole.log(result.error());\n\n\u003e null\n\n```\nFailure Case\n\n```ts\n\nconst result = isEven(43);\n\nconsole.log(result.isFail());\n\n\u003e true\n\nconsole.log(result.error());\n\n\u003e 'Object { message: \"43 is not even\" }'\n\nconsole.log(result.metaData());\n\n\u003e 'Object { arg: 43 }'\n\nconsole.log(result.value());\n\n\u003e null\n\n```\n\n#### Void\n\nThe most simple void success example.\u003cbr\u003e\nLet's see the same example using void.\n\n```ts\n\nconst checkEven = (value: number): Result\u003cvoid | null\u003e =\u003e {\n\n\tconst isEven = value % 2 === 0;\n\n\t// success case\n\tif(isEven) return Ok(); \n\t\n\t// failure case\n\treturn Fail('not even');\n}\n\n```\nUsing the function as success example\n\n```ts\n\nconst result: Result\u003cvoid\u003e = checkEven(42);\n\nconsole.log(result.isOk());\n\n\u003e true\n\nconsole.log(result.isFail());\n\n\u003e false\n\nconsole.log(result.error());\n\n\u003e null\n\nconsole.log(result.value());\n\n\u003e null\n\nconsole.log(result.metaData());\n\n\u003e 'Object {}'\n\n```\n\nFail example\n\n```ts\n\nconst result: Result\u003cvoid\u003e = checkEven(43);\n\nconsole.log(result.isFail());\n\n\u003e true\n\nconsole.log(result.isOk());\n\n\u003e false\n\nconsole.log(result.error());\n\n\u003e \"not even\"\n\nconsole.log(result.value());\n\n\u003e null\n\nconsole.log(result.metaData());\n\n\u003e 'Object {}'\n\n```\n\n#### toObject method\nyou can get a summarized object with the properties of an instance of a `Result`\n\n```ts\n\nconsole.log(result.toObject());\n\n\u003e Object\n`{\n\t\"data\": null, \n\t\"error\": \"not even\", \n\t\"isFail\": true, \n\t\"isOk\": false, \n\t\"metaData\": Object {}\n }`\n\n```\n\n#### Hooks\n\nIn the instances of a Result there are two hooks that allow the execution of a command according to the state.\n\n```ts\n\nclass Command implements ICommand\u003cvoid, void\u003e {\n\texecute(): void {\n\t\tconsole.log(\"running command ...\");\n\t}\n}\n\nconst myCommand = new Command();\n\nconst result = Ok();\n\nresult.execute(myCommand).on('Ok');\n\n\u003e \"running command ...\"\n\n```\n\nYou might also want to pass arguments to the command\n\n```ts\n\nclass Command implements ICommand\u003cstring, void\u003e {\n\texecute(error: string): void {\n\t\tconsole.log(error);\n\t}\n}\n\nconst myCommand = new Command();\n\nconst result = Fail('something went wrong');\n\nresult.execute(myCommand).withData(result.error()).on('fail');\n\n\u003e \"something went wrong\"\n\n```\n\n#### Combine\n\nYou can use the static `combine` function of `Result` to check many instances if any are failed it will return the instance with error state.\n\nSuccess example \n\n```ts\n\nimport { Ok, Combine } from \"rich-domain\";\n\nconst resultA = Ok();\nconst resultB = Ok();\nconst resultC = Ok();\n\nconst result = Combine([ resultA, resultB, resultC ]);\n\nconsole.log(result.isOk());\n\n\u003e true\n\n// OR \n\nimport { Result } from \"rich-domain\";\n\nconst resultA = Result.Ok();\nconst resultB = Result.Ok();\nconst resultC = Result.Ok();\n\nconst result = Result.combine([ resultA, resultB, resultC ]);\n\nconsole.log(result.isOk());\n\n\u003e true\n\n```\nFailure example \n\n```ts\n\nconst resultA = Ok();\nconst resultB = Fail('oops err');\nconst resultC = Ok();\n\nconst result = Combine([ resultA, resultB, resultC ]);\n\nconsole.log(result.isOk());\n\n\u003e false\n\nconsole.log(result.error());\n\n\u003e 'oops err'\n\n```\n\n---\n\n\n### ID\n\nWhat is ID:\nA symbol which uniquely identifies an object or record.\u003cbr\u003e\nIn this Lib all IDs are generated by domain and uses uuid v4.\n\n#### Create New\nCreate a new uuid.\n\n```ts\n\nimport { ID, Id } from \"rich-domain\";\n\nconst id = ID.create();\n\nconsole.log(id.value());\n\n\u003e \"eb9c563c-719d-4872-b303-0a82921351f7\"\n\n// OR as function\n\nconst id = Id();\n\n```\n\n#### Short New\nCreate a short id\n\n```ts\n\nconst id = ID.short();\n\nconsole.log(id.value());\n\n\u003e \"EB9C563DB4872BF7\"\n\n```\n\n#### Create Existing\nCreate a id with provided value\n\n```ts\n\nconst id = ID.create('this-is-my-id-01');\n\nconsole.log(id.value());\n\n\u003e \"this-is-my-id-01\"\n\n// OR \n\nconst id = Id('this-is-my-id-01');\n\n```\n\n#### Compare ids\nThe id instance has a method to compare two ids.\n\n```ts\n\nconst idA = ID.short('id-01');\nconst idB = ID.short('id-02');\n\nconsole.log(idA.equal(idB));\n\n\u003e false\n\nconsole.log(idB.equal(idB));\n\n\u003e true\n\n```\n\n#### IsNew\nCheck if id instance is has a new value\n\n```ts\n\nconst idA = ID.create('this-is-my-id-01');\nconst idB = ID.create();\n\nconsole.log(idA.isNew());\n\n\u003e false\n\nconsole.log(idB.isNew());\n\n\u003e true\n\n// OR\n\nconst idA = Id('my-custom-id-01');\n\nconsole.log(idA.isNew());\n\n\u003e false\n\nconst idB = Id();\n\nconsole.log(idB.isNew());\n\n\u003e true\n\n```\n\n#### Type for ID\nDefine type for ID\n\n```ts\n\nimport { UID, ID, Id } from 'rich-domain';\n\n// UID type\nlet id: UID;\n\n// ID value\nid = ID.create();\n\n// OR\n\nid = Id();\n\n```\n\n---\n\n### ValueObject\n\nWhat is value object:\n\n- Are only identified by their values, not by their ids (for example money is a value object as long as we are not tracking individual banknotes, if we need to track individual banknotes then it should be a banknote entity)\n- Can be used to measure or describe things (name, description, amount, height, date, time, range, address, etc.)\n- You can combine other value types that usually go together into a new value object type, like address (city, street, country, postal code) or ...range, or ...type\n- Prefer to put the behavior on value objects rather than on entities because value objects are immutable and do not have side effects (like changing their state or changing the state of any entity)\n- Can be part of an entity\n- Should be immutable, behaviors should not change the state of a value object, but can rather create a new value object (should act similar to C# strings, structs, ints, and other value types)\n- Can be persisted but only as part of an entity, not individually.\n\n\n#### Simple Value Object.\n\nValue objects extend to `ValueObject` class have private constructor and public static method called `create`.\u003cbr\u003e\nThe `create` method receives the props which by default is an object with the key `value`.\n\nthe value object below is a base example without any kind of validation\n\n```ts\n\nimport { Result, ValueObject } from \"rich-domain\";\n\nexport interface NameProps {\n\tvalue: string;\n}\n\nexport class Name extends ValueObject\u003cNameProps\u003e{\n\tprivate constructor(props: NameProps) {\n\t\tsuper(props);\n\t}\n\n    public static init(value: string): Name {\n        return new Name(value);\n    }\n\n\tpublic static create(value: string): Result\u003cName\u003e {\n\t\treturn Result.Ok(new Name({ value }));\n\t}\n}\n\nexport default Name;\n\n```\n\nNow that we have defined our value object class, we can create an instance.\u003cbr\u003e\nThe `create` method returns an instance of Name encapsulated by the `Result`, so it is important to always assess whether the result is a success before getting the value.\n\n```ts\n\nconst result = Name.create('Jane');\n\nconsole.log(result.isOk());\n\n\u003e true\n\nconst name = result.value();\n\nconsole.log(name.get('value'));\n\n\u003e \"Jane\"\n\n```\n\nOnce we have an instance of a value object, we can use some methods that the library makes available.\n\nValue Objects has only `get` method because instance is immutable\n\n```ts\n\nconsole.log(name.get('value'));\n\n\u003e \"John\"\n\n```\n\n\u003e **We don't advise you to use state change of a value object. Create a new one instead of changing its state. However the library will leave that up to you to decide.**\n\nTo disable the setters of a value object use the parameters below in the super.\u003cbr\u003e\nThis property disables the set function of the value object.\n\n```ts\n\nconstructor(props: NameProps){\n\tsuper(props, { disableGetters: true })\n}\n\n```\n\nNow when trying to get the value using `get` throws an error.\n\n```ts\n\nconsole.log(name.get('value'));\n\n\u003e Error\n\n```\n\n#### Using validation\n\nValidation before create instance.\u003cbr\u003e\nA validator instance is available in the \"Value Object\" domain class.\n\n```ts\n\nimport { Result, Ok, Fail, ValueObject } from \"rich-domain\";\n\nexport class Name extends ValueObject\u003cstring\u003e{\n\tprivate constructor(props: string) {\n\t\tsuper(props);\n\t}\n\n\tpublic static isValid(value: string): boolean {\n\t\tconst { string: Check } = this.validator;\n\t\treturn Check(value).hasLengthBetween(3, 30);\n\t}\n\n    public static init(value: string): Name {\n        const isValid = this.isValid(value);\n        if(!isValid) throw new Error('invalid name');\n        return new Name(value);\n    }\n\n\tpublic static create(value: string): Result\u003cName | null\u003e {\n\t\tif (!this.isValid(value)) return Fail('invalid name');\n\t\treturn Ok(new Name(value));\n\t}\n}\n\nexport default Name;\n\n```\n\nNow when you try to instantiate a name, the value will be checked and if it doesn't meet the validation requirements, a `Result` will be returned with an error state.\n\n```ts\n\nconst empty = '';\n\nconst result = Name.create(empty);\n\nconsole.log(result.isFail());\n\n\u003e true\n\nconsole.log(result.error());\n\n\u003e \"invalid name\"\n\nconsole.log(result.value());\n\n\u003e null\n\n```\n\nAlternatively you can init a new instance and receive a throw if provide invalid value.\n\n```ts\n\nconst name = Name.init('Jane');\n\nconsole.log(name.get('value'));\n\n\u003e \"Jane\"\n\nconst other = Name.init('');\n\n\u003e \"throw error: invalid name\"\n\n```\n\n#### toObject\nThis method transforms a complex object into a simple object or value.\u003cbr\u003e\nThis method is useful for cases where you have value objects inside other value objects\n\n```ts\n\nconst street = Street.create('Dom Juan').value() as Street;\n\nconst complement = Complement.create('n42').value() as Complement;\n\nconst result = Address.create({ street, complement });\n\nconst address = result.value();\n\nconsole.log(address.toObject());\n\n\u003e Object \n`{\n\t\"street\": \"Dom Juan\", \n\t\"complement\": n42,\n }`\n\n```\n\n#### Clone\nThis method creates a new instance with the same properties as the current value object.\n\n```ts\n\nconst result = Name.create('Sammy') as Name;\n\nconst originalName = result.value();\n\nconsole.log(originalName.value());\n\n\u003e \"Sammy\"\n\nconst clone = originalName.clone();\n\nconsole.log(clone);\n\n\u003e \"Sammy\"\n\n```\n\nClone being a new instance does not change the properties of the original value object\n\n```ts\n\nclonedName.change('value', 'Jones');\n\nconsole.log(clonedName.value());\n\n\u003e \"Jones\"\n\nconsole.log(originalName.value());\n\n\u003e \"Sammy\"\n\n```\n\n#### createMany\n\nSometimes you will need to create many instances of different value objects and for that there is static method available `createMany` on value objects, entity and aggregate.\n\n```ts\n\nconst itemPrice = Class\u003cPriceProps\u003e(ProductPrice, { value: price });\nconst itemName = Class\u003cNameProps\u003e(ProductName, { value: name });\nconst itemQtd = Class\u003cQtdProps\u003e(ProductQtd, { value: qtd });\n\nconst { data, result } = ValueObject.createMany([ itemPrice, itemName, itemQtd ]);\n\n// you check if all value objects are ok\nif (result.isFail()) return Result.fail(result.error());\n\n// you can get instances from iterator data. In the same order as the array\nconst price = data.next().value() as ProductPrice;  // index 0\nconst name = data.next().value() as ProductName;    // index 1\nconst quantity = data.next().value() as ProductQtd; // index 2\n\nconst product = Product.create({ name, price, quantity });\n\n```\n\n---\n\n### Entity\n\nWhat is value object:\n\n- Live longer than the application, should endure restarts, and are persisted and read from data sources (DB, file system, network, etc.)\n- Have an id (preferably a GUID rather than a DB generated int because business transactions do not rely on persistence, can be persisted after other operations carried out in model's behavior)\n- Have entity semantics (equality and `GetHashCode()` defined by class name + id)\n- Behavior in an entity mostly orchestrates value objects for a use case\n- Entity class should not have public property setters, setting a property should be a behavior method\n- Entities should not have bidirectional relations (depending on the bounded context, either an egg can have a chicken or a chicken can have eggs, but not both)\n- Entity relations should not reflect the complete set of DB foreign key relationships, should be bare down to the minimum for performing the behavior inside the bounded context\n- Entity relations should not hold a reference to another entity class, it can only keep the id of another entity\n- If a business transaction needs a reference to other entities in relation, aggregates should be used instead (aggregates can hold a reference to other aggregate roots, which are entity classes by definition)\n\n#### Simple Entity\n\nEntities extend to `Entity` class, have private constructor and public static method called `create`.\nThe `create` method receives the props which by default is an object with the key `id`.\n\nthe entity below is a base example without any kind of validation\n\n```ts\n\ninterface UserProps { id?: UID, name: Name, age: Age };\n\nexport class User extends Entity\u003cUserProps\u003e{\n\tprivate constructor(props: UserProps){\n\t\tsuper(props)\n\t}\n\n\tpublic static create(props: UserProps): Result\u003cUser\u003e {\n\t\treturn Result.Ok(new User(props));\n\t}\n}\n\nexport default User;\n\n```\n\n`id` is a reserved word and must have the type `UID` or `string`.\n\nAll attributes for an entity must be value object except id.\n\n```ts\n\nconst nameAttr = Name.create('James');\nconst ageAttr = Age.create(21);\n\n// always check if value objects are success\nconst results = Combine([ nameAttr, ageAttr ]);\n\nconsole.log(results.isOk());\n\n\u003e true\n\nconst name = nameAttr.value();\n\nconst age = ageAttr.value();\n\n// if you don't provide a value for the id it will be generated automatically\nconst result = User.create({ name, age });\n\nconsole.log(result.isOk());\n\n\u003e true\n\n```\n\n#### toObject\n\nwhen you extend entity class you get some methods from domain class, one of them is `toObject` method.\u003cbr\u003e\nIn the entity, this method aims to transform a domain class into a simple object, that is, all value objects are transformed into simple attributes.\n\n```ts\n\nconst user = result.value();\n\nconsole.log(user.toObject());\n\n\u003e Object\n`{\n\tage: 21,\n\tname: \"James\",\n\tcreatedAt: \"2022-08-13T03:51:25.738Z\",\n\tupdatedAt: \"2022-08-13T03:51:25.738Z\"\n\tid: \"0709220f-7c2f-41e2-b535-151926286893\",\n }`\n\n```\n\n#### with id value\n\nyou can create an instance by entering an id\n\n```ts\n\nconst name = nameAttr.value();\n\nconst id = Id('my-id-value-01');\n\nconst result = User.create({ id, age, name });\n\nconsole.log(result.isOk());\n\n\u003e true \n\nconst user = result.value();\n\nconsole.log(user.toObject());\n\n\u003e Object\n`{\n\tage: 21,\n\tname: \"James\",\n\tid: \"my-id-value-01\",\n\tcreatedAt: \"2022-08-13T03:51:25.738Z\",\n\tupdatedAt: \"2022-08-13T03:51:25.738Z\"\n }`\n\n```\n\n#### isNew\n\nCheck if instance is a new entity.\u003cbr\u003e if you do not provide an id the entity will be considered as a new created entity instance.\n\n```ts\n\n// no id provided\nconst newUserResult = User.create({ name, age });\n\ncons newUser = newUserResult.value();\n\nconsole.log(newUser.isNew());\n\n\u003e true\n\n// id provided\nconst userResult = User.create({ id, name, age });\n\ncons user = userResult.value();\n\nconsole.log(user.isNew());\n\n\u003e false\n\n```\n\n#### isValidProps\n\nValidating props before create an instance.\u003cbr\u003e Here you can apply your business validation.\n\n```ts\n\npublic static isValidProps({ name, age }: UserProps): boolean {\n\t\n\t// your business validation \n\tconst isValidName = doSomeBusinessValidation(name);\n\tconst isValidAge = doSomeBusinessValidation(age);\n\n\treturn isValidName \u0026\u0026 isValidAge;\n}\n\n```\n\nLet's apply our props validation method to our entity class\n\n```ts\n\ninterface UserProps { id?: UID, name: Name, age: Age };\n\nexport class User extends Entity\u003cUserProps\u003e{\n\tprivate constructor(props: UserProps){\n\t\tsuper(props)\n\t}\n\n\tpublic static isValidProps({ name, age }: UserProps): boolean {\n\t\t// your business validation \n\t\tconst isValidName = doSomeBusinessValidation(name);\n\t\tconst isValidAge = doSomeBusinessValidation(age);\n\n\t\treturn isValidName \u0026\u0026 isValidAge;\n\t}\n\n\tpublic static create(props: UserProps): Result\u003cUser\u003e {\n\n\t\tconst isValidRules = this.isValidProps(props);\n\t\tif(!isValidRules) return Result.fail('invalid props');\n\n\t\treturn Result.Ok(new User(props));\n\t}\n}\n\nexport default User;\n\n```\n\n#### change\n\nin entities you can easily change an attribute with `change` or `set` method\n\n```ts\n\nconst result = Name.create('Larry');\n\nconst newName = result.value();\n\nconst changed = user.change(\"name\", newName);\n\nconsole.log(user.get(\"name\").value());\n\n\u003e \"Larry\"\n\nconsole.log(changed);\n\n\u003e true\n\n```\n\n#### Validation before change\n\nThe `isValidProps` Method validates properties when creating a new instance, but which method validates before modifying a value?\u003cbr\u003e\nFor this there is the method `validation`\n\nThe validation method takes two arguments, the first the `key` of props and the second the `value`.\nSo when calling the `set` or `change` function, this method will be called automatically to validate the value, if it doesn't pass the validation, the value is not changed.\n\n\u003e There must be a validation for each \"props\" key\n\n```ts\n\nvalidation\u003cKey extends keyof Props\u003e(value: Props[Key], key: Key): boolean {\n\n\tconst options: IPropsValidation\u003cProps\u003e = {\n\t\tname: (value: Name) =\u003e doSomeBusinessValidation(value),\n\t\tage: (value: Age) =\u003e doSomeBusinessValidation(value)\n\t} \n\n\treturn options[key](value);\n};\n\n```\n\nLet's apply our validation method to our entity.\u003cbr\u003e Now if the validation does not pass the value will not be changed.\n\n```ts\n\ninterface UserProps { id?: UID, name: Name, age: Age };\n\nexport class User extends Entity\u003cUserProps\u003e{\n\tprivate constructor(props: UserProps){\n\t\tsuper(props)\n\t}\n\n\tvalidation\u003cKey extends keyof Props\u003e(value: Props[Key], key: Key): boolean {\n\t\tconst options: IPropsValidation\u003cProps\u003e = {\n\t\t\tname: (value: Name) =\u003e doSomeBusinessValidation(value),\n\t\t\tage: (value: Age) =\u003e doSomeBusinessValidation(value)\n\t\t} \n\t\treturn options[key](value);\n\t};\n\n\tpublic static isValidProps({ name, age }: UserProps): boolean {\n\t\t// your business validation \n\t\tconst isValidName = doSomeBusinessValidation(name);\n\t\tconst isValidAge = doSomeBusinessValidation(age);\n\t\treturn isValidName \u0026\u0026 isValidAge;\n\t}\n\n\tpublic static create(props: UserProps): Result\u003cUser\u003e {\n\n\t\tconst isValidRules = User.isValidProps(props);\n\t\tif(!isValidRules) return Result.fail('invalid props');\n\n\t\treturn Result.Ok(new User(props));\n\t}\n}\n\nexport default User;\n\n```\n\n#### disableSetters\n\nTo disable the setters of an entity use the parameters below in the super.\u003cbr\u003e\nThis property disables the set function of the entity.\n\n```ts\n\nconstructor(props: NameProps){\n\tsuper(props, { disableSetters: true })\n}\n\n```\n\n#### clone entity\n\nyou can clone an entity and get a new instance\n\n```ts\n\nconst result = User.create({ id, age, name });\n\nconsole.log(result.isOk());\n\n\u003e true \n\nconst user = result.value();\n\n const clonedUser = user.clone();\n\n const newNameResult = Name.create('Luke');\n\n const newName = newNameResult.value();\n\n const changed = clonedUser.set('name').to(newName);\n\n console.log(user.get('name').value());\n\n \u003e \"James\"\n\n console.log(changed);\n\n \u003e true\n\n console.log(clonedUser.get('name').value());\n\n \u003e \"Luke\"\n\n```\n\n#### compare entities\n\nYou can compare two entities.\n\n`isEqual` just check props values and id value for both instances.\n\n```ts\n\nconst isEqual = user1.isEqual(user2);\n\nconsole.log(isEqual);\n\n\u003e false\n\n\n```\n\n#### history\n\nEach operation to change any entity state property generates a history.\u003cbr\u003e\nAt any time you can return to a previous state\n\n```ts\n\nconst result = User.create({ name, age });\n\nconst user = result.value();\n\nconsole.log(user.toObject());\n\n\u003e Object\n`{\n\tage: 21,\n\tname: \"James\",\n\tcreatedAt: \"2022-08-13T03:51:25.738Z\",\n\tupdatedAt: \"2022-08-13T03:51:25.738Z\",\n\tid: \"0709220f-7c2f-41e2-b535-151926286893\"\n }`\n ```\n\n---\n\n### Aggregate\n\nWhat is aggregate:\n\n- Encapsulate and are composed of entity classes and value objects that change together in a business transaction\n- Aggregates are a transactional graph of model objects\n- Aggregate root should be an entity, an aggregate can even be a single entity\n- Aggregate can keep a reference to other aggregate roots, but not to other entity classes which are not aggregate roots themselves\n- Aggregate should not keep a reference to other aggregate root entity classes if those other entities do not change together with this aggregate root entity\n- Aggregate can also keep the id of another entity, but keeping too many foreign key ids is a code smell (why?)\n- If deleting an entity has a cascade effect on the other entities referenced by class in the object graph, these entities are part of the same aggregate, if not, they should not be inside this aggregate\n\nThe aggregate has the same methods already mentioned in the entity.\nAnd in addition to the entity methods, there is another one that is responsible for managing the `domain's events`.\n\n#### Simple Aggregate\n\n```ts\n\nexport interface ProductProps {\n\tid?: UID;\n\tname: ProductName;\n\tprice: ProductPrice;\n\tcreatedAt?: Date;\n\tupdatedAt?: Date;\n}\n\n// extends to Aggregate\nexport class Product extends Aggregate\u003cProductProps\u003e{\n\tprivate constructor(props: ProductProps) {\n\t\tsuper(props);\n\t}\n\n\tpublic static create(props: ProductProps): Result\u003cProduct\u003e {\n\t\treturn Result.Ok(new Product(props));\n\t}\n}\n\nexport default Product;\n\n```\n\n#### Domain Event\n\nLet's create an aggregate instance and see how to add domain event to it.\nEvents are stored in memory and are deleted after being triggered.\n\n```ts\n\nconst result = Product.create({ name, price });\n\nconst product = result.value();\n\nproduct.addEvent('eventName', (product) =\u003e {\n\tconsole.log(product.toObject())\n});\n\n// or alternatively you can create a event handler\n\nclass Handler extends EventHandler\u003cProduct\u003e {\n    constructor() { super({ eventName: 'eventName' }) };\n\n    dispatch(product: Product): void {\n        const model = product.toObject();\n\t\tconsole.log(model);\n    }\n}\n\n// add instance to aggregate\nproduct.addEvent(new Handler());\n\n// dispatch from aggregate instance\n\nproduct.dispatchEvent(\"eventName\");\n\n```\n---\n### Adapter\n\nHow to adapt the data from persistence to domain or from domain to persistence.\n\n```ts\n\n// from domain to data layer\nclass MyAdapterToInfra implements Adapter\u003cDomainUser, DataUser\u003e{\n\tadaptOne(target: DomainUser): DataUser {\n\t\t// ...\n\t}\n}\n\n// from data layer to domain\nclass MyAdapterToDomain implements Adapter\u003cDataUser, DomainUser\u003e{\n\tadaptOne(target: DataUser): DomainUser {\n\t\t// ...\n\t}\n}\n\n// You can use adapter instance in toObject function\nconst myAdapter = new MyAdapterToInfra();\n\nconst dataUser = domainUser.toObject\u003cDataUser\u003e(myAdapter);\n\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4lessandrodev%2Frich-domain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F4lessandrodev%2Frich-domain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F4lessandrodev%2Frich-domain/lists"}