停止到处编写查询:你的 Laravel 代码库所需的 Atomic Habit
Source: Dev.to
请提供您希望翻译的完整文本内容,我将为您翻译成简体中文。
主题
- 原子查询构建 (AQC)
- 后端开发
- 软件架构
- 清洁代码
你有查询问题,只是还没承认而已。
让我们描述一个普通的 Laravel 代码库。
- 一个控制器获取活跃用户
- 一个任务几乎获取相同的用户,只是多加了一个条件
- 一个服务再次获取它们,却缺少了一个条件
- 一个测试从头重建查询
意图相同,查询不同,结果也不同。
没有人注意到,直到出现故障。然后大家开始猜测哪个版本是 “正确的”。
这就是问题所在。
不是复杂性。不是规模。
是不一致。
问题不在于重复,而在于漂移。
人们常说“不要重复自己”,但他们仍然到处写这种代码:
User::where('active', true)->get();然后在别的地方:
User::where('active', true)
->whereNotNull('email_verified_at')
->get();再在别的地方:
User::where('active', true)
->where('role', 'admin')
->get();现在你没有重复了。
你有同一事物的不同定义。
这更糟,因为你的系统会根据你打开的文件而表现不同。
规则:单层拥有所有查询
只有一种方式可以阻止这种情况:所有数据库查询必须位于同一层。 该层即 AQC。
一旦它存在:
- 控制器 不 编写查询
- 服务 不 编写查询
- 任务 不 编写查询
- 测试 不 编写查询
如果查询存在于 AQC 之外,那就是错误的。没有争议。它违反了 AQC。
之前(控制器)
public function index(Request $request)
{
$users = User::where('active', true)
->whereNotNull('email_verified_at')
->get();
return view('users.index', compact('users'));
}之后(控制器)
public function index(Request $request)
{
$users = (new GetUsers())->handle(['active' => true]);
return view('users.index', compact('users'));
}现在该查询有 唯一的归属。
Source: …
同样的规则遍及所有地方
控制器
$users = (new GetUsers())->handle([
'role' => $request->role,
]);服务
$users = (new GetUsers())->handle([
'digest_enabled' => true,
]);任务
$users = (new GetUsers())->handle([
'active' => false,
]);测试
$users = (new GetUsers())->handle([
'active' => true,
]);已经没有人再编写查询了。他们只会使用它们。猜猜怎么着?你拥有组合。
AQC 实战
where('active', $params['active']);
}
if (!empty($params['digest_enabled'])) {
$query->where('digest_enabled', $params['digest_enabled']);
}
if (!empty($params['role'])) {
$query->where('role', $params['role']);
}
// Apply sorting when requested; otherwise sort by id
if (isset($params['sortBy']) && isset($params['type'])) {
$query->orderBy($params['sortBy'], $params['type']);
}
return isset($params['paginate'])
? $query->paginate(User::PAGINATE)
: $query->get();
}
}正在发生什么?
- 条件过滤 – 活跃用户、启用摘要的用户、基于角色的过滤——全部由调用方控制。没有在多个地方硬编码。
- 灵活排序 – 查询支持动态排序,但如果未指定,则默认采用可预测的行为。
- 分页或完整集合 – 一个类同时处理用于 UI 的分页列表和用于任务或导出的完整集合。
这就是组合模式的实际应用。一个查询类为多个消费者服务,每个消费者传入自己的参数。基础条件和逻辑集中在一个地方。没有控制器、服务或任务会重新实现过滤器或意外遗漏条件。
美妙之处? 每个消费者都能准确获得所需内容,而无需复制或分叉查询逻辑。你只需维护一个获取用户的唯一真相来源。
您将获得的(以及不再失去的)
当查询集中在一个地方时:
- 您不再重新定义业务规则
- 您不再忘记条件
- 您不再跨文件追踪错误
- 您不再猜测哪个查询是正确的
更改只需一次。行为在所有地方更新。就是这样。
什么算作违规
如果你在 AQC 之外的任何地方写下以下代码:
User::where(...);就已经破坏了架构,违反了 AQC 设计模式规则。
- 不管它是“只有一个条件”
- 不管它是“临时的”
- 不管它是“这样更快”
这是错误的。AQC 只有在强制执行时才有效,而不是仅仅建议。
习惯
- 它已经存在 → 使用它
- 它不存在 → 在 AQC 中创建它
ist → create it in AQC
没有第三种选择。
最后思考
Atomic Query Construction 通过为每个查询提供唯一的归宿,消除了重复查询:
- 单层。
- 每个操作一个类。
- 一个公共方法。
- 一个参数签名。
每个使用者每次都使用相同的查询。
- 控制器调用 AQC 类。
- 服务调用 AQC 类。
- 任务调用 AQC 类。
- 测试调用 AQC 类。
数据库只响应 单层——且仅此单层。
停止猜测。
停止重复。
停止在文件之间追逐细微的差异。
构建这一层,强制执行规则,使查询纪律成为习惯。
这不是可选的。这是可维护、可预测的 Laravel 代码库的基石。