Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Activity
  • Jan 31 2019 19:32

    prolic on v1.10.3

    (compare)

  • Jan 31 2019 19:32

    prolic on master

    update changelog (compare)

  • Jan 31 2019 19:29
    prolic closed #176
  • Jan 31 2019 19:28
    prolic commented #186
  • Jan 31 2019 19:28
    prolic closed #186
  • Jan 31 2019 19:28

    prolic on master

    fix restarting projection durin… respect lock in memory resetting projection and 12 more (compare)

  • Jan 31 2019 16:26
    fritz-gerneth commented #189
  • Jan 31 2019 16:21
    prolic commented #189
  • Jan 31 2019 16:06
    sandrokeil commented #189
  • Jan 31 2019 15:31
    grzegorzstachniukalm synchronize #186
  • Jan 31 2019 15:23
    kamil-nawrotkiewicz edited #76
  • Jan 31 2019 15:22
    kamil-nawrotkiewicz edited #76
  • Jan 31 2019 15:22
    kamil-nawrotkiewicz opened #76
  • Jan 31 2019 15:19
    grzegorzstachniukalm synchronize #186
  • Jan 31 2019 15:09
    grzegorzstachniukalm synchronize #186
  • Jan 31 2019 13:54
    coveralls commented #186
  • Jan 31 2019 13:48
    mrook commented #189
  • Jan 31 2019 13:41
    prolic commented #189
  • Jan 31 2019 13:40
    prolic commented #189
  • Jan 31 2019 13:31
    fritz-gerneth commented #189
Alexander Miertsch
@codeliner
no it wasn't me. I remember that a team ran into an update issue with sequential arrays and it was fixed with this arrayReplaceRecursiveAssocOnly method. I'll look up who had the issue and talk directly to them
Gary Lockett
@internalsystemerror_gitlab
ok, to seq arrays there should be no change, its only assoc arrays as values that will be affected, to copy how seq arrays are already handled
Alexander Miertsch
@codeliner
:+1:
Martin added the method. I have a call with him tomorrow anyway, so I can ask him for review.
Gary Lockett
@internalsystemerror_gitlab
back again, also added some PRs for the replace methods which would be required to remove a top level key in an existing document. up to you how you want to handle those
Zacharias Luiten
@netiul
So, I'm still finding my way with Event Sourcing and CQRS in general...
Now I have this "post message" request payload which besides some generic message details contains upload ids which reference an "Upload" (existing aggregate).
These uploads referenced by the upload ids need to be "converted to" (created from) "Attachment"s (aggregate) and added to a "Message" (aggregate).
So basically I have an http request payload that consists of multiple command payloads...
My guess for the right approach would be to have this request handler dispatching multiple commands: 0..n times "Create Attachment From Upload", then a "Post Message" command with in the payload references to the just created Attachment aggregates.
Any thoughts about this? :)
Gary Lockett
@internalsystemerror_gitlab
image.png
I'm new to event engine/prooph myself. You could use a preprocessor?
like I use one to ensure that the unique constraint is checked before passing to the aggregate, but you can return a modified form of the command from the preprocessor
Zacharias Luiten
@netiul
I'm not using the event-engine though, but it gives some ideas. Thanks
Zacharias Luiten
@netiul
Ok, the event engine is amazing! :) Hadn't taken the time yet to look into it. I just used the prooph components so far
I'm about to start a brand new project for a client. Seriously thinking about event engine. So much quicker to get something up and running
Gary Lockett
@internalsystemerror_gitlab
I'm currently trying to figure out how to queue commands and have them run by a separate process from the event engine skeleton. If anyone has any suggestions/information on this, please let me know
Zacharias Luiten
@netiul
Maybe dispatch to a controller and take it from there? https://event-engine.io/api/descriptions.html#3-2-5
Fritz Gerneth
@fritz-gerneth
@internalsystemerror_gitlab any kind of job scheduler / queue can do. e.g. publish them to a rabbitmq queue or so
Alexander Miertsch
@codeliner

Sorry guys, not so much time at the moment to look into the chat every day. Would love to support you more with your questions.

@netiul For your problem you can route the command to a controller $eventEngine->passToController(self::INSTALL_FIRST_USER_BOARD, InstallFirstUserBoard::class);

The controller can then return a list of commands to be dispatched:

<?php
declare(strict_types=1);

namespace Inspectio\Board\Domain\Controller;

