转自:https://medium.com/expedia-group-tech/graphql-component-architecture-principles-homeaway-ede8a58d6fde

At Vrbo, we’ve been using GraphQL for over a year. But there are some differences in how we’ve implemented and used GraphQL compared to some examples we’ve seen in the wild.

Components of components

This is because in adopting GraphQL at scale, we wanted to ensure not only the success of individual teams, but the scalability and predictability of that success from team to team across Vrbo.

In addition, we’ve learned from past mistakes with other orchestration models and wanted our new model to work differently.

Previously, we relied on dedicated orchestration services. These proved to be not only an unnecessary moving part, but created dependencies between teams that slowed them down and created a wider blast radius when something went wrong.

Diagram indicating blast radius of a failure

High level goals

In developing a new front-tier API (orchestration, public APIs) architecture, we had a few goals in mind.

  1. Increase team velocity by reducing dependencies between teams and services
  2. Reduce blast radius by:
    * Insulating teams from change
    * Reducing points of failure
  3. Improve predictability of a team’s:
    * Compute needs
    * Cost expectations
  4. Simplify collaboration and sharing
  5. Improve portability across environments

Breaking up GraphQL schema into components

A GraphQL schema is made up of types (type schema), root types (API operations) and resolvers (business logic).

An example type:

GraphQL type example

This defines a new type Author, which is merely a type definition. To perform operations on Author, such as a query, we must define a root type:

GraphQL root type example

This defines a query operation to the API’s surface called author that clients can now interact with.

But we still need to execute some code for the query to do work and this is where resolvers come in. In javascript, a resolver might look something like this:

GraphQL resolver example

As you can imagine, a schema can grow quite large as our API surface area grows. This is in part why we break down and componentize these elements of a schema into independently versionable modules that can be imported and merged into an API based on the requirements of the use case.

An application or API can aggregate as many components as it needs to fulfill its UI or user needs.

Our first iteration of where schema is merged, which has been the basis of our success so far, is called “partials”. This brought the convenience and simplicity of node modules to GraphQL.

Our next major iteration for GraphQL includes a newer component model in which each component is its own fully independently executable schema, making both composition and aggregation easier.

Diagram indicating what a package is comprised of

Implementation design principles

In developing tools to create component-based schemas, we want the following characteristics:

  • Composable types
    * Types can be made up of many shared types
    * Extend and compose types for a use case without impacting others
  • Composable resolvers
    * Invoke without invoking a new service (no network hops)
    * Ability to reduce repetitive service calls within the same query tree
  • Portable
    * Injectable upstream requirements (service clients, etc)
  • Easy to collaborate
    * Shared code, not services
    * Manage contributions, version as needed, simplify co-development
  • Schema first
    * Easy to read, Easy to update
    * Collaborate cross platform on API design in common language

Separate use cases, separate services

With a component model it is not necessary to provision, operationalize and share a service. Each application or API can provide for its own needs by specifying the components it needs.

Diagram depicting independent team iteration / responsibility

Because components are composed and merged together without a centralized or shared service, teams can:

  1. Pick and choose their own orchestration needs
  2. Version independently of other teams
  3. Scale independently as their use case demands
  4. Perform better cost attribution
  5. Explicitly declare data requirements in UI components

This is a powerful model because it enables our goals for reducing dependencies between teams and the affected area caused by changes in a shared service.

Composition

One of the great aspects of GraphQL is its ability to compose types and resolvers. We don’t want to force two teams to constantly iterate on the same definitions just to satisfy all use cases.

Instead, it is easy for teams to compose new types or extend existing types as separate components that can then be versioned and developed independently.

Diagram of component dependencies as packages

A component module A may be composed of an imported module and type T while another component module B, with slightly different needs than A has been composed from a T’ component.

This also allows components to take advantage of existing resolvers by simply invoking across a binding to the composed type. We can also cache results from these calls within a single execution of a query or mutation, reducing the number of times something must be resolved at all.

Components in code

So what might this look like from a code perspective? Let’s take a more real-world example for a property listing appearing on a site like Vrbo.

What is a Listing made up of? Let’s make it overly simple and assume:

  • Property
  • Reviews

Let’s start with the Property component.

Example property component

Next, Reviews component:

Example reviews component

Finally let’s compose these together into a Listing:

Example listing component

This is different because it also has a new declaration to import property and review. This enables listing to take advantage of both the types in these two components, as well as the resolvers.

In this last example, let’s take a look at the listing resolvers as well:

Example listing resolver

This makes the listing resolver delegate its primary query to property and review in parallel to form its base. The cool thing about this is that it does not simply invoke the resolver function, but rather executes through GraphQL. This lets both type validation and type resolvers to continue to run normally.

The rest of the shaping for Listing type is done with type resolvers (not shown here).

Collaboration

GraphQL is both a query language and type-based schema-first API specification.

One of the problems (and sometimes benefits) of REST is that it is not naturally schema-first. Tools like the OpenAPI specification often rely on after-the-fact generation of specification for documentation purposes only; there is no strict binding of API schema to implementation.

With GraphQL, the schema is the API, and that is a powerful thing.

Because we have relied on inner-source collaboration to develop many of our GraphQL components, the importance of being able to do so in a more accessible way has been critical.

Plain test and even separate .graphql files for type and root type definitions are much easier to read and collaborate on than code, not to mention agnostic to any particular language or platform.

Keeping in sync

When you start versioning modules independently, keeping teams moving off of unsupported versions requires additional tooling to keep track of dependencies and notify developers.

We keep track by building dependency graphs from applications which we can query and run reports to see who is using what.

