تایپاسکریپت - تبدیل Entity به DTO را در کدام لایه باید پیادهسازی کنم؟
من از TypeORM استفاده کردم تا یک موجودیت به نام User و 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;
}
}
کاربر به شناسه مدرسه از طریق کلید خارجی به نام schoolId وابسته است. در میان پاسخهای Stack Overflow مشابه این موضوع، متوجه شدم که پیادهسازی تبدیل Entity به DTO در لایه سرویس توصیه شده است.
پس من کد زیر را برای 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
}
}
کد برای 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);
}
}
همانطور که در بالا ذکر شد، DTO باید به Entity تبدیل شود تا یک schoolId به User Entity ارائه دهد، زیرا سرویس طوری طراحی شده است که DTO را بازگرداند. با این حال، فکر میکنم کدی که نوشتم نامناسب است چون UsersService به SchoolsService، School (Entity)، و DTO وابسته است. هرچقدر هم به آن فکر کنم، تنها راه حل این مشکل این است که سرویس Entity را بازگرداند.
در حالی که من به دنبال راه حلی برای این مشکل بودم، متوجه شدم که کسی روش تبدیل DTO به Entity را داخل خود DTO پیادهسازی کرده است. با این حال، فکر نمیکنم این روش هم مناسب باشد چون به نظر من DTO باید فقط داده خام داشته باشد. آیا ساختار موثری برای حل این مشکل وجود دارد؟
پاسخ شماره ۱
ممکن است به مهمانی دیر برسم، اما به هر حال دارم پست میکنم. شما میتوانید شیٔ ResponseSchoolDto مدرسه را با استفاده از plaintToInstance تبدیل کنید و مقدار مدرسه کاربر را با recovery تنظیم کنید. سپس کاربر خود را ذخیره میکنید. اما نباید یک سرویس را در سرویس دیگر تزریق کنید. از یک مخزن به طور مستقیم برای بازیابی مدرسه استفاده کنید و سپس آن را در کاربر تنظیم کنید.
راه دیگر ایجاد نگاشتکنندهها است. شما چند راهحل برای انجام این کار دارید:
- @automapper/nestjs
- تایپورت
- یک نگاشتگر قابل تزریق خود با یک تابع سفارشی پیادهسازی کنید
- یک سازنده ایجاد کنید
- ...
مزایای واگذاری آموزش به مپر جداگانه:
- نقشهبرداری متمرکز، بنابراین مقاومتر در برابر تغییرات
- خواناتر بنابراین هزینه درک پایینتر
نکات
- به اسامی توجه کنید:
userData: UserData; // OK
userEntity: UserEntity // OK
user: User // OK
userData: CreateUserDto // NON OK
etc...
- به معماری خوب لایهای REST احترام بگذارید. یک لایه به لایه دیگر وابسته است اما به خودش وابسته نیست.
- در ایجاد کلاسهای واسطهای (مبدل، سازنده، مدیر، کمککنندهها، اعتبارسنجها و غیره) برای واگذاری کارها تردید نکنید. به این ترتیب، توابع کوتاهتر، با نامهای بهتر، آسانتر برای تست و نگهداری خواهند بود.
بحث های بیشتر در مورد این موضوع: NestJS، نحوه و مکان ساخت DTO های پاسخ