use Inspectio\Board\Domain\Model\Board\BoardId;
use Inspectio\Board\Domain\Model\Board\BoardIdList;
use Inspectio\Board\Domain\Model\Board\Command\InstallBoardWithDefaultName;
use Inspectio\Board\Domain\Model\BoardPermissions\Command\AdminGrantAccessToBoard;
use Inspectio\Board\Domain\Model\BoardPermissions\PermissionSet;
use Inspectio\Board\Domain\Model\BoardPermissions\PermissionsId;
use Inspectio\Board\Domain\Model\User\Command\AddUser;
use Inspectio\Board\Domain\Model\Board\Command\InstallFirstUserBoard as InstallFirstUserBoardCommand;
use Inspectio\Board\Domain\Model\User\UserInfo;

final class InstallFirstUserBoard
{
    /**
     * @var BoardIdList
     */
    private $exampleBoards;

    /**
     * InstallFirstUserBoard constructor.
     * @param BoardId|null $exampleBoard
     */
    public function __construct(BoardIdList $exampleBoards)
    {
        $this->exampleBoards = $exampleBoards;
    }


    public function __invoke(InstallFirstUserBoardCommand $command): array
    {
        return $this->initiate($command);
    }

    public function initiate(InstallFirstUserBoardCommand $command): array
    {
        $nextCommands = [
            AddUser::fromRecordData([
                AddUser::USER_ID => $command->userInfo()->userId(),
            ])->withUserInfo($command->userInfo()),

            InstallBoardWithDefaultName::fromRecordData([
                InstallBoardWithDefaultName::BOARD_ID => $command->boardId(),
                InstallBoardWithDefaultName::USER_ID => $command->userInfo()->userId(),
            ])->withUserInfo($command->userInfo()),
        ];

        foreach ($this->exampleBoards->items() as $exampleBoard) {
            $nextCommands[] = AdminGrantAccessToBoard::fromRecordData([
                AdminGrantAccessToBoard::BOARD_ID => $exampleBoard,
                AdminGrantAccessToBoard::USER_ID => $command->userInfo()->userId(),
                AdminGrantAccessToBoard::PERMISSIONS_ID => PermissionsId::fromBoardAndUserRefs(
                    $exampleBoard,
                    $command->userInfo()->userId()
                ),
                AdminGrantAccessToBoard::BOARD_PERMISSIONS => PermissionSet::readOnly(),
            ])->withUserInfo(UserInfo::asSysAdmin());
        }

        return $nextCommands;
    }
}
@internalsystemerror_gitlab
Do you want to queue a command triggered by an event that has been recorded?
Alexander Miertsch
@codeliner

@netiul

I'm about to start a brand new project for a client. Seriously thinking about event engine. So much quicker to get something up and running

Yeah, that's the main purpose. Getting started as fast as possible, but still have the full power of CQRS/ES at hand when needed. My company is a software agency. We need to be fast, flexible, efficient, cloud-ready and event-driven. Event Engine is our company framework. Nothing in the PHP world is comparable. Currently also setting up a similar structure for Node.js + TypeScript.

Last year I supported a team of 6 devs to build a globally operating logistics platform in ~6 moths with the help of InspectIO and EventEngine. It was an amazing experience for all of us ;)

Currently working for a real estate company where we integrated parts from the event engine into their existing boilerplate (based on Laravel). But it's not the same experience. The Event Engine works best as a whole. If you take out individual parts you get back to the problem of having to maintain a lot of boilerplate code and that slows teams down. But with the InspectIO code generator we might have a solution for that problem. Testing phase starting soon ;)

Alexander Miertsch
@codeliner
My vision is that CQRS/ES becomes as easy as CRUD so that you no longer have an excuse to not use it :D
Gary Lockett
@internalsystemerror_gitlab

@codeliner

@internalsystemerror_gitlab
Do you want to queue a command triggered by an event that has been recorded?

Not necessarily, I need to query a webservice to get a list, and then foreach item in that list either do a create or update action. Ideally I want to dispatch this command asynchronously. I know how I can do it by hand, I'm wondering is there anything in EventEngine that I could reuse to have a separate docker container running a process which will monitor the "command_queue", pick up the commands and run them

essentially I'm wondering how to handle our long running tasks (which once triggered, to get through ~1million jobs takes a few hrs)
Alexander Miertsch
@codeliner

@internalsystemerror_gitlab I see. A queue or messaging layer is not included in the Event Engine. There are so many different solutions available all with pros and cons.
So same as what @fritz-gerneth suggested. We mainly work with AWS SQS, the logistic platform uses rabbitMQ
and the SRE Team at the real estate company evaluates Google Pub/Sub at the moment.

