BonBonSlick
@BonBonSlick
Vanilla Web Architect

Передавать обьект или простые данные в команду CQRS?

/**
     * @throws JsonException
     */
    public function resetPassword(
        UserResetPasswordToken $resetPasswordToken,
        ResetPasswordRequest $request
    ): JsonResponse {
        $token = $resetPasswordToken->token();
        $this->commandBus->dispatch(
            (new UpdateUser((new UserFilter())->setResetPasswordToken($token), $request->ip()))


class UpdateUser {
    public UserFilter $filter;
    public string     $ip;
    public function __construct(
        UserFilter $filter,
        string $ip
    ) {
        $this->filter = $filter;
        $this->ip     = $ip;
...

private function update(UpdateUser $command): void {
        $entity = $this->userRepository->single($command->filter);

Таким подходом мы избегаем двойного вызова как если бы, использованые простые данные.
Если будут вызваны еще команды после текущей, можно переиспользовать фильтр и передавать в следующие команды, вместо прописи каждого поля по новой.
Однако, если фильтр пустой, к примеру такой случай
/**
     * @throws JsonException
     */
    public function resetPassword(
        ResetPasswordRequest $request
    ): JsonResponse {
        $this->commandBus->dispatch(
            (new UpdateUser(UserFilter::fromRequest($request), $request->ip()))

А данные запроса пустые, то репозиторий выдаст ошибку EntityNotFound т.к. фильтры со значениями null отсутствуют при выборке.

Это можно решить так

/**
     * @throws JsonException
     */
    public function resetPassword(
        UserResetPasswordToken $resetPasswordToken,
        ResetPasswordRequest $request
    ): JsonResponse {
        $token = $resetPasswordToken->token();
        /** @var User $user */
        $user = $this->handle(new GetSingleUser((new UserFilter())->setResetPasswordToken($token)));
        $this->commandBus->dispatch(
            (new UpdateUser($user->id->value(), $request->ip()))
                ->setResetPasswordToken($token)

class UpdateUser {
    public string     $ip;
    public string     $id;

    public function __construct(
        string $id,
        string $ip
    ) {
        $this->id = $id;
        $this->ip     = $ip;

То есть для апдейта данные для комманды будут уже валидны, такой юзер существует. Но двойной вызов и поиск юзера.
private function update(UpdateUser $command): void {
        $filter = (new UserFilter())->setUuid($command->id);
        $entity = $this->userRepository->single($filter);

Если будет ексепшен то в контроллере или на стадии валидации запроса, если это к примеру токен и у нас проверка на его существование.

В любом случае в каждой команде надо будет делать выборку юзера, ибо неизвестно какие данные были изменены до того как команда была вызвана. Поетому максимум что выйдет это -1 запрос.
Выходит 3 критерия выбора подхода
1 - логика, что логичнее, более очевидно. Легче поддерживать. Сюда входит само собой грамотное распределение ответственности, т.к. фильтр это не совсем данные для команды, все дело в валидности. Простые данные проще валидировать, они или определенного типа или null, а когда передан обьект надо проверить есть ли значение внутри обьекта команды.
2 - что там по DDD, CQRS, наверняка поднимали тему, т.к. это лишь рекомендации, но уверен что там уже задавались таким вопросом и пришли к конкретному выводу оперируя аргументами почему и какие параметры для CQRS команды?

С одной стороы Object Segregation, с другой нужен ли он тут?

Что думаете? Когда и как правильно, почему?
  • Вопрос задан
  • 17 просмотров
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы
LAPTOP.RU Москва
от 150 000 до 190 000 ₽
Sibedge Москва
До 200 000 ₽
28 февр. 2021, в 01:35
200000 руб./за проект
28 февр. 2021, в 00:22
2500 руб./за проект
27 февр. 2021, в 22:14
30000 руб./за проект