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也被称为查询语言,因此就像向服务器发送查询以获取所需的响应一样。
可以用一个日常的例子来解释,就像向数据库查询并获取所需的数据一样。
「干净架构」是什么?

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

预计的响应如下所示。
{
"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,对于提高应用程序的可维护性和可扩展性也非常有效。因此,在任何时候都可以积极采用清洁架构。