This blog post is all about: how developers get lured by shiny wrapping, what kind of problems can be solved by GraphQL, what are the basic ways to solve problems GraphQL in fact can't solve.
We had so many "silver bullets" in the software craftsmanship industry recently - some aimed to insta-boost the productivity, others to make the scalability-related problems evaporate like a bad dream - sadly, none of them truly made that difference, but we (as engineers) are still being naive enough to keep falling into very similar traps ...
One of the newest kid on this is named GraphQL. It's present for some time already, but until recently it lacked mature-enough implementations, so its presence was merely as a high-level concept, not something you can easily jump into. That has changed, partly thanks to libraries like Absinthe (hence cover image of this post).
Personally, I don't have anything against GraphQL as a concept, in fact I like it much more than REST which was IMHO treated too literally, thoughtlessly & ... religiously, so over the years it has caused (in general) more harm than good. But it annoys me that I've already encountered several people who've blindly preached the need of applying GraphQL as a solution to almost every kind of problem spotted ...
- any HTTP API performance issues -> move to GraphQL
- domain-related problems with API design -> move to GraphQL
- significant differences between various API usage scenarios (e.g. writes & reads) -> move to GraphQL
- struggling with documenting the API (!) -> move to GraphQL
Pardon me being blunt, but it seems that some want to apply GraphQL wherever they miss (can't build) a proper structure (/isolation /decomposition), just because GraphQL is so flexible.
They recklessly miss the most point (and it truly amazes me):
GraphQL is a query language for APIs. Nothing more than that.
It will NOT help you with decomposition in any other area than your API's contract. You'll have great flexibility in forming API calls, but you'll still have to map it (somehow) to your ORMs, aggregates, atomic services, active records or whatever crap you maintain "under the hood".
Which (according to myself) is the real essence of the problem here.
API is definitely how the external world (of service consumers) perceives your platform. In fact it can act as an (more or less intentional) "anti-corruption layer" & hide real issues (with composition, coupling, isolation, etc.) from the applications that do rely upon it.
If you're struggling with designing & maintaining your contract with service consumers (API definition) then the real issues are probably with:
- understanding of the domain (its capabilities)
- understanding of the consumer needs / consumer-specific scenarios
This can be helped with proper design discipline, client workshops, living documentation tools (even such simple ones like Swagger), API-level BDD style tests & many other techniques.
GraphQL is awesome (if applied properly), that's true. It's unmatched in certain scenarios, e.g.:
- navigating across very flexible, meta-models of numerous levels of detail
- operating on non-relational, hierarchical, flexibly structured object graphs
- when setting up a commercial API for unspecified consumers (B2B) who'll build you have no idea what upon that
But these scenarios are very specific and they strongly require certain architecture-related decisions up-front (e.g. regarding data persistence). Personally, I'm quite sure that even if you consciously apply these decisions up-front, you can make yourself (& your system) a lot of harm. Starting with just another "hop" in request processing pipeline, ending with dynamic combination of multiple, distributed queries/commands that can't be reliably optimised.
How come? GraphQL represents a very powerful, yet SIMPLE abstraction.
E.g. majority of data-mutating business domain scenarios are NOT about what GraphQL calls "mutations". Certain actions are happening (due to activities of actors - people or systems), they DO HAVE a certain scope (range & "transactionality"), they DO HAVE certain side effects (possibly in different bounded contexts), they DO chain & sometimes fail & roll-back after partial-failure. This is because such situations happen in real life (not everything is a simple "mutation"), so we need to model them into systems.
GraphQL is a very convenient abstraction that (like REST) is not flexible enough to cover all of them.
- it's very helpful for read-only, multi-LoD (Level-of-Detail) bounded contexts (e.g. certain read models in CQRS)
- it's also useful for some flexible "CRUD 2.0"-style "inventories" of flexible structure
- but for a typical LoB (Line-of-Business) application that supports certain processes (workflows), has API tuned for its own UI (instead of generic consumer), follows carefully crafted customer journeys, etc. - GraphQL's applicability is very limited
For me, people getting allured by GraphQL are yet another prove that we mature up very slowly as an industry.
In case of many new shining novelties before (like microservices or CQRS) all the controversies regarding their applicability were in fact much harder to clear out - all the discussing parties had some good arguments, the boundaries between reasonable & unreasonable scenarios were far less obvious. In case of GraphQL discussion seems much more unequivocal, but engineers still get so mesmerised with examples of its QL's brevity & "meatiness" that they jump straight away onto applying it w/o seriously considering its implications ...