DDD and TypeScript: Implementing Value Object and Factory

In order to clarify the concept, I have created this aggregate and this value object. Please note that my value object currently does not have any validations implemented.

Aggregate:

export class Person extends Entity<{name: PersonName}> {
  private constructor(name: PersonName, id?: string) {
    super({name}, id)
  }
  public static build(
    name: PersonName, id: string
  ): Either<IErrorModel[], Person> {
    const person = new Person(name, id)
    return right(person)
  }
}

Value-Object:

export class PersonName extends ValueObject<IName> {

  public static maxLength: number = 30;
  public static minLength: number = 4;

  private constructor(props: IName) {
    super(props)
  }

  public get fullName(): string {
    return `${this.props.firstName} ${this.props.lastName}`
  }
    public get firstName(): string {
        return this.props.firstName
    }
    public get lastName(): string{
        return this.props.lastName
    }

    public static build(props: IName): Either<IErrorModel[], PersonName> {
        const name = new PersonName(props)
        return right(name)
    }

}

I am unsure about where to create my factory in my aggregate. For example, should I create and validate my value object "Name" within the factory of my aggregate, or should I do it elsewhere?

Below is an example of how it could be done in the aggregate factory:

export class Person extends Entity<{ name: PersonName }> {
  private constructor(name: PersonName, id?: string) {
    super({ name }, id)
  }
  public static build(
    { lastName, firstName }: { firstName: string; lastName: string },
    id: string,
  ): Either<IErrorModel[], Person> {
    let errors = [] as IErrorModel[]
    // other validations
    const name = PersonName.build({lastName,firstName})
    if(name.isLeft()) errors.push(...name.value)
    const person = new Person(name.value as PersonName, id)
    return right(person)
  }
}

Alternatively, it could also be done in the person service like so:

Aggregate Factory:

public static build(
    name: PersonName, id: string
  ): Either<IErrorModel[], Person> {
    const person = new Person(name, id)
    return right(person)
  }

Service:

export class PersonService{
  execute(req: any) {
    let errors = [] as IErrorModel[]
    const {lastName, firstName} = req
    const name = PersonName.build({lastName,firstName})
    if(name.isLeft()) errors.push(...name.value)
    const person = Person.build(name.value as PersonName, v4())
  }
}

My question remains: Should I create my value objects within the aggregate factory, or is there a better approach?

Answer №1

  public static foo( ... ): Either<IErrorModel[], Thing>

