redwoodjs / redwood

The App Framework for Startups
https://redwoodjs.com
MIT License
17.31k stars 994 forks source link

[RFC]: Rename Services -> Resolvers and Decouple Resolvers from Business Logic #7616

Open MichaelrMentele opened 1 year ago

MichaelrMentele commented 1 year ago

Summary

Services are misnamed and overloaded

Let's call them resolvers and stop recommended people put biz logic inside them. It's an anti-pattern for multiple reasons happy to discuss at length.

Motivation

Code smell: information leakage

Calling resolvers services is confusing. At the end of the day, by default every export (by default) gets exported out of the services directory into the services field on createGQLHandler. GQL doesn't know about "services" it only knows how to resolve fields. Why are we leaking this concept?

There a basic bugs like the fact that every export is sent into services createGraphQLHandler which can result in nasty name clash bugs :(

Unnecessary Conceptual Coupling

TL;DR creates tech debt, doesn't scale

Services are a broad term that can refer to: (1) separately hosted "services", (2) packages that fulfill a service or handle a concern. Classically services are organized around domains or business logic. However, naming our resolvers services and then encouraging reusing them as a fundamental building block does not work at scale and leads to a host of issues.

Resolvers aka services map to your external API which should be as simple as possible. Your external API is not your internal API for a variety of reasons I hope are self-evident.

The apollo docs themselves recommend having a model and biz logic layer but in RW we mush that all together in our API or lead folks down that path by encouraging reusing services in other services and building packages around our services and calling them services.

This works fine when early on you have a stupidly simple API that mostly does CRUD which is the genesis of most apps. But there are good reasons there is distinct layers for models, domain logic, and api fulfillment in every major web framework including Rails, Django, etc.

Classic example is seeding data, you might have validation or side-effects like sending emails that should happen during a mutation that you do NOT want to happen AND you may have side-effects like creating a child relation that you DO want to happen. A service is clearly overloaded. What do I do when seeding data then? Copy what I need and not what I don't? Oh but wait, now I have an obscure dependency and we redesign the schema or move that side-effect from the API -- now it's no longer coupled for whatever reason but I've forgotten to update my seed script. Clearly, there is some effects that are tightly coupled to the model layer and other that are coupled to the business layer and other that are coupled to the API layer. The classic solution is -- have a model layer you can extend.

If you squint at GQL it really is an interface replacement and resolver isn't so different from a controller in classic MVC. Anyway, I've gone off the rails now. My point is if we are going to prescribe how biz logic is organized we should be more thoughtful about our layers.

Django has apps which vertically slice domain logic and bundle it with routing. Rails has classic MVC with mixins and "concerns". What does RW have?

Detailed proposal

Clarify Single Responsibilities

Write a Scalable Architecture Doc for Code Organization

Are you interested in working on this?

MichaelrMentele commented 1 year ago

This is an unedited rough draft to start discussion. Detailed plan needs development.

MichaelrMentele commented 1 year ago

We seriously need to change this for numerous reasons. This advice is at odds with guidance from the creators of GQL, Apollo, etc. Fair warning I will pick on this at the builder's conf.

MichaelrMentele commented 1 year ago

This reddit post is good https://www.reddit.com/r/graphql/comments/eovr0h/business_logic_layer/

Checkout graphql.org https://graphql.org/learn/thinking-in-graphs/#business-logic-layer

There is comments on the Apollo blog as well.