Skip to content

Instantly share code, notes, and snippets.

@idibidiart
Last active September 16, 2023 18:36
Show Gist options
  • Star 141 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save idibidiart/49a095b6bc528638f34f to your computer and use it in GitHub Desktop.
Save idibidiart/49a095b6bc528638f34f to your computer and use it in GitHub Desktop.
Building an Agile, Maintainable Architecture with GraphQL

Building a Maintainable, Agile Architecture for Realtime, Transactional Apps

A maintainable application architecture requires that the UI only contain the rendering logic and execute queries and mutations against the underlying data model on the server. A maintainable architecture must not contain any logic for composing "app state" on the client as that would necessarily embed business logic in the client. App state should be persisted to the database and the client projection of it should be composed in the mid tier, and refreshed as mutations occur on the server (and after network interruption) for a highly interactive, realtime UX.

With GraphQL we are able to define an easy-to-change application-level data schema on the server that captures the types and relationships in our data, and wiring it to data sources via resolvers that leverage our db's own query language (or data-oriented, uniform service APIs) to resolve client-specified "queries" and "mutations" against the schema.

We use GraphQL to dynamically derive a UI-specific projection of app state from server-side data sources to client that can be updated in real time (with help of Feathers)

With this approach, the developer's job becomes a much more pleasant and simpler task of building the application-level GraphQL schema, building UI components and declaratively specifying the required queries and mutations.

Client-centric architectures (like those based on client-side 'app state' stores) tend to compose "app state" on the client, and as such end up replicating business logic in the client. While those architectures enable things like 'optimistic UI' and 'offline first' apps they can exponentially complicate the work involved in maintaining and evolving the application.

GraphQL

Optimistic and Offline-First UI

This principles presented here are for realtime, transactional apps (think: ordering & delivery and 'hard realtime' apps like Uber) In the context of realtime apps, what does it mean to tell the user that their order was successful or the driver is 2 minutess away then finding out from server that the order cannot be fulfilled or the driver is stuck in traffic? This is to say that offline-first UI and optimistic UI are at odds with 'realtime' apps and present extra overhead of replicating app state and business logic in the client.

So then the question is wouldn't losing Optimistic UI hurt mobile performance? The fact is that GraphQL already does a lot to help mobile performance by allowing us to get exactly the data we need at any given instance as opposed to downloading more data than we need and putting it in the client side cache then having to keep all that data in sync with the server, which could be very challenging, especially if we compose app state on the client, which means that we have a lot of client derived state that depends on the cached data.

Some Challenges and Considerations

  1. Avoid business logic in the client: “app state” should not be composed and/or cached on the client as that would put redundant business logic in the client which adds a lot of unneeded complexity to the app. The client needs to be just an I/O layer, fetching/re-fetching UI-component-bound query results and making mutations to data on the server. In this pure, functional client model, local component state (including local state of higher order components) is used for ephemeral, client-only state such as animation state. We avoid composing app state on the client because that necessitates the embedding of business logic in the client, which complicates app building and maintenance. Instead, we opt to simply refetch UI-component-bound query results based on certain events like (backend mutation events, navigation state change or after network interruption) and let UI components derive their own local state from server-side app state which is persisted to the database and composed and projected to the client via GraphQL. The business logic stays on the server and the UI developer's job becomes very simple.

  2. The need for pre-mutation hooks: we need to be able to authorize the user before executing a mutation (and conditionally avoiding the mutation) so we can implement authorization on the server and leave business logic completely outside the client.

  3. The need for post-mutation hooks to make calls to external APIs (e.g. send email) after a given mutation.

  4. The need for live mutation events that perform well at scale: when something changes on the server, we need to know about it, either via polling or signaling. The latter has the advantage of being immediate.

  5. Currently, GraphQL developers need to know many different querying syntaxes for the various resolvers in the schema (MySQL, MongoDB, different APIs, etc), or write their own API adapters for each data store to decouple their resolvers from the underlying data store.

  6. We should be able to validate data based on our business logic, inside GraphQL resolvers

  7. We should be able to implement secure authentication using OAuth and Email/Password.

  8. We should be able to implement authorization independent of our database or network interface.

  9. We should be able to implement validation generically at the UI component level for instant feedback as user types

  10. We should be able to compose business logic in GraphQL resolver using microservices with a uniform data-oriented interface.

###References

  1. [Building a GraphQL Server with Node.js and SQL] (https://www.reindex.io/blog/building-a-graphql-server-with-node-js-and-sql/)

Repo coming soon...

###Update

@gaearon (Dan Abramov), author of Redux, was gracious enough to tweet this, and I give him a lot of credit for doing so.

Here is a section of the very long conversation that Dan started:

twitter conversation

###Update 3:

My follow up conversation with Dan Abrahmov below

twitter follow up

@alecmev
Copy link

alecmev commented Jun 24, 2016

Have you heard about Apollo client yet? It's in its infancy, but in a nutshell, it's like Relay, but built on top of Redux.

@idibidiart
Copy link
Author

Sorry for the very late response. Yes, I eventually decided to go with Apollo because of the community around it. Cc: @apollostack

@maximveksler
Copy link

@idibidiart me too, for the same reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment