GraphQL与Clean Architecture非常搭配好

过去,当提到API开发时,唯一的选择是REST。然而,由于GraphQL的出现,以及GitHub采用了GraphQL,现在可以使用REST和GraphQL两种方式来开发API。我自己也在实际的API开发中使用了GraphQL。从我的经验来看,GraphQL与清晰架构非常相配。所以本次我将写一些关于GraphQL和清晰架构的内容。

GraphQL 是什么

首先,让我简单解释一下GraphQL是什么。

GraphQL是一种用于发出API请求的方法。与REST也是用于发出API请求的方法不同的是,GraphQL可以灵活地设置响应内容,这是最大的区别。

举个例子,假设我们要调用一个获取用户信息的API。

在REST的情况下

假设是REST的情况下,当访问https://localhost/user/1这样的端点时,可以得到以下的响应。

{
	"user_id": 1,
	"user_name": "山田太郎",
	"email": "yamada@example.com",
	"tasks": [
		{
			"task_id": 1,
			"task_title": "要件定義"
		},
		{
			"task_id": 2,
			"task_title": "設計書作成"
		}
	]
}

由于这个响应已经牢固固定了,即使在“不需要tasks的信息啊…”的时候也会无条件地获取tasks的信息。
因此,在这种情况下,需要创建一个新的API来排除tasks的信息。

在GraphQL的情况下

在GraphQL的情况下,与REST类似

{
	"user_id": 1,
	"user_name": "山田太郎",
	"email": "yamada@example.com",
	"tasks": [
		{
			"task_id": 1,
			"task_title": "要件定義"
		},
		{
			"task_id": 2,
			"task_title": "設計書作成"
		}
	]
}

可以以类似的方式获取响应。
对于REST的情况,“我不需要tasks的信息…”这个问题,在GraphQL中可以在相同的端点完成。

{
	"user_id": 1,
	"user_name": "山田太郎",
	"email": "yamada@example.com",
}

由于可以在不包含任务信息的情况下接收响应,因此无需创建新的API。
GraphQL也被称为查询语言,因此就像向服务器发送查询以获取所需的响应一样。
可以用一个日常的例子来解释,就像向数据库查询并获取所需的数据一样。

「干净架构」是什么?

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3238373832342f34333834313063372d323237382d373033612d346431612d3162326364313738373733322e6a706567.jpeg

我为什么认为在使用GraphQL时采用Clean Architecture是个不错的选择,接下来我来解释一下。

制作物

er.jpg

预计的响应如下所示。

{
	"user_id": 1,
	"user_name": "山田太郎",
	"tasks": [
		{
			"task_id": 1,
			"title": "要件定義",
			"status": "未着手"
		}
	]
}

请用中文将以下内容进行释义:”クリーンアーキテクチャのディレクトリ構造”

根据上面所述的清晰架构图,将目录安排如下。

app/
  ├ controllers
  │  └ graphql_controller.py
  └ presenters
  │  └ user_presenter.py
  ├ usecases
  │  └ get_user.py
  ├ entities
  │  └ user_entities.py
  └ main.py

每个目录的作用如下所示。

控制器

接收请求并将其转化为可以在较低层进行处理的格式。
GraphQL建议使用Base64形式发送请求的ID。
因此,需要将其转化为可以处理Base64的形式。
在此示例中,将user_id从VXNlcjox转换为1是控制器的职责。

用例

这个应用的主要功能是获取用户信息的处理过程。
本次操作将根据用户ID作为筛选条件从数据库中获取记录。

主持人

创建响应的形式。
这次将采用以下模式的形式。GraphQL需要将ID变成Base64形式以保证其唯一性。ID在从数据库获取数据时是整型,因此需要进行Base64编码。

@strawberry.experimental.pydantic.type(model=TaskBaseModel)
class TaskType:
    id: ID
    title: str
    status: str
    user_id: int


@strawberry.experimental.pydantic.type(model=UserBaseModel)
class UserType:
    id: ID
    user_name: auto
    email: auto
    tasks: auto

实体

管理数据在不同层之间的地方。例如,从控制器层传递数据到用例时,将其传递给以下的类型。通过这样做,用例将能够接收到以UserFilter形式传递的数据并进行处理。

class UserFilter(BaseModel):
    user_id: int

处理流程

按照 Clean Architecture 图的指示进行处理。具体来说,
1. 接收请求 → 转换为 usecase 可处理的形式(通过 controller)。
2. 通过 usecase 从数据库中获取记录。
3. 使用 presenter 创建响应的数据结构。
4. 返回响应。

规格变更

到目前为止,我们已经成功创建了一个满足规范的API。
接下来,我们将解释为什么在使用GraphQL进行实现时采用清洁架构会更好。

请用REST方式开发

太意想不到了,规格已经变更了…w
虽然我使用GraphQL构建了,但是现在希望改成REST。
这个规格变更似乎会产生相当大的影响范围。。。

这次的项目是使用GraphQL创建的。

然而,這次我們是使用乾淨架構進行開發。GraphQL和REST的區別在於請求的接收方式和回應的返回方式。
因此,受影響的範圍僅限於控制器和呈現器。
我認為僅僅明確界定受影響範圍就已經相當有益。

控制器的修改

首先,我們將從接收到請求後的處理開始對controller進行修改。但是,我們不會修改graphql_controller.py。而是新建一個rest_controller.py作為REST的控制器。

重點在於將userFilter作為形式參數傳遞給usecase進行處理。

主持人的修改

接下来需要修改的是presenter的响应处理。不过,使用FastAPI的功能时,在response_model=UserBaseModel的位置上会生成响应,并且UserBaseModel的类型是由usecase返回的类型,所以不需要修改。

@app.get("/user/", response_model=UserBaseModel)
def get_user(user_id: int):
    return RestUserController().get_user(user_id)

修正的結果 (Alternative option: 修正后的結果)

由于使用了干净架构,所以当从GraphQL更改为REST时,只需要修改controllers和main.py文件即可。并且,对于之前使用GraphQL创建的处理程序完全没有进行任何更改。

这之所以了不起,是因为即使有人说“我们想换回GraphQL”,由于已经完成了某个版本,所以可以立即进行更改。

app/
  ├ controllers
  │  └ graphql_controller.py
  │  └ rest_controller.py → REST用のコントローラー追加
  └ presenters
  │  └ user_presenter.py
  ├ usecases
  │  └ get_user.py
  ├ entities
  │  └ user_entities.py
  └ main.py → RESTのエンドポイントと追加

最终版本的代码可在以下链接找到:https://github.com/y-p-e/graphql_fastapi_clean_architecture。

总结

我认为GraphQL和清洁架构非常相配。尤其是在初次引入GraphQL时,可能会遇到学习成本高和不知道有效使用方法的问题,这时候可能会考虑切换回熟悉的REST。实际上,我也看到过类似的文章。

即使在这种情况下,如果我们使用清洁架构构建应用程序,核心功能中的用例部分仍然可被直接使用,只需要更改控制器(controller)和呈现器(presenter)来处理请求和响应。我认为这非常强大。

此外,清洁架构的思想不仅仅适用于GraphQL,对于提高应用程序的可维护性和可扩展性也非常有效。因此,在任何时候都可以积极采用清洁架构。

bannerAds