命令查询职责分离模式 Command and Query Responsibility Segregation (CQRS) Pattern
描述#
把应用拆分成查询和更新两部分。这种模式可以最大限度地提高性能,可扩展性和安全性。
背景和问题#
在传统的数据管理系统中,数据存储可能会同时执行读和写操作,这意味着有读写之间会有资源的争夺,着对性能是会有负面的影响的。
解决方案#
程序中的读和写操作分开。读就是查询(Query),写就是更新,也称为命令(Command)。
注意事项#
把数据分拆成读和写操作可以提高系统的性能和安全性,但是数据同步存在延迟了。
何时使用#
- 业务单一,有大量读写操作的程序。
- 想通过读写分离提高业务性能。
结构中包含的角色#
- CommandStorage 抽象命令仓库
- QueryStorage 抽象查询仓库
- Mysql 具体仓库
- CommandMysql 具体命令仓库
- QueryMysql 具体查询仓库
- QueryService 查询服务
- CommandService 命令服务
可用到的设计模式思维#
仓库按照功能分成两部分,一部分是查询,另外一部分是更新(命令)。查询仓库可以看成一个代理,限制这个代理只负责仓库的查询部分,而命令仓库就是更新部分,它们使用相同数据的仓库(可能使用同一个仓库,可能是两个数据一摸一样的仓库)。
最小可表达代码#
// 抽象命令仓库
interface CommandStorage
{
public function updateById($id, array $attribute);
}
// 抽象查询仓库
interface QueryStorage
{
public function all();
}
// 具体仓库
class Mysql implements CommandStorage, QueryStorage
{
protected $students = [
['id' => 1, 'name' => '张三', 'age' => 18],
];
public function updateById($id, array $attribute)
{
foreach ($this->students as &$student) {
if ($id == $student['id']) {
$student = $attribute + $student;
}
}
unset($student);
return true;
}
public function all()
{
return $this->students;
}
}
// 具体命令仓库
class CommandMysql implements CommandStorage
{
protected $commandStorage;
public function __construct(CommandStorage $commandStorage)
{
$this->commandStorage = $commandStorage;
}
public function updateById($id, array $attribute)
{
return $this->commandStorage->updateById($id, $attribute);
}
}
// 具体查询仓库
class QueryMysql implements QueryStorage
{
protected $queryStorage;
public function __construct(QueryStorage $queryStorage)
{
$this->queryStorage = $queryStorage;
}
public function all()
{
return $this->queryStorage->all();
}
}
// 查询服务
class QueryService
{
protected $queryStorage;
public function __construct(QueryStorage $queryStorage)
{
$this->queryStorage = $queryStorage;
}
public function all()
{
return $this->queryStorage->all();
}
}
// 命令服务
class CommandService
{
protected $commandStorage;
public function __construct(CommandStorage $commandStorage)
{
$this->commandStorage = $commandStorage;
}
public function update()
{
return $this->commandStorage->updateById(1, ['age' => 11]);
}
}
$mysql = new Mysql;
$commandService = new CommandService(new CommandMysql($mysql));
$commandService->update();
$queryService = new QueryService(new QueryMysql($mysql));
$data = $queryService->all();
var_dump($data);
推荐文章: