B
B
BonBon Slick2021-02-07 01:34:31
Problem-Based Design
BonBon Slick, 2021-02-07 01:34:31

Pass object or simple data to CQRS command?

/**
     * @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);

With this approach, we avoid double calling as if using simple data.
If more commands are called after the current one, you can reuse the filter and pass it to the next commands, instead of writing each field in a new one.
However, if the filter is empty, for example this case
/**
     * @throws JsonException
     */
    public function resetPassword(
        ResetPasswordRequest $request
    ): JsonResponse {
        $this->commandBus->dispatch(
            (new UpdateUser(UserFilter::fromRequest($request), $request->ip()))

And the request data is empty, then the repository will throw an EntityNotFound error. filters with null values ​​are not present in the selection.

It can be solved like this

/**
     * @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;

That is, for the update, the data for the command will already be valid, such a user exists. But a double call and user search.
private function update(UpdateUser $command): void {
        $filter = (new UserFilter())->setUuid($command->id);
        $entity = $this->userRepository->single($filter);

If there is an exception, then in the controller or at the request validation stage, if this is, for example, a token and we have a check for its existence.

In any case, in each command it will be necessary to make a selection of the user, because it is not known what data was changed before the command was called. Therefore, the maximum that will come out is -1 request.
It turns out 3 criteria for choosing approach
1 - logic, which is more logical, more obvious. Easier to maintain. This includes, of course, a competent distribution of responsibility, tk. the filter is not really data for the team, it's all about validity. Simple data is easier to validate, they are either of a certain type or null, and when an object is passed, you need to check if there is a value inside the command object.
2 - what is there on DDD, CQRS, they probably raised the topic, because these are just recommendations, but I'm sure that such a question has already been asked there and they have come to a specific conclusion using the arguments why and what parameters for the CQRS command?

On the one hand, Object Segregation, on the other hand, is it needed here?

What do you think? When and how, why?

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question