What is the best layer to handle Entity-DTO conversion in my application?

I utilized TypeORM to establish two entities: User and School:

@Entity()
export class User {
  // ...

  @ManyToOne(() => School, school => school.id)
  school: School;

  // ...

  static from(
    uid: string,
    name: string,
    email: string,
    studentId: string,
    password: string,
    tel: string,
    role: string,
    school: School,
    changePasswordToken?: string
  ): User {
    const user = new User();
    user.uid = uid;
    user.name = name;
    user.email = email;
    user.studentId = studentId;
    user.password = password;
    user.tel = tel;
    user.role = role;
    user.school = school;
    user.changePasswordToken = changePasswordToken;
    return user;
  }
}
@Entity()
export class School {
  @PrimaryColumn()
  @OneToMany(() => Product, product => product.school)
  @OneToMany(() => Order, order => order.school)
  @OneToMany(() => User, user => user.school)
  id: string;

  // ...

  static from(
    id: string,
    name: string,
    legalName: string,
    address: string,
    tel: string,
    ceo: string,
    brn: string,
    mobrn: string,
    password: string
  ): School {
    const school = new School();
    school.id = id;
    school.name = name;
    school.legalName = legalName;
    school.address = address;
    school.tel = tel;
    school.ceo = ceo;
    school.brn = brn;
    school.mobrn = mobrn;
    school.password = password;
    return school;
  }
}

User relies on the id of the School through a foreign key named schoolId. In my research on similar topics on Stack Overflow, I discovered that implementing Entity-DTO conversion in the Service layer is advisable.

As a result, I crafted the following code for SchoolsService:

@Injectable()
export class SchoolsService {
  constructor(
    @InjectRepository(School) private readonly schoolRepository: Repository<School>
  ) { }
  async findOne(id: string): Promise<ResponseSchoolDto> {
    const school = await this.schoolRepository.findOne({ where: { id } });
    const responseSchoolDto = plainToInstance(ResponseSchoolDto, school);
    return responseSchoolDto
  }
}

Now let's examine the code for UsersService:

@Injectable
export class UsersService {
  constructor(private readonly schoolsService: SchoolsService) { }
  create(userData: CreateUserDto): Promise<User> {
    const user = instanceToPlain(userData);
    // WHAT SHOULD I DO?
    // const responseSchoolDto = this.schoolsService.findOne(userData.schoolId);
    // const school = plainToInstance(School, responseSchoolDto);
    return this.userRepository.save(user);
  }
}

As stated earlier, the DTO needs to be converted to Entity in order to provide a schoolId to the User Entity since the Service is designed to return DTOs. Nevertheless, I believe that the approach I employed is not suitable as UsersService relies on SchoolsService, School (Entity), and DTO. After much contemplation, the only resolution seems to be for the Service to return Entity.

During my quest for a solution to this dilemma, I encountered someone who implemented the practice of converting DTO to Entity within the DTO itself. However, I am hesitant to endorse this method as I believe DTOs should only contain pure data. Is there a more efficient structure to tackle this issue?

Answer №1

Although I may be arriving late to the conversation, I still wanted to share my input. Consider converting the ResponseSchoolDto school using plaintToInstance and setting the user's school value with recovery before saving the user. However, it is recommended not to inject one service into another. Instead, utilize a repository directly to retrieve a school and then assign it to the user.

Another approach is to implement Mappers. There are various solutions available:

Benefits of delegating the instruction to a separate Mapper:

  • Centralized mapping, making it more resistant to changes
  • Improved readability, resulting in lower understanding costs

Helpful Tips

  • Pay attention to naming conventions:
userData: UserData; // OK
userEntity: UserEntity // OK
user: User // OK
userData: CreateUserDto // NOT OK
etc...
  • Follow the principles of a well-layered REST architecture. Each layer should depend on the one below it but not on itself.
  • Consider creating intermediate classes (mapper, builder, manager, helpers, validators, etc.) to delegate actions. This approach leads to shorter and better-named functions that are easier to test and maintain.

For further discussions on the topic, check out: NestJS, how and where to build response DTOs

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

What's the best way to track changes in multiple form fields simultaneously in Angular?

Situation I have a form with 8 fields, but I want to monitor changes in just three of them to apply the same function. I don't want to set up individual subscriptions for each field like this: this.headerForm.get('start').valueChanges.subsc ...

Encountering a Login Issue with Firebase Google Authentication in Angular 16

I am currently working on implementing a Google sign-in/login feature in Angular 16 using Firebase. However, when I try to click the "LogIn" button, I encounter the following error: "ERROR Error: Uncaught (in promise): TypeError: Cannot read properties of ...

The use of dates (YYYY-MM-DD) as identifiers in HTML/Angular 2 is causing issues

