Free AI web copilot to create summaries, insights and extended knowledge, download it at here
4376
Abstract
<span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> paymentService: PaymentService,
<span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> moderationService: ModerationService
</span>) {}
<span class="hljs-keyword">public</span> <span class="hljs-title function_">createPost</span>(<span class="hljs-attr">args</span>: <span class="hljs-title class_">CreatePostArgs</span>): <span class="hljs-title class_">Post</span> {
<span class="hljs-keyword">const</span> post = <span class="hljs-variable language_">this</span>.<span class="hljs-property">postRepository</span>.<span class="hljs-title function_">create</span>(args);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">reputationService</span>.<span class="hljs-title function_">increaseReputation</span>(args.<span class="hljs-property">userId</span>);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">notificationService</span>.<span class="hljs-title function_">notifyFollowersAboutPost</span>(post);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">userService</span>.<span class="hljs-title function_">updateUserActivity</span>(<span class="hljs-string">'post.created'</span>, post);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">trackingService</span>.<span class="hljs-title function_">registerTrackable</span>(<span class="hljs-string">'post'</span>, post);
<span class="hljs-variable language_">this</span>.<span class="hljs-property">moderationService</span>.<span class="hljs-title function_">checkPostContentForViolations</span>(post.<span class="hljs-property">content</span>);
<span class="hljs-keyword">if</span> (args.<span class="hljs-property">isPremium</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">paymentService</span>.<span class="hljs-title function_">chargeUserForPremiumPost</span>(args.<span class="hljs-property">userId</span>);
}
<span class="hljs-keyword">return</span> post;
}
}</pre></div><p id="154d">This example is obviously made up, so please don’t pay too much attention to the details. I just want you to notice how many services <code>PostService</code> is dependent on and imagine that these services may also be dependent on PostService. For example, the notification service may require additional data from the post service to dispatch notifications, or the <code>ModerationService</code> may need to modify the post again via <code>PostService</code> after moderation. This is where circular dependencies occur.</p><p id="96c0">So now that we have a grasp of the problem, let’s explore the solution that I want to propose.</p><h1 id="c471">The solution</h1><p id="72f0">The solution I want to propose for this problem is to use the well-known concept of Event-Driven Architecture. Instead of calling all subsequent actions from the <code>createPost</code> method, we will simply create the post there and emit an event. Then, any module interested in performing some action related to the event may do so without crossing its logical borders.</p><p id="53d2">NestJS already comes with handy tools that we can use to benefit from events. If you don't yet have the package installed, you can simply add <code>@nestjs/event-emitter</code> to your application.</p><div id="9629"><pre>yarn <span class="hljs-keyword">add</span> @nestjs/<span class="hljs-keyword">event</span>-emitter</pre></div><p id="32cf">And when the package is installed, add the <code>EventEmitterModule</code> to the root module of your application.</p><div id="e0ec"><pre><span class="hljs-keyword">import</span> { Module } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { EventEmitterModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/event-emitter'</span>;
<span class="hljs-meta">@Module(<span class="hljs-params">{
imports: [
EventEmitterModule.forRoot(<span class="hljs-params"></span>),
// ...
],
}</span>)</span>
export <span class="hljs-keyword">class</span> <span class="hljs-title class_">AppModule</span> {}</pre></div><p id="f073">When we have it ready, we may simply ge
Options
t rid of all <code>PostService</code> dependencies and replace them with only one — <code>EventEmitter2</code>. After this, we may also remove subsequent method invocations from the <code>createPost</code> method and instead emit an event with its key and payload. The payload may be defined as a separate interface or class if you wish, but here, for presentation purposes, I will just emit the created post as a payload.</p><div id="86c2"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">PostsService</span> {
<span class="hljs-title function_">constructor</span>(<span class="hljs-params"><span class="hljs-keyword">private</span> eventEmitter: EventEmitter2</span>) {}
<span class="hljs-keyword">public</span> <span class="hljs-title function_">createPost</span>(<span class="hljs-attr">args</span>: <span class="hljs-title class_">CreatePostArgs</span>): <span class="hljs-title class_">Post</span> {
<span class="hljs-keyword">const</span> post = <span class="hljs-variable language_">this</span>.<span class="hljs-property">db</span>.<span class="hljs-property">posts</span>.<span class="hljs-title function_">create</span>({ ... });
<span class="hljs-variable language_">this</span>.<span class="hljs-property">eventEmitter</span>.<span class="hljs-title function_">emit</span>(<span class="hljs-string">'post.created'</span>, post);
<span class="hljs-keyword">return</span> post;
}
}</pre></div><p id="a801">The last thing we have to do is to add a listener to the modules that may be interested in this event. For example, in the notification module, we may have a listener as below. In the other modules, the code will be pretty much the same, just other services will be involved in event handling.</p><div id="c0ad"><pre><span class="hljs-keyword">class</span> <span class="hljs-title class_">NotificationsListener</span> {
<span class="hljs-title function_">constructor</span>(<span class="hljs-params">
<span class="hljs-keyword">private</span> <span class="hljs-keyword">readonly</span> notificationsService: NotificationsService
</span>) {}
<span class="hljs-meta">@OnEvent</span>(<span class="hljs-string">'post.created'</span>)
<span class="hljs-title function_">handlePostCreated</span>(<span class="hljs-params">post: Post</span>) {
<span class="hljs-variable language_">this</span>.<span class="hljs-property">reputationService</span>.<span class="hljs-title function_">notifyFollowersAboutPost</span>(post);
}
}</pre></div><p id="9b3f">This way, the <code>NotificationService</code> and <code>PostService</code> are only loosely coupled now. The <code>PostService</code> is no longer dependent on <code>NotificationService</code>. We get rid of circular dependency here, yet we keep the functionality still working — Yay!</p><h1 id="ea7f">Summary</h1><p id="bf46">To sum up, in this article we explored the proposition of introducing events into your application to solve the issue of circular dependencies. This approach helps modules to stay within their borders yet react to actions performed in another module as well. Even though this way may not be applicable to all solutions it definitely may fix the one presented today.</p><p id="c9dc">I want to say thank you to all who read this article. I would love to hear your thoughts about this proposal and your ways of tackling circular dependencies in your applications, so feel free to share.</p><p id="913c">Don’t forget to check out my other articles for more tips and insights. Happy hacking!</p><h1 id="7cf6">Stackademic 🎓</h1><p id="d5c3">Thank you for reading until the end. Before you go:</p><ul><li>Please consider <b>clapping</b> and <b>following</b> the writer! 👏</li><li>Follow us <a href="https://twitter.com/stackademichq"><b>X</b></a><b> | <a href="https://www.linkedin.com/company/stackademic">LinkedIn</a> | <a href="https://www.youtube.com/c/stackademic">YouTube</a> | <a href="https://discord.gg/in-plain-english-709094664682340443">Discord</a></b></li><li>Visit our other platforms: <a href="https://plainenglish.io"><b>In Plain English</b></a><b> | <a href="https://cofeed.app/">CoFeed</a> | <a href="https://venturemagazine.net/">Venture</a> | <a href="https://blog.cubed.run">Cubed</a></b></li><li>More content at <a href="https://stackademic.com"><b>Stackademic.com</b></a></li></ul></article></body>