Finally

In this journey, our evolution has been from monoliths, to miniliths and BFFs (back-ends-for-front-ends), to node apps and modules. But the journey isn’t complete. The industry is evolving to serverless and static pages (JAMStack, etc) and we have begun to as well. As a result, part of our design has also been about runtime portability as well as environment portability.

When it comes down to what it takes to develop a modern web application at Vrbo, it looks something like this:

Diagram depicting an application comprised of SSR and GraphQL

Developers spend their time in two areas: React development and GraphQL development (which is usually just reused). This begs the question: why are we deploying applications at all?

With the advent of new capabilities in CDNs like compute with CloudFlare Workers, Fly.io, and others, a serverless (and even containerless) orchestration layer makes a lot of sense.

Diagram depicting GraphQL orchestration moving from web tier to CDN

We plan to experiment with pushing our model to the bleeding edge (pun intended), and it is made easier through a component model designed for flexibility and developer scale.

Further reading

While we were working on the next iteration of our partial schema / component model, a new open source project was released called GraphQL Modules. GraphQL follows an almost identical paradigm and looks great.

I’ve been working on a similar project in the open that is an iteration on our existing internal solution and that is what is used here for the examples. Currently, some simple examples can be seen here.

You can also read more about some of the history in these links:

The Architectural Principles Behind Vrbo’s GraphQL Implementation的更多相关文章

  1. Architectural principles

    原文 "If builders built buildings the way programmers wrote programs, then the first woodpecker t ...

  2. Why GraphQL is Taking Over APIs

    A few years ago, I managed a team at DocuSign that was tasked with re-writing the main DocuSign web ...

  3. Training - An Introduction to Enterprise Integration

    What is EI? Enterprise Integration (EI) is a business computing term for the plans, methods, and too ...

  4. Three Sources of a Solid Object-Oriented Design

    pingback :http://java.sys-con.com/node/84633?page=0,1 Object-oriented design is like an alloy consis ...

  5. Windows Kernel Security Training Courses

    http://www.codemachine.com/courses.html#kerdbg Windows Kernel Internals for Security Researchers Thi ...

  6. Angular vs React---React-ing to change

    这篇文章的全局观和思路一级棒! The Fairy Tale Cast your mind back to 2010 when users started to demand interactive ...

  7. 斯坦福CS课程列表

    http://exploredegrees.stanford.edu/coursedescriptions/cs/ CS 101. Introduction to Computing Principl ...

  8. (转)Web2.0 大型互联网站点的架构

    这种资料.向来可遇不可求啊 WikiPedia 技术架构学习分享 http://www.dbanotes.net/opensource/wikipedia_arch.html YouTube 的架构扩 ...

  9. Awesome Go

    A curated list of awesome Go frameworks, libraries and software. Inspired by awesome-python. Contrib ...

随机推荐

  1. 【题解】Kathy函数 [BZOJ1223] [P2235] [HNOI2002]

    [题解]Kathy函数 [BZOJ1223] [P2235] [HNOI2002] 这几疯狂刷了数位\(dp\)的题,到这道题时被卡了一天,一看大佬的讲解发现居然是求回文数╮(╯_╰)╭ 感觉被大佬狠 ...

  2. go get 命令

    示例: go get github.com/jinzhu/gorm 下载并安装gorm包. 远程代码库有github,GitLlab,Gogs 命令介绍说明: -fix : 比如,我的代码是一年前1. ...

  3. TinyXPath 对于xpath标准的支持测试

    xpath是一种基于xml的查询标准,一般的xml解析工具都具有,有的因为卓越的xpath性能而出名,其匹配查询算法牛逼而又高效,和正则有的一拼.虽然我现在大部分从事前端工作了,但是对于原理性的东西还 ...

  4. Java自学-操作符 算数操作符

    Java的算数操作符 算数操作符 基本的有: + - * / % 自增 自减 ++ -- 基本的加 减 乘 除: public class HelloWorld { public static voi ...

  5. IDEA 环境下更改Maven的仓库镜像提高下载速度

    Maven把所有常用的jar包存放在一个集中的仓库(repository)中,项目需要什么jar包和他相关的依赖,只要在pom.xml文件中声明就可了,还是很方便的.repository分两种,一个是 ...

  6. Spring的配置文件找不到元素 'beans' 的声明

    Spring的配置文件找不到元素 'beans' 的声明 一般是由Spring的版本导致的,你可以尝试使用如下的某一种. <?xml version="1.0" encodi ...

  7. uni-app学习

    1. 学习uni-app 1.1. 概述 号称一次编写多端运行的前端框架,架构图如下 对某些不同平台的特殊功能,可以通过条件进行自动编译,也就是写两套代码,不同的环境会选择不同代码编译 1.2. 推荐 ...

  8. HTML5深入学习之数据存储

    概述 本来,数据存储都是由 cookie 完成的,但是 cookie 不适合大量数据的存储,cookie 速度慢且效率低. 现在,HMLT5提供了两种在客户端存储数据的办法: localStorage ...

  9. APS应用案例|纽威阀门实现高效排产

    企业背景: 苏州纽威阀门股份有限公司(下文简称:纽威阀门)成立于1997年,总部设在江苏苏州.自成立以来一直致力于工业阀门的研发与制造,以为客户提供全套工业阀门解决方案为目标.纽威阀门通过企业的努力发 ...

  10. jsp 获取后端配置文件.properties的某个配置内容

    如后端有个叫做config.properties的配置文件: sys.img=st_sp jsp中引用的方式是: <%@ page language="java" impor ...