If I were to label this approach, I would call it Parser (refer to Parse, Don't Validate by Alexis King).

A parser usually relies on the domain model as a dependency but is not necessarily inherent to the domain model itself; its structure and purpose resemble that of a REPOSITORY.

The goal here is to steer clear, when feasible, from introducing non-domain-related elements into our domain model.

To illustrate this delineation further, let's consider a scenario where we have a thousand distinct input messages that must be parsed into a Person entity. In such cases, having a separate parser for each message directly within our domain model could lead to an overwhelming amount of parsing logic overshadowing other aspects. Instead, having specialized parsers closely tied to their respective input messages, rather than embedding them in the domain layer, offers two advantages: keeping the parsing logic isolated from the domain concerns, and facilitating easier tracking of each parser's usage and conditions required for its potential removal.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilizing Typescript with Mongoose Schemas

Currently, I am attempting to connect my Model with a mongoose schema using Typescript. Within my IUser interface: export interface IUser{ _id: string; _email: string; } I also have a User class: export class User implements IUser{ _id: string; ...

The type 'number' cannot be assigned to the type 'Element'

Currently, I am developing a custom hook called useArray in React with TypeScript. This hook handles array methods such as push, update, remove, etc. It works perfectly fine in JavaScript, but encounters errors in TypeScript. Below is the snippet of code f ...

Assign the private members of the class to the arguments of the constructor

class Bar { #one #two #three #four #five #six #seven #eight #nine #ten #eleven #twelve #thirteen #fourteen #fifteen #sixteen constructor( one, two, three, four, five, six, seven, eight, ...

Building a resolver to modify a DynamoDB item via AppSync using the AWS Cloud Development Kit (CDK)

After successfully creating a resolver to add an item in the table using the code provided below, I am now seeking assistance for replicating the same functionality for an update operation. const configSettingsDS = api.addDynamoDbDataSource('configSet ...

Problem with ngStyle: Error indicating that the expected '}' is missing

I encountered an error in the Chrome console when trying to interpret the code below: <div [ngStyle]="{'width': '100%'; 'height': '100%'; 'background-size': 'cover'; 'background-repeat&ap ...

Using *ngFor in TypeScript code

Hello there, I have a question about performing the logic of *ngFor in typescript. Is it possible to loop through data from my JSON API in the typescript file similar to how *ngFor works in HTML? Any guidance on this matter would be greatly appreciated. ...

How can we effectively test the document.save() function in NestJS using Mongoose?

My approach involves using some abstractions where the code receives a User, converts it to Mongo format (adding an underscore to the id generated elsewhere), saves it, and then returns the saved User without the underscore in the id: constructor( @I ...

Issue with Datepicker validation in Angular 5 and Angular Material

I have implemented the most recent version of Angular and Angular Material. I am facing an issue with a datepicker where the validation requirements are not being met as expected. The documentation states that the required attribute should work by default, ...

Webpack bundling, however, encountering issues with resolving TypeScript from the node_modules package

Hey everyone, I've been exploring various approaches to tackle this issue. We are working with two folders within a makeshift mono-repo structure (without using yarn workspace). One folder is named Mgt-Shared and the other is Server. We have set up a ...

Animations could not be processed as no elements were found

After implementing a service for my data, the animations on my page suddenly stopped working. It seems that the issue arises from the fact that when the page first loads, there are no elements in the ng-repeat due to the data being fetched. This situation ...

Displaying webpack errors within gulp is a simple process

My TypeScript project utilizes webpack for transpilation, and gulp for managing build tasks. Let's consider this simple task to illustrate my query: var webpack = require('webpack'); var webpackStream = require("webpack-stream"); gulp.task ...

Executing a dual ajax request in Angular 5

I am attempting to perform two HTTP requests consecutively, with the second request depending on the result of the first. However, it seems like I am overlooking something: getParkingSpots(date) { var gmt = this.getTimezone().subscribe(data=>{ if(d ...

Leveraging ts-jest in conjunction with create-react-app

Having trouble getting accurate coverage reports when running tests with the --coverage flag using create-react-app and react-scripts-ts for TypeScript. Is there a way to integrate ts-jest for correct coverage reports? Here's my jest configuration in ...

Tips for altering Koa's HTTP status code for undeclared paths

If an undefined route is accessed on a Koa server, what is the best method to change the default HTTP status code and response body? Currently, Koa returns a 404 status and 'Not Found' text in the body. I intend to modify this to 501 (Not implem ...

What is the best way to save objects in the store (ngrx, ngxs) while preserving their methods functionality?

As I delve into the Redux pattern, I realize the importance of storing only plain objects in the Store. However, I find myself wanting to use more complex objects with methods like "hasParent", "isReadonly", and "isValid" in my application. While ngrx all ...

Typescript Code Coverage with karma-jasmine and istanbul: A complete guide

I am attempting to calculate the Code Coverage for my typescript Code in karma framework using Istanbul. In the karma.conf file, typescript files are added and through karma typescript-preprocessor we are able to conduct unit testing and code coverage of t ...

When interacting with a <select> element, the behavior of test script execution varies between Firefox and Chrome

I've encountered an unusual problem that I need help describing and solving. Your assistance is greatly appreciated! The issue I'm facing involves Testcafe behaving differently when running the same test script on various browsers. testcafe: ...

In Typescript, it is not possible to invoke a function within a class using

Could you shed some light on why this code snippet is producing an error? It seems like splitval should be functioning correctly as a method. I've set up a stackblitz with the code here- don't forget to check the console for errors Here's w ...

Looking to personalize the MUI - datatable's toolbar and place the pagination at the top?

I successfully managed to hide the toolbar icon, but I am struggling with positioning pagination from bottom to top. Additionally, I am attempting to add two buttons (reset and apply) in the view-Column toolbar without any success in customizing the class. ...

The exported class's public property references a private name

After browsing through this specific question, I realized that the exporting method mentioned didn't quite help me with my problem. Here is a snippet of the code from cloudFoundry.ts: export var cf = require.__$__nodeRequire<any>('cf-clie ...