It usually works like this:

  • One or more queue worker pull commands from queue
  • Command message is prepared
  • $eventEngine->dispatch($command) is called to route command to handler or controller
The rabbitMQ solution for the logistics platform uses a queue worker library written in Go that executes a php script for each command pulled from the queue.
Gary Lockett
@internalsystemerror_gitlab
Ah that's what I'm after, $eventEngine->dispatch()
tx
Alexander Miertsch
@codeliner
you're welcome
Gary Lockett
@internalsystemerror_gitlab
I guess there's no amqp-consumer included with the event engine skeleton then
Alexander Miertsch
@codeliner
@internalsystemerror_gitlab it's similar to what is happening in the skeleton messagebox: https://github.com/event-engine/php-engine-skeleton/blob/master/src/Http/MessageBox.php#L48
Well, there is a consumer included. You need to do the tutorial ;)
Gary Lockett
@internalsystemerror_gitlab
I did, u mean for the projections?
That seems all self contained though, nothing in the bin file suggests anything I could reuse
I'm currently just going through the code trying to figure out how its working so I'm sure I'll get there eventually
Alexander Miertsch
@codeliner
Ah sorry not a consumer, only a rabbitMQ producer integration to forward messages to the security system: https://github.com/event-engine/php-engine-skeleton/blob/master/src/System/SystemServices.php#L64
Gary Lockett
@internalsystemerror

I asked this question in phpunit gitter but want to ask here to see if you guys have come across the same issue:

I'm using PHP 7.4.3 and Xdebug 2.9.2 and having an issue with the code coverage (specifically code paths)...

I have the following function I'm trying to test:

public static function modifyManufacturer(Manufacturer\State $state, Message $message): Generator
    {
        $name = $message->get(Payload::MANUFACTURER_NAME);
        if ($name !== $state->name()) {
            yield [Event::MANUFACTURER_NAME_CHANGED,
                [
                    Payload::MANUFACTURER_ID   => $message->get(Payload::MANUFACTURER_ID),
                    Payload::MANUFACTURER_NAME => $name,
                ],
            ];
        }
    }

and the following tests which cover that static method:

public function testModifyManufacturerSucceeds(): void
    {
        $payload = [
            Payload::MANUFACTURER_ID   => $this->manufacturerId,
            Payload::MANUFACTURER_NAME => 'Test Manufacturer 2',
        ];
        $events = $this->collectNewEvents(
            Manufacturer::modifyManufacturer(
                Manufacturer\State::fromArray($this->createDefaultState()),
                $this->makeCommand(Command::MODIFY_MANUFACTURER, $payload)
            )
        );
        self::assertRecordedEvent(
            Event::MANUFACTURER_NAME_CHANGED,
            $payload,
            $events
        );
    }

public function testModifyManufacturerIgnoresDuplicates(): void
    {
        self::assertNotRecordedEvent(
            Event::MANUFACTURER_NAME_CHANGED,
            $this->collectNewEvents(
                Manufacturer::modifyManufacturer(
                    Manufacturer\State::fromArray($this->createDefaultState()),
                    $this->makeCommand(
                        Command::MODIFY_MANUFACTURER,
                        [
                            Payload::MANUFACTURER_ID   => $this->manufacturerId,
                            Payload::MANUFACTURER_NAME => $this->name,
                        ]
                    )
                )
            )
        );
    }

and yet the coverage shows that 0 paths were covered for that method.
image.png

What am I doing wrong?

Alexander Miertsch
@codeliner

absolutely no idea. Seems like code coverage can not handle the generator function properly.
what happens if you write a test for a function like this:

public static function test(): Generator
{
  yield "test";
}

?

Gary Lockett
@internalsystemerror
Yeah I figured it might be a bug with xdebug. Just putting together a test repo to see what happens when testing a yield behind a condition.
Alexander Miertsch
@codeliner
:+1:
Gary Lockett
@internalsystemerror
yeh happens with just a yield behind an if statement: https://github.com/internalsystemerror/phpunit-test
Alexander Miertsch
@codeliner
interesting
Zacharias Luiten
@netiul
The event store provides 2 interfaces for projectors (Projector/ReadModelProjector). I fail to see when to use the "normal" Projector. Until now I only have use cases for the ReadModelProjector. Can someone explain what a good use case is for the Projector interface?
Sascha-Oliver Prolic
@prolic
a projector stores its state in the event store backend (rdbms), it's not meant to update any readmodel
this way you can query the event-store directly and use it as a read model
especially useful for temporal queries, where rdbms really suck
Zacharias Luiten
@netiul
I see, thanks