Graphql-go: Question regarding resolver organization

Created on 23 Aug 2017  ยท  7Comments  ยท  Source: graph-gophers/graphql-go

For simplicity, I started off with all resolvers defined in the same package, resolver. Hundreds of resolvers have been added and the package has grown to many files.

I've found that resolvers often reference each other, making circular dependencies highly likely if we were to break resolver into type or concept-specific packages. It seems like the circular dependency problem _may_ be solvable by declaring interfaces, but the solution is not immediately clear to me.

Does anyone have a good resolver structure that they could share?

Most helpful comment

Like this?

โ”œโ”€โ”€ mutations
โ”‚ย ย  โ”œโ”€โ”€ base.go
โ”‚ย ย  โ”œโ”€โ”€ login.go
โ”‚ย ย  โ””โ”€โ”€ signup.go
โ”œโ”€โ”€ objects
โ”‚ย ย  โ””โ”€โ”€ user.go
โ”œโ”€โ”€ queries
โ”‚ย ย  โ”œโ”€โ”€ base.go
โ”‚ย ย  โ””โ”€โ”€ me.go
โ””โ”€โ”€ root.go

Only queries and mutations depend on objects.

mutations/base.go:

type MutationResolver struct {}

queries/base.go:

type QueryResolver struct {}

root.go:

type RootResolver struct {
        QueryResolver
        MutationResolver
}

mutations/login.go:

type LoginInput struct {}

type LoginPayload struct {}

func (r *MutationResolver) Login(args *struct{ Input LoginInput }) *LoginPayload {
    return &LoginPayload{}
}

func (r *LoginPayload) User() *objects.UserResolver {
    return &objects.UserResolver{}
}

All 7 comments

Same problem. Any Ideas??

Like this?

โ”œโ”€โ”€ mutations
โ”‚ย ย  โ”œโ”€โ”€ base.go
โ”‚ย ย  โ”œโ”€โ”€ login.go
โ”‚ย ย  โ””โ”€โ”€ signup.go
โ”œโ”€โ”€ objects
โ”‚ย ย  โ””โ”€โ”€ user.go
โ”œโ”€โ”€ queries
โ”‚ย ย  โ”œโ”€โ”€ base.go
โ”‚ย ย  โ””โ”€โ”€ me.go
โ””โ”€โ”€ root.go

Only queries and mutations depend on objects.

mutations/base.go:

type MutationResolver struct {}

queries/base.go:

type QueryResolver struct {}

root.go:

type RootResolver struct {
        QueryResolver
        MutationResolver
}

mutations/login.go:

type LoginInput struct {}

type LoginPayload struct {}

func (r *MutationResolver) Login(args *struct{ Input LoginInput }) *LoginPayload {
    return &LoginPayload{}
}

func (r *LoginPayload) User() *objects.UserResolver {
    return &objects.UserResolver{}
}

Thank you. But all resolvers have to be in objects directory. Currently we have around 30-40 resolvers, but it keeps increasing.

I'm thinking about using apollo server as proxy, it support schema stitching.
https://dev-blog.apollodata.com/graphql-tools-2-0-with-schema-stitching-8944064904a5

So each mircoservice will be a mini graphql server. Still looking for better ideas.

@sanae10001 that's a good idea. It seems to allow for separation between mutations and the object types.

But what happens when you have hundreds of interdependent types in the objects directory? It almost feels like a massive resolver package is unavoidable. Maybe that's okay?

We've started enforcing a 1:1 schema type to resolver file convention, which helps with discoverability when paired with an IDE with fuzzy file matching.

@tonyghita Sorry, I have no experience for the project of hundreds of objects . But I think a massive resolver package is unavoidable. Same issue in graphql-js.

Mybe you can try to separate business logic as mircoservice.

@sanae10001 I agree that all business logic should not be in resolvers at that scale. I _think_ a large package might be manageable as long as we are aggressive enforcing a separation of concerns.

Has anyone found a better solution to this?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

rogchap picture rogchap  ยท  5Comments

Melaninneal picture Melaninneal  ยท  5Comments

vladimiroff picture vladimiroff  ยท  6Comments

CreatCodeBuild picture CreatCodeBuild  ยท  4Comments

jakubdal picture jakubdal  ยท  6Comments