Giter Site home page Giter Site logo

Comments (4)

codeliner avatar codeliner commented on July 28, 2024

The CommandDispatchResult contains some information about the dispatch, for example the effected aggregate id and the recorded events (if any).

In one project we return the aggregate id back to the client, because it is generated on-the-fly for new aggregates and the client needs to know it.

Recorded events could be inspected or pushed to a message queue.

If a command preprocessor returns a CommandDispatchResult, dispatching is stopped (see https://github.com/event-engine/php-engine/blob/master/src/EventEngine.php#L804). You can use this to stop command handling. This is especially useful if you have a preprocessor that acts as an async switch: The preprocessor can check command metadata. If it contains for example an handled_async key the prprocessor does nothing and command dispatching goes on.
If the key is not present, the preprocessor pushes the command on an async queue and aborts the current dispatch. This way your commands (or some of them, depends on your implementation) can be handled async while still using a single Event Engine configuration.

Also CommandDispatchResult is used for logging.

Unfortunately, docs are not complete here, but I hope my short answers gives you some idea what you can do with it.

from php-engine.

arnedesmedt avatar arnedesmedt commented on July 28, 2024

Ok, Thx for the information!

I've looked into the source code and was wondering what could be the meaning of the CommandDispatchResult.

And now I know that I misuse the CommandDispatchResult to return an Id.

I know this is not the way CQRS works, but in our case we had to find a way to return data from the controllers, so I used a CommandDispatchResult filled with a MessageBag to return an Id.

Our backend systems would need a big refactor to send uuid's from the client, that's the reason why we retrieve a generated id from the backend.

If you got some better solutions/tweaks for this particular case, be my guest 😉

But I also realize, that you can't implement something like this in the library, otherwise you would break the main rule of CQRS

Thx!

from php-engine.

codeliner avatar codeliner commented on July 28, 2024

It's ok, we do the same! You just need to be aware that you cannot handle commands async in that case or you have to generate the id on the server, pass it to the command bevor it is pushed on a queue and return that id back to the client.

Here is how we do it in a current project:

Modified MessageboxHandler

public function handle(ServerRequestInterface $request): ResponseInterface
    {
        $payload = null;
        $messageName = 'UNKNOWN';
        $metadata = [];

        try {
            $payload = $request->getParsedBody();

            $messageName = $request->getAttribute('message_name', $messageName);

            if (\is_array($payload) && isset($payload['message_name'])) {
                $messageName = $payload['message_name'];
                $metadata = $payload['metadata'] ?? [];
                $payload = $payload['payload'] ?? [];
            }

            $result = $this->messageDispatcher->dispatch($messageName, $payload, $metadata);

            if ($result === null) {
                return new EmptyResponse(StatusCodeInterface::STATUS_ACCEPTED);
            }

            // Here we use the CommandDispatchResult to get a newly created aggrgateId
           // In fact, all command responses include the effected aggregate id
            if ($result instanceof CommandDispatchResult) {
                if ($effectedAggregateId = $result->effectedAggregateId()) {
                    return new JsonResponse(
                        ['aggregateId' => $effectedAggregateId],
                        StatusCodeInterface::STATUS_ACCEPTED
                    );
                }

                return new EmptyResponse(StatusCodeInterface::STATUS_ACCEPTED);
            }

            if (is_object($result) && method_exists($result, 'toArray')) {
                $result = $result->toArray();
            }

            return new JsonResponse($result);
        } catch (\InvalidArgumentException $e) {
            throw new \RuntimeException(
                $e->getMessage(),
                StatusCodeInterface::STATUS_BAD_REQUEST,
                $e
            );
        }
    }

And here is an example command that is handled by a new aggregate and therefor generates the aggregate id if not present in the command payload:

<?php

declare(strict_types=1);

namespace Acme\Configuration\Domain\Model\Customer\Command;

use EventEngine\Data\ImmutableRecord;
use EventEngine\Data\ImmutableRecordLogic;
use EventEngine\JsonSchema\JsonSchema;
use EventEngine\Schema\PayloadSchema;
use Acme\Application\Domain\Model\Base\AggregateCommand;
use Acme\Application\Domain\Model\Base\ProvidesPayloadSchema;
use Acme\Application\Domain\Model\InternationalAddress;
use Acme\Configuration\Domain\Api\Payload;
use Acme\Configuration\Domain\Api\Schema;
use Acme\Configuration\Domain\Model\Customer\CustomerId;
use Acme\Configuration\Domain\Model\Customer\CustomsId;
use Acme\Configuration\Domain\Model\Customer\Name;
use Acme\Configuration\Domain\Model\Customer\TaxId;

class AddCustomer implements ImmutableRecord, AggregateCommand, ProvidesPayloadSchema
{
    use ImmutableRecordLogic;

    /** @var CustomerId */
    private $customerId;

    /** @var Name */
    private $name;

    /**
     * @var InternationalAddress
     */
    private $businessAddress;

    /**
     * @var CustomsId
     */
    private $customsId;

    /**
     * @var TaxId
     */
    private $taxId;

    private function init(): void
    {
        if (null === $this->customerId) {
            $this->customerId = CustomerId::generate();
        }
    }

    public function name() : Name
    {
        return $this->name;
    }

    public function customerId() : CustomerId
    {
        return $this->customerId;
    }

    public function aggregateId(): string
    {
        return $this->customerId->toString();
    }

    public function businessAddress() : InternationalAddress
    {
        return $this->businessAddress;
    }

    public function customsId() : CustomsId
    {
        return $this->customsId;
    }

    public function taxId() : TaxId
    {
        return $this->taxId;
    }

    public static function payloadSchema(): PayloadSchema
    {
        return JsonSchema::object(
            [
                Payload::NAME => Schema::name(),
                Payload::CUSTOMS_ID => Schema::customsId(),
                Payload::TAX_ID => Schema::taxId(),
                Payload::BUSINESS_ADDRESS => Schema::internationalAddress(),
            ],
            [
                Payload::CUSTOMER_ID => Schema::customerId(),
            ]
        );
    }
}

All our messages are ImmutableRecords and use immutable value objects. They also provide their own payload schema which is passed to the Event Engine registerMessageType method.

As you can see, CustomerId is marked as an optional property in the schema and in the ImmutableRecord::init function we check if it is null and generate a new one in that case. The newly generated customer id is added automatically to the CommandDispatchResult by Event Engine.

from php-engine.

arnedesmedt avatar arnedesmedt commented on July 28, 2024

Thx for the info, glad to see, we almost implemented it the same way :-)

from php-engine.

Related Issues (14)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.