如何在 CQRS 架构中创建读写分离的 repos
How to create read and write separate repos in CQRS architecture
我有这个界面和存储库
<?php
namespace App\Domain\Model\User;
interface UserRepositoryInterface
{
public function findNextId(string $id = null): string;
public function findOneById(string $id): ?User;
public function findOneByEmail(string $email): ?User;
public function save(User $user): void;
public function remove(User $user): void;
}
final class DoctrineUserRepository implements UserRepositoryInterface {...}
现在,根据 CQRS,我必须将我的回购分成 2 个回购。一种用于读取,一种用于写入模型,对吗?
所以将我的界面拆分为 2 个界面。
<?php
namespace App\Domain\Model\User;
interface UserRepositoryReadInterface
{
public function findNextId(string $id = null): string;
public function findOneById(string $id): ?User;
public function findOneByEmail(string $email): ?User;
}
And write:
<?php
namespace App\Domain\Model\User;
interface UserRepositoryWriteInterface
{
public function save(User $user): void;
public function remove(User $user): void;
}
并且应该创建 DoctrineUserWriteRepository.php
和 DoctrineUserReadRepository.php
。那是对的吗?我真的迷失了。红吨文章,但它解释绝对不清楚。
好的,所以您在这里所做的是使用存储库模式拆分代码以实现 CQRS
架构。
我不知道你为什么要使用存储库模式来做到这一点,因为在大多数情况下,使用 CQRS
的想法与 DDD
和其他架构等一起使用(我我不会说它应该,但根据我的经验和与不同团队的合作......)。
另一方面,如果您只是想使用存储库模式来获得更好、更有条理的代码,那就继续去做吧,但另一方面,您应该意识到,在大多数情况下,人们不会这样做喜欢像这些案例这样的过度工程,而且这些人不仅仅是小辈。
无论如何,在这种情况下,我宁愿使用 Services
作为开始,所以这里有一个例子:
让我们从安装 Ecotone
开始,我个人在许多项目(大项目 of-course)中使用并看到了它。
composer require ecotone/laravel
然后你就可以构建你的服务了:
// I'll call this UserService since you've pointed to User in your repository pattern.
class UserService
{
#[CommandHandler]
public function save(SaveUserCommand $command) : void
{
// get user and save new info, create new one Or etc.
}
#[QueryHandler]
public function findOneByEmail(GetUserEmaillAddressQuery $query)
{
$userFoundOut= User::where(... or find or etc. // use the query
// to get user by email address or id or etc.;
return $userFoundOut;
}
}
在上面的代码中,我们创建了 UserService
和 query and command
,您可能会想为什么它们会合二为一 class,我应该说,我和很多人一起工作过人们,在许多项目中,我看到人们使用一个 class 来做到这一点,而在其他一些情况下,他们使用两个 classes 来分隔 query
和 command
.
我宁愿将他们大体分开并做一些类似 UserQueryServices Or UserCommandServices
的事情,但在 other-hand 中你可能会与像 lead-architectures 这样的人一起工作,在这种情况下,他们'我会根据构建上下文等来决定要做什么(这根本没有任何问题,并且不要将同样的 class 想法视为一个坏主意。)。
现在在控制器中查询示例见下文:
class UserController
{
public function __construct(private QueryBus $queryBus) {}
public function getUserEmailAddress(Request $request)
{
$userId = $request->get('user_id');
$user = $this->queryBus->send(new GetUserEmaillAddressQuery($userId));
return new response($user->email);
}
}
class GetUserEmaillAddressQuery
{
public function __construct(private string $user_Id) {}
public function getUserId(): string
{
return $this->user_Id;
}
}
和 of-course 命令:
class UserController
{
public function __construct(private CommandBus $commandBus) {}
public function saveUser(Request $request)
{
$userId = $request->get('user_id');
$name = $request->get('user_name');
$email = $request->get('user_email');
$something_cool= $request->get('something_cool');
$this->commandBus->send(new SaveUserCommand($userId, $name $email ,$something_cool));
return new response('done');
}
}
class SaveUserCommand
{
public function __construct(private string $user_id, private string $name,
private string $email, private string $something_cool) {}
public function getUserId(): string
{
return $this->user_id;
}
public function getEmail(): string
{
return $this->email;
}
public function getSomethingCool(): string
{
return $this->something_cool;
}
public function getName(): string
{
return $this->name;
}
}
我有这个界面和存储库
<?php
namespace App\Domain\Model\User;
interface UserRepositoryInterface
{
public function findNextId(string $id = null): string;
public function findOneById(string $id): ?User;
public function findOneByEmail(string $email): ?User;
public function save(User $user): void;
public function remove(User $user): void;
}
final class DoctrineUserRepository implements UserRepositoryInterface {...}
现在,根据 CQRS,我必须将我的回购分成 2 个回购。一种用于读取,一种用于写入模型,对吗? 所以将我的界面拆分为 2 个界面。
<?php
namespace App\Domain\Model\User;
interface UserRepositoryReadInterface
{
public function findNextId(string $id = null): string;
public function findOneById(string $id): ?User;
public function findOneByEmail(string $email): ?User;
}
And write:
<?php
namespace App\Domain\Model\User;
interface UserRepositoryWriteInterface
{
public function save(User $user): void;
public function remove(User $user): void;
}
并且应该创建 DoctrineUserWriteRepository.php
和 DoctrineUserReadRepository.php
。那是对的吗?我真的迷失了。红吨文章,但它解释绝对不清楚。
好的,所以您在这里所做的是使用存储库模式拆分代码以实现 CQRS
架构。
我不知道你为什么要使用存储库模式来做到这一点,因为在大多数情况下,使用 CQRS
的想法与 DDD
和其他架构等一起使用(我我不会说它应该,但根据我的经验和与不同团队的合作......)。
另一方面,如果您只是想使用存储库模式来获得更好、更有条理的代码,那就继续去做吧,但另一方面,您应该意识到,在大多数情况下,人们不会这样做喜欢像这些案例这样的过度工程,而且这些人不仅仅是小辈。
无论如何,在这种情况下,我宁愿使用 Services
作为开始,所以这里有一个例子:
让我们从安装 Ecotone
开始,我个人在许多项目(大项目 of-course)中使用并看到了它。
composer require ecotone/laravel
然后你就可以构建你的服务了:
// I'll call this UserService since you've pointed to User in your repository pattern.
class UserService
{
#[CommandHandler]
public function save(SaveUserCommand $command) : void
{
// get user and save new info, create new one Or etc.
}
#[QueryHandler]
public function findOneByEmail(GetUserEmaillAddressQuery $query)
{
$userFoundOut= User::where(... or find or etc. // use the query
// to get user by email address or id or etc.;
return $userFoundOut;
}
}
在上面的代码中,我们创建了 UserService
和 query and command
,您可能会想为什么它们会合二为一 class,我应该说,我和很多人一起工作过人们,在许多项目中,我看到人们使用一个 class 来做到这一点,而在其他一些情况下,他们使用两个 classes 来分隔 query
和 command
.
我宁愿将他们大体分开并做一些类似 UserQueryServices Or UserCommandServices
的事情,但在 other-hand 中你可能会与像 lead-architectures 这样的人一起工作,在这种情况下,他们'我会根据构建上下文等来决定要做什么(这根本没有任何问题,并且不要将同样的 class 想法视为一个坏主意。)。
现在在控制器中查询示例见下文:
class UserController
{
public function __construct(private QueryBus $queryBus) {}
public function getUserEmailAddress(Request $request)
{
$userId = $request->get('user_id');
$user = $this->queryBus->send(new GetUserEmaillAddressQuery($userId));
return new response($user->email);
}
}
class GetUserEmaillAddressQuery
{
public function __construct(private string $user_Id) {}
public function getUserId(): string
{
return $this->user_Id;
}
}
和 of-course 命令:
class UserController
{
public function __construct(private CommandBus $commandBus) {}
public function saveUser(Request $request)
{
$userId = $request->get('user_id');
$name = $request->get('user_name');
$email = $request->get('user_email');
$something_cool= $request->get('something_cool');
$this->commandBus->send(new SaveUserCommand($userId, $name $email ,$something_cool));
return new response('done');
}
}
class SaveUserCommand
{
public function __construct(private string $user_id, private string $name,
private string $email, private string $something_cool) {}
public function getUserId(): string
{
return $this->user_id;
}
public function getEmail(): string
{
return $this->email;
}
public function getSomethingCool(): string
{
return $this->something_cool;
}
public function getName(): string
{
return $this->name;
}
}