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

2023-11-23

说我有一个StudentService一种为学生增加课程的方法LessonService使用一种将学生添加到课程中的方法。在我的课程和学生解决程序中,我希望能够更新本课程学生关系。所以在我的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 and A.2上面是StudentService可以访问StudentRepositoryLessonService可以访问LessonRepository- 我相信这坚持了牢固的关注点分离。

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

虽然应尽可能避免循环依赖,但您 不能总是这样做。(这是其中之一吗?)

这似乎应该是使用 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);
}

So StudentModule and LessonModule现在完全独立了,同时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);
});

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

如果您使用的是反应式库(例如 RxJS),您应该使用Subject<AssignStudentsToLessonInput>解析器发布并订阅新引入的模块。

Update

正如OP所建议的,还有其他选择,例如将两个存储库注入到两个服务中。但是,如果每个模块都包含存储库和服务,即如果您导入LessonRepository and LessonService from LessonModule,这不起作用,因为您仍然具有模块级别的循环依赖关系。 但如果学生和课程之间确实有紧密的联系,你也可以将两个模块合并为一个模块,那就没有问题了。

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

我在单一解析器/服务解决方案中看到的一个优点是,它提供了用于将学生分配到课程的单一解决方案,而在事件解决方案中,studentService.assignLessonToStudents 和lessonService.assignStudentsToLesson 有效地执行完全相同的操作,因此尚不清楚应该使用哪一个。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

以正确的方式避免循环依赖 - NestJS 的相关文章

  • 如何在 Prisma 中指定可选查询过滤器?

    我正在使用 React Apollo 和 Prsima 构建一个应用程序 允许用户按型号 品牌 价格过滤汽车 我知道 例如 如何按品牌过滤汽车 const GET CARS gql query FilterCars brandId ID c
  • 获取类的公共属性而不创建它的实例?

    假设我们有一个 JavaScript 类 var Person function function Person name surname this name name this surname surname Person prototy
  • Apollo 服务器,Graphql - 必须提供查询字符串

    我不确定我在这里做错了什么 我现在已经被困了一段时间 让我的突变在无服务器设置中与我的 apollo server lambda 一起运行 当我尝试运行这样的查询时 我的查询工作正常 mutation signIn username Som
  • 重载签名、联合类型和“没有重载与此调用匹配”错误

    想象一个 TypeScript 4 2 2 函数接收一个string or a Promise
  • Typescript + Jquery Ajax + 这个

    我正在将一些 javascript 代码移植到 typescript 中 但遇到了一个问题 我有一个 ajax 调用 它将一个对象作为上下文传递 该对象包含一些回调和一些其他信息 这些信息由成功或错误回调读出 指示成功调用应重定向到的位置
  • 如何获取相对于根目录的文件?

    与正常node您可以设置 JavaScriptNODE PATH并需要本地模块 例如给出 project node modules src library index js 您可以设置NODE PATH src and require li
  • Typescript 从传递的函数返回类型推断返回类型

    我可能正在尝试实现不可能的目标 但事情就这样了 我想定义一个函数 function A 它将返回与传递给函数 A 的参数的新函数相同的类型 e g export function test
  • NestJS 中的 FileInterceptor 和 Body 问题(在请求中上传文件和数据)

    我有以下控制器 createCollection UploadedFile file Body createCollectionDto CreateCollectionDto GetUser user User Promise
  • 如何使用 Angular 5 在单击按钮时调用多个方法?

    我正在使用 Angular 5 并面临问题 我想提交 点击 事件并一一调用两个或多个方法 请给我想法或解决方案 以便我可以提交 点击 事件并调用两个或多个方法 such as html 文件
  • 如何为 Apollo 的 React HOC 定义 props 接口?

    我正在尝试使用 Apollo 的 React HOC 来获取数据并将其传递给我的组件 但出现以下错误 Argument of type typeof BrandList is not assignable to parameter of t
  • C++ OOP:将哪些函数放入类中?

    假设我有一个a类 class a public void load data private void check data void work data void analyze data 这些函数都对类或其成员之一执行某些操作 然而这个
  • TypeScript 中的“环境”是什么意思

    我不明白这个词是什么意思ambient在下面的句子中 不能在环境上下文中声明函数实现 我不确定是否理解这个词的一般含义 英语不是我的母语 如果这里有什么具体含义 我也不明白 我试图用我的母语来理解 但在这种情况下无法理解 这就像curren
  • 注入包含接口的所有已注册实现的 Enumerable

    给出以下接口 public interface IMyProcessor void Process 我希望能够注册多个实现 并让我的 DI 容器将它们的可枚举注入到这样的类中 public class MyProcessorLibrary
  • Angular 中的动态子组件

    我正在构建一个具有一致的元素列表设计模式的应用程序 如果我有一个 A 类型的对象 我会创建AComponent它接受a作为输入 然后创建另一个组件来迭代 A 列表 AListComponent 那么如果我有一个对象 B 我需要做同样的事情
  • Typescript 继承:扩展基类对象属性

    当扩展一个类时 我可以轻松地向它添加一些新属性 但是 如果当我扩展基类时 我想向基类的对象 简单对象的属性 添加新属性怎么办 这是一个带有一些代码的示例 基类 type HumanOptions alive boolean age numb
  • 为什么 TypeScript 在接口被重写时不发出警告

    interface ClockInterface setTime d Date class Clock implements ClockInterface I would expect this to raise a compile err
  • 使用验证器禁用 FormGroup

    I had formArray我的复选框checkboxForm 如果没有复选框 我需要禁用按钮提交checked 我在我的上实现了自定义验证器checkboxForm 这是我尝试过的 Ts file get formReceivedSum
  • `Account` 是 TypeScript 中的保留字吗?

    我很困惑 以下 TypeScript 代码无法编译并出现此错误 fails ts 10 7 error TS2420 Class Account incorrectly implements interface IAccount Prope
  • IDependencyResolver 是反模式吗?

    我正在对旧版 ASP NET 应用程序进行一些架构更改 我模拟了 ASP NET MVC 的 IDependencyResolver 为依赖解析创建了一些类的原型 我不会发布 因为它的界面几乎相同 但采用其他自然语言 我发现它可能被认为是服
  • F#:模式构成?

    我正在尝试编写一个由另外两个模式组成的模式 但我不确定如何去做 我的输入是字符串列表 文档 我有一个与文档标题匹配的模式和一个与文档正文匹配的模式 该模式应该匹配整个文档并返回标题和正文模式的结果 您可以使用以下命令一起运行两个模式 您在问

随机推荐

  • 提交前确认表单

    我正在使用一个简单的表单 我想允许用户在表单提交之前进行确认 我知道使用 jQuery 这会很容易 但我对代码有点困惑 function testform submit function submitbtn text confirm 我知道
  • 如何在 Angular 4 中使用 ngStyle 作为背景 url

    我有以下 html li div class w3l banner nav right banner1 style background url assets images 2 jpg no repeat 0px 0px h3 Make y
  • Python tf-idf:更新 tf-idf 矩阵的快速方法

    我有一个包含数千行文本的数据集 我的目标是计算 tfidf 分数 然后计算文档之间的余弦相似度 这就是我按照教程在 Python 中使用 gensim 所做的事情 dictionary corpora Dictionary dat corp
  • Spring Boot 是否可以通过 JAR 打包来提供 JSP?

    我熟悉 Spring BootJSP 示例应用程序 然而该示例使用了WAR包装 是否可以做同样的事情
  • 引用 UWP 和 ASP.NET 5 中的类库

    我正在尝试创建一个类库 其中包含 WebAPI 使用 ASP NET 5 和消费 UWP 应用程序的常见对象 主要是 DTO 但是 我还没有弄清楚如何创建类库 以便可以从其他项目中引用它 到目前为止我已经尝试过 首先 我尝试了一个类库 包
  • 使用 axios.get 时套接字挂起,但使用 https.get 时套接字不挂起

    据我所知 我正在使用两种不同的方法做同样的事情 const https require https const axios require axios let httpsAgent new https Agent rejectUnautho
  • 如何在命名空间中使用连字符?

    我在用https packagist org packages bitdevelopment yii2 validators Yii2 中的字数验证 但我得到 gt PHP Parse Error yii base ErrorExcepti
  • 如何排除`node_modules/@types/**/node_modules`?

    我遇到过一种情况 其中的类型定义node modules types正在安装自己的 types 依赖项 并且这些 嵌套 types 与我的顶级 types 冲突 types angular v1 5 angular ui bootstrap
  • Lucene - 短语中的通配符

    我目前正在尝试使用 Lucene 来搜索索引中填充的数据 我可以通过将其括在括号中来匹配确切的短语 即 处理文档 但无法让 Lucene 通过执行任何类型的 处理文档 来找到该短语 明显的区别是末尾的通配符 我目前正在尝试使用 Luke 来
  • Perl:使用变量传递正则表达式搜索和替换

    我有一个 Perl 脚本 它读取正则表达式搜索并替换 INI 文件中的值 在我尝试使用捕获变量 1 或 1 之前 这工作正常 这些被逐字替换为 1 或 1 有什么想法可以让这个捕获功能通过变量传递正则表达式位吗 示例代码 不使用 ini 文
  • 在 Renderscript 计算中将数组传递给 rsForEach

    据我所知 我发现 RenderScript 缺乏良好的文档 forEachRS 中的方法是对分配中的每个单独项目执行 root 我正在尝试为 Renderscript 制作一个进行图像处理的库 作为起点 我达到了这个很好的答案 但问题是模糊
  • NSURLSession 和后台流上传

    我在使用时遇到一些问题NSURLSession将照片从资源库上传到服务器 首先NSURLSession不支持流式上传 我在尝试使用它时遇到了异常 property nonatomic strong NSURLSession uploadSe
  • 如何使用密钥大小和明文长度计算 RSA 密文的大小?

    我有一些明文 我想使用 RSA PKCS V21 使用 PolarSSL 库 进行加密 问题是我需要在执行算法之前知道密文的大小 用于动态内存分配目的 我知道 RSA 密钥大小和明文长度 我还想知道输入明文长度的限制 任何想法 只需检查RS
  • Unicode 与实体框架

    我有一个带有 nvarchar 字段的表 MS SQL Server 2008 R2 对于测试 此代码运行良好 Update Screenshots set name N where id 230246 现在我创建了实体框架模型 我已将 U
  • Elisp 中的 CGI 编程?

    有人为 elisp 编写过 CGI 编程库吗 我快速拼凑了第一个脚本 然而 我只是一个长期的 emacs 用户 我从来没有真正编写过它 当我看到我可以在 emacs 而不是 bash 中编写脚本 script 时 我想我应该尝试一下 usr
  • 如果用户提交表单,则不要询问确认

    我使用以下 JavaScript 代码来警告用户 如果他尝试重定向到另一个页面而不提交表单 window onbeforeunload function return Are you sure that you want to leave
  • servicePrincipalName 应该具有什么值?

    我正在尝试在我的服务上设置客户端模拟 我需要为我的服务端点的 servicePrincipalName 设置一个值 我正在看这个MSDN 文章但还是不太明白 我的服务托管在我们称为 ServerName1 的服务器上的控制台应用程序中 乌里
  • 类型的打字稿扩展运算符[重复]

    这个问题在这里已经有答案了 我正在尝试定义一种类型 该类型获取函数类型作为泛型参数 并返回一个与输入函数类型相同的函数类型 只是它最后还有一个参数 type AugmentParam
  • java.lang.NoClassDefFoundError:无法初始化类 sun.nio.ch.FileChannelImpl

    我正在开发一个执行的应用程序Jython 2 5 3脚本来自JAVA 1 6 027 该脚本只是使用编解码器库打开一个文件 如下所示 try from codecs import open as codecs open except Imp
  • 以正确的方式避免循环依赖 - NestJS

    说我有一个StudentService一种为学生增加课程的方法LessonService使用一种将学生添加到课程中的方法 在我的课程和学生解决程序中 我希望能够更新本课程学生关系 所以在我的LessonResolver我有一些类似的事情 a