#Circular dependencies

6 messages · Page 1 of 1 (latest)

solar sand
#

Hi guys, how do you deal with circular dependencies with graphql? We just realized our backend will be "a bit" more complicated than we expected and a lot of services use other services that use the other services and so on. A lot of circular dependencies just spawned .e.g. users.service.ts uses posts.service.ts to display users.posts(first: Int, after: Int): PostConnection!, but for post we also want to include authors in query, where there can be a lot of authors because it's editable by everyone (it's just an example, it's not about posts) and there is a lot of circulars like this, is it wrong? Is there any other interesting approach of getting it work without using forwardRef() for 30th time a day?

obsidian crag
#

Other than forwardRef(), you can also refactor whatever functionality is causing the circular dependency into a third service, which then depends on the other two services. In your example, it could be user-post.service. Whereas, in that example, you might also question needing to call the users service directly to get posts for users. That should be a posts service activity. Right? 🙂

What one is building with all the dependencies is a hierarchical tree. First thing to think about is where do the two features fit in that tree. Which one is the parent to the other. Try to keep in mind which feature actually needs to depend on other features to get its work done. In theory, you should also try to keep the number of dependencies as minimal as possible. Lastly, if you find a service doing too much, like taking care of more than one kind of task, you should break the service apart.

granite charm
#

I've settled on the following structure:

  • PostsModule with PostsService, might use UsersModule for various reasons
  • UsersModule with UsersService, preferably doesn't import other domains than Auth, I think of it as a core module, and such core should be independent of the specifics of the system
  • GraphQLModule with PostsResolver, UsersResolver etc., importing almost all modules so PostsResolver can import UsersService to satisfy the post.author resolver, and UsersResolver importing PostsService to satisfy the user.posts resolver.

This way, I never had to use forwardRef. There is no circular dependency.

Sure it has two issues:

  • GraphQLModule is a beast. It imports 10s of modules and provides 10s of resolvers.
  • You can't just copy PostsModule directory and drop it to other project, since it doesn't include the PostsResolver.

I can live with these tradeoffs, since:

  • There will always be a module that stitches all the parts together.
  • I don't need to reuse my modules like this yet. If I do some day, I'll revisit the structure.
obsidian crag
#

@granite charm - The resolvers should be within the feature module they support. Not in GraphQL. 🙂

granite charm
obsidian crag
#

I guess if you don't care about code reuse and as long as future you remembers that all resolvers are to be found in the GraphQL module, you'll be ok. 😛