I have successfully created a calendar using html in Angular 2, and now I am looking to incorporate events into it. These events should be able to color specific days, add a dot inside the cell, or something similar. I have a json file that contains the ev ...

Lazy loading in Angular allows you to navigate directly to a certain feature component

I'm currently working on implementing lazy loading in Angular 6, and I want to include links on my main homepage that direct users to specific feature components. Here is the hierarchy of my project: app.module.ts |__homepage.component.ts |__options ...

Can a number value in a JSON object be converted to a string?

In my application, I have defined an interface: export interface Channel { canal: string; name: number; status: string; temperature: number; setpoint: number; permission: boolean; percentOut: number; } [UPDATE] in the HTML file: <input ...

Angular threw an error stating that it encountered an unexpected token 'var' when trying to declare a variable x as

Currently facing a challenge with my Angular application development. I have created a TS File that interacts with my API (imported in the index.html using a script tag) and exported some functions from this file to be used in my components. Despite everyt ...

Filtering a key-value pair from an array of objects using Typescript

I am working with an array of objects containing elements such as position, name, and weight. const elements = [{ position: 3, name: "Lithium", weight: 6.941, ... },{ position: 5, name: "Boron", weight: 10.811, ... }, { position: 6, name: "Carbon", weight: ...

Limiting the data types of array elements applies to variables, not to indexes

type Soccer = { ball: string } type Basketball = { jump: string } type Data = Soccer[] | Basketball[] if ('ball' in data[index]) { // type guard not effective here. <MyComponent something={data[index]} /> // data: Soccer[] | Basketball[] ...

Using RadSideDrawer with Typescript in Vue class components: A Step-by-Step Guide

I am attempting to integrate external components into Vue Typescript Class Components. Following the installation of the standard template, I made modifications to its <script> block based on this guide: import { Vue, Component, Prop } from "vue-pro ...

Discovering routes in Angular2

I'm attempting to replicate something similar to this example here: AngularJS show div based on url/condition <span id="page-locator" *ngIf="location.path() == '/overview'">test</span> Unfortunately, it's not working as ex ...

Executing a Typescript function in an MVC5 cshtml file, optimized with webpack bundling

Brand new to webpack, and I'm facing obstacles in merging MVC components with webpack AND typescript. Take a look at my code combinations below: webpack.config.js: var wbConfigEntries = { "jqkendoMain": [ paths.appjs + "main.ts" ] }; ...

What is the best way to access variables within a function from an external function in Typecript?

On my HTML page, I am invoking a method with parameters using the onchange event. Once I retrieve the value, I trigger function2 on the click of another button. I am looking to access the variable from function in function2 within the same class. My implem ...

The assignment of Type Observable<Observable<any[]>> to Observable<any[]> is not valid

Working on implementing autocomplete using data from a database service: @Injectable() export class SchoolService { constructor(private db: AngularFirestore) { } getSchools(): Observable<School[]> { return this.db.collection<School> ...

react-hook-form replaces the onChange function causing delays in updating the value

Recently, I created a unique Select component utilizing useState and onChange. I attempted to integrate this custom component with the powerful react-hook-form. Allow me to share the code snippet for the bespoke Select component. const Select = forwardRef ...

Using NestJS Middleware to access the request body in the main.ts file

When receiving a request from the frontend, I usually include a log in the route handler as shown below: @Post('create') createPost( @Body() body: CreatePostDto, ){ console.log(body); // Received the body correctly } However, I realized that I ...

Is it possible to convert JSON to enum using TypeScript?

I have a JSON string that looks like the following. { "type": "A", "desc": "AAA" } or { "type": "B", "desc" "BBB" } etc. How can I utilize an enum in Ty ...

Tips for showcasing images stored in Azure Blob storage

Currently, I am developing an application that requires loading images from a web novel stored in Azure Storage Accounts as blobs. While I have enabled anonymous reads to show the image upon request successfully via a web browser or Postman, I am facing an ...

Retrieve the runtime configuration object or file using Jest

Is there a way to access the Jest runtime config object or file path? I wanted to utilize runtime config properties in my custom matchers. // ./jest.config.js const path = require("path"); module.exports = { prop1: "foo", prop2: "bar" }; // my-custo ...

Next.js is refusing to render an array of HTML elements

Consider this scenario where I have a block of code in TypeScript that attempts to create and display a list of elements. Here is a sample implementation: const MenuList = ():ReactElement => { const router = useRouter(), liElements:any = []; con ...

Unable to display toast notification in React/MUI/Typescript project

Currently tackling error notifications targeted at 400,401, and 500 errors within a large-scale project. I am facing an issue where I want to integrate my ErrorToastNotification component into my layout.tsx file to avoid duplicating it across multiple page ...