Issue
I used TypeORM to create an entity called 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
depends on the id of the School
through foreign key called schoolId
.
Among Stack Overflow's answers similar to this topic, I found that implementing Entity-DTO conversion is recommended in the Service layer.
So I wrote 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
}
}
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 mentioned above, DTO must be converted to Entity to provide a schoolId
to User Entity because Service is designed to return DTO.
However, I think the code I wrote is inappropriate because UsersService
depends on SchoolsService
, School
(Entity), and DTO. No matter how much I think about it, the only way to solve this problem is for Service to return Entity.
While I was looking for a solution to this problem, I found that someone implemented the method of converting DTO to Entity inside of DTO. However, I don't think this method is appropriate either because I think DTO should have only pure data. Is there an efficient structure to solve this problem?
Solution
I may be late to the party, But I'm posting anyway, you can convert the ResponseSchoolDto school with plaintToInstance and set the user's school value with recovery. Then you save your user. But you should not inject one service into another. Use a repository directly to retrieve a school and then set it on the user.
Another way is to create Mappers. You have several solutions to do this:
- @automapper/nestjs
- typevert
- implement your own Injectable Mapper with a custom function
- Create a builder
- ...
Advantages of delegating the instruction to a separate Mapper:
- centralized mapping, therefore more robust to changes
- more readable so a lower cost of understanding
Tips
- pay attention to names:
userData: UserData; // OK
userEntity: UserEntity // OK
user: User // OK
userData: CreateUserDto // NON OK
etc...
- respect the good layered REST architecture. One layer depends on another but does not depend on itself.
- Do not hesitate to create intermediate classes (mapper, builder, manager, helpers, validators, etc.) to delegate actions. This way, functions are shorter, better named, easier to test and maintain.
More discussions on the subject: NestJS, how and where to build response DTOs
Answered By - Ugo Evola
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.