gpt4 book ai didi

typescript - 以正确的方式避免循环依赖 - NestJS

转载 作者:行者123 更新时间:2023-12-04 12:20:44 26 4
gpt4 key购买 nike

假设我有一个 StudentService使用一种为学生增加类(class)的方法和一个 LessonService使用一种将学生添加到类(class)中的方法。在我的类(class)和学生解析器中,我希望能够更新本类(class) <---> 学生关系。所以在我的 LessonResolver我有一些类似的东西:

  async assignStudentsToLesson(
@Args('assignStudentsToLessonInput')
assignStudentsToLesson: AssignStudentsToLessonInput,
) {
const { lessonId, studentIds } = assignStudentsToLesson;
await this.studentService.assignLessonToStudents(lessonId, studentIds); **** A.1 ****
return this.lessonService.assignStudentsToLesson(lessonId, studentIds); **** A.2 ****
}

而在我的 StudentResolver 中基本上是相反的

的区别A.1 A.2 上面是 StudentService可以访问 StudentRepositoryLessonService可以访问 LessonRepository - 我相信它坚持关注点的牢固分离。

但是, StudentModule 似乎是一种反模式。必须导入 LessonModuleLessonModule必须导入 StudentModule .这可以使用 forwardRef 修复方法,但在 NestJS Documentation它提到如果可能的话应该避免这种模式:

While circular dependencies should be avoided where possible, you can't always do so. (is this one of those cases?)



在使用 DI 时,这似乎应该是一个常见问题,但我正在努力获得关于哪些选项可以消除这种情况的明确答案,或者我是否偶然发现了一种不可避免的情况。

最终目标是让我能够编写以下两个 GraphQL 查询:
query {
students {
firstName
lessons {
name
}
}
}

query {
lessons {
name
students {
firstName
}
}
}

最佳答案

可能最简单的方法是完全删除依赖项,而是引入依赖于其他两个模块的第三个模块。
在您的情况下,您可以将两个解析器合并为一个 StudentLessonResolver存在于自己的模块中,比如 ResolverModule :

async assign({ lessonId, studentIds }: AssignStudentsToLessonInput) {
await this.studentService.assignLessonToStudents(lessonId, studentIds);
return this.lessonService.assignStudentsToLesson(lessonId, studentIds);
}

所以 StudentModuleLessonModule现在完全独立,而 ResolverModule取决于他们两个。没有循环了:)

如果由于某种原因您需要有两个解析器并让它们相互更新,您可以使用事件或回调来发布更改。
然后,您将再次引入第三个模块,该模块监听这些事件并更新另一个模块。

type AssignCallback = (assignStudentsToLesson: AssignStudentsToLessonInput) => Promise<void>;

class LessonResolver { // and similar for StudentResolver
private assignCallbacks: AssignCallback[] = [];

// ... dependencies, constructor etc.

onAssign(callback: AssignCallback) {
assignCallbacks.push(callback);
}

async assignStudentsToLesson(
@Args('assignStudentsToLessonInput')
assignStudentsToLesson: AssignStudentsToLessonInput,
) {
const { lessonId, studentIds } = assignStudentsToLesson;
await this.lessonService.assignStudentsToLesson(lessonId, studentIds); **** A.2 ****
for (const cb of assignCallbacks) {
await cb(assignStudentsToLesson);
}
}
}

// In another module
this.lessonResolver.onAssign(({ lessonId, studentIds }) => {
this.studentService.assignLessonToStudents(lessonId, studentIds);
});
this.studentResolver.onAssign(({ lessonId, studentIds }) => {
this.lessonService.assignStudentsToLesson(lessonId, studentIds);
});

同样,你打破了循环,因为 StudentModuleLessonModule彼此不了解,而您注册的回调保证调用任何一个解析器都会导致两个服务都被更新。

如果你使用的是响应式库,比如 RxJS,你应该使用 Subject<AssignStudentsToLessonInput> 而不是手动管理回调。解析器发布和新引入的模块订阅。

更新

正如 OP 所建议的那样,还有其他替代方案,例如将两个存储库注入(inject)到两个服务中。但是如果每个模块都包含存储库和服务,即如果您导入 LessonRepositoryLessonService来自 LessonModule ,这是行不通的,因为您仍然会在模块级别上有循环依赖。
但是如果学生和类(class)之间真的有紧密的联系,你也可以将两个模块合并为一个,也没有问题。

一个类似的选择是将第一个解决方案的单个解析器更改为直接使用存储库的服务。这是否是一个好的选择取决于管理商店的复杂性。从长远来看,您最好通过该服务。

我在单一解析器/服务解决方案中看到的一个优点是它提供了一个单一的解决方案来为学生分配类(class),而在事件解决方案中, studentService.assignLessonToStudents 和 courseService.assignStudentsToLesson 有效地做完全相同的事情,所以不清楚应该使用哪一个。

关于typescript - 以正确的方式避免循环依赖 - NestJS,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62054703/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com