Hello —

Let me give you an example.

We have a requirement: an user uploads a file. This file is to be ETL’d into 
the database. But before being uploaded, the file needs to be validated. And 
this validation needs to be user-friendly, so the user can fix the file (we use 
a custom file format for file uploads). If all goes well, the file is to be 
ETL’d into the staging database.

So I have a command that validates, and uploads, the file. It raises (actually, 
returns) two possible events: files uploaded, or validation failed. The 
validation failed event triggers the compilation of the errors into a 
user-friendly to be returned as a response. The file uploaded event triggers 
the ETL process which grabs the file, performs ETL, and loads it directly into 
the staging database.

Where did DDD come into play here?

John

On May 19, 2017, at 10:43 AM, xprt64 
<xpr...@gmail.com<mailto:xpr...@gmail.com>> wrote:

Hi, thanks for the feedback, it's nice to see that other are thinking about 
CQRS in PHP :)

> ... you’re assuming that we would use DDD as the domain ..

I assumed DDD as the approach, not the "domain". The domain is the business 
domain and probably any (complex enough) domain would work (we are not talking 
about CRUD implementations, we are assuming that the problem CQRS is trying to 
solve is not solvable by CRUD). I assumed DDD because it is one of the nicest 
approaches used to solve complex domains and CQRS is one of the DDD 
implementations.

2017-05-19 16:38 GMT+03:00 Rivera, John 
<rive...@buffalo.edu<mailto:rive...@buffalo.edu>>:
Hello!

Thanks for bringing this topic up — I’m a big fan of CQRS/ES!

But I’ll begin with a caveat — I do not believe it’s suitable to a PSR. That is 
my opinion, and I am definitely open to debate.

Continuing with the assumption that I was convinced that it’s a worthy PSR… I 
think your interfaces are definitely excessive. Here’s my minimum requirements 
for a working CQRS application:

Interface Command {}

interface Query {}

interface Event {}

interface Mediator
{
    public function send(Command $command): void;
    public function request(Query $query): mixed;
    public function raise(Event $event): void;
}

That’s it.

You’re probably asking ‘wait a minute, what about the CommandHandler and 
EventHandler?” — we could have interfaces for each, as follows:

interface CommandHandler
{
   public function __invoke(Command $command): Event;
}

interface EventHandler
{
    public function __invoke(Event $event): void;
}

However, I eliminated those interfaces in my application because without them, 
I can write in the concrete Command object as a type hint instead of being 
forced to do a ‘if ($command instanceof ConcreteCommand) { … }”. Requiring the 
handlers to be invokable is sufficient. Perhaps someday we’ll get generics in 
PHP…

Another typical way of defining the Handler interfaces is to replace 
__invoke(…) with handle(…), but I liked the elegance of working with invokable 
classes in the Mediator, and that I could enforce an sort of ‘interface’ 
without defining one.

One more thing — requiring that the Command interface has a getAggregateId() 
method is a no-no — you’re assuming that we would use DDD as the domain. Don’t 
assume this — in fact, I’m not using DDD in my application at all, only CQRS/ES.

Your Subscriber stuff seems intriguing, however — I’ll take a closer look at 
your application to see how they work.

Thanks for bringing up the topic — I hope this will lead to a illuminating 
debate!
John

On May 19, 2017, at 2:53 AM, Constantin Galbenu 
<xpr...@gmail.com<mailto:xpr...@gmail.com>> wrote:

What do you think about introducing some standards regarding CQRS and Event 
sourcing?

I have a working/in production web based CRM application that uses one of my 
micro-frameworks<https://github.com/xprt64/cqrs-es> . It uses the command 
handler style described on cqrs.nu<http://cqrs.nu/>, where command handlers are 
aggregate methods. The aggregate is identified by the ID (extracted from 
Command) and class (manually/automatically subscribed).

The normal flow is this (the flow implemented by my DefaultCommandDispatcher):
- the client code (i.e. a REST endpoint) creates a new Command and it sends the 
command to the CommandDispatcher
- the CommandDispatcher identifies the aggregate class (using 
CommandSubscriber)  and ID (using Command::getAggregateId) then creates a new 
aggregate instance
- it rehydrates the aggregate from the event store (by loading all the prior 
events and applying them to the aggregate instance)
- it calls the aggregate's command method (identified by using 
CommandSubscriber) and collects the yielded events (also it applies them onto 
the aggregate itself, one by one, at collect time - a kind of reactive 
aggregate);
- it persists the collected events to the EventStore, FutureEventsStore or 
ScheduledCommandStore depending on the yielded message type
- it notifies the event handlers using EventDispatcher
- it discards the aggregate

The aggregate should not inherit from anything and nothing should be injected 
into it. It should remain pure, side effect free. Any side effect should be 
observed by its yielded events.

I like this style because it reduces a lot of code duplication found in the 
other style (command handler being in the Application layer) where almost every 
command handler looks the same: it loads the Events from the store using a 
Repository, it factories a new Aggregate, it rehydrates the Aggregate (by 
applying the events to the Aggregate), it calls a method on the aggregate, it 
collects the events, it persists the events and then notify all subscribers 
(event handlers). In cqrs.nu<http://cqrs.nu/>'s style this entire algorithm can 
be extracted in a CommandDispatcher.

So, I propose (at least) the following interfaces (in the namespace Cqrs)

//events

interface Event;


interface EventDispatcher;


interface EventSubscriber;


interface MetadataFactory;

interface EventStore;


interface AggregateEventStream extends EventStream;


interface EventStream extends \IteratorAggregate;


interface EventStreamGroupedByCommit extends EventStream;


//commands

interface Command;


interface CommandDispatcher; //can be decorated by a 
CommandDispatcherWithCommandValidation


interface CommandSubscriber;


interface MetadataWrapper;


interface CommandValidatorSubscriber; //used by 
CommandDispatcherWithCommandValidation


//scheduling


interface FutureEventsStore

interface ScheduledCommand extends Command, ScheduledMessage;


interface ScheduledEvent extends ScheduledMessage


interface ScheduledMessage extends IdentifiedMessage;

interface IdentifiedMessage;


interface CommandScheduler;


interface ScheduledCommandStore;


//read models and sagas (process managers)

interface ReadModelInterface;


interface SagaEventTrackerRepository;


I know that there are a lot of interfaces, but in fact, in a working 
application, more interfaces are needed.

In order for this to work, the minimal requirements from the code in the Domain 
layer is the Command interface (for its getAggregateId method). The Event 
interface should be used only to detect event handlers by reflection. So, the 
domain code remains (almost) pure.

I have a todo<https://github.com/xprt64/todosample-cqrs-es> example on github 
if you want to learn more.

I know that my question mixes the proposed standard with my implementation but 
I don't know how to start the discussion otherwise.

So, what do you think?

--
You received this message because you are subscribed to the Google Groups "PHP 
Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to 
php-fig+unsubscr...@googlegroups.com<mailto:php-fig+unsubscr...@googlegroups.com>.
To post to this group, send email to 
php-fig@googlegroups.com<mailto:php-fig@googlegroups.com>.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/0c9a062b-4f1b-4f3c-85d3-433a93fdb234%40googlegroups.com<https://groups.google.com/d/msgid/php-fig/0c9a062b-4f1b-4f3c-85d3-433a93fdb234%40googlegroups.com?utm_medium=email&utm_source=footer>.
For more options, visit https://groups.google.com/d/optout.


--
You received this message because you are subscribed to a topic in the Google 
Groups "PHP Framework Interoperability Group" group.
To unsubscribe from this topic, visit 
https://groups.google.com/d/topic/php-fig/78LIU6d7mic/unsubscribe.
To unsubscribe from this group and all its topics, send an email to 
php-fig+unsubscr...@googlegroups.com<mailto:php-fig+unsubscr...@googlegroups.com>.
To post to this group, send email to 
php-fig@googlegroups.com<mailto:php-fig@googlegroups.com>.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/2E629BBA-3015-4A4C-AA47-E85DC1539E64%40buffalo.edu<https://groups.google.com/d/msgid/php-fig/2E629BBA-3015-4A4C-AA47-E85DC1539E64%40buffalo.edu?utm_medium=email&utm_source=footer>.

For more options, visit https://groups.google.com/d/optout.



--
Best regards,
Constantin Galbenu,
Tel: +40728247366
Skype: xprt64
Twitter: @gicagalbenu<https://twitter.com/gicagalbenu>
Github: https://github.com/xprt64

--
You received this message because you are subscribed to the Google Groups "PHP 
Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to 
php-fig+unsubscr...@googlegroups.com<mailto:php-fig+unsubscr...@googlegroups.com>.
To post to this group, send email to 
php-fig@googlegroups.com<mailto:php-fig@googlegroups.com>.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/CABEUsAe24OaPQRQrKwHiYHfpzEJGG71S4o%3DeHBxPcK-A9-rvfw%40mail.gmail.com<https://groups.google.com/d/msgid/php-fig/CABEUsAe24OaPQRQrKwHiYHfpzEJGG71S4o%3DeHBxPcK-A9-rvfw%40mail.gmail.com?utm_medium=email&utm_source=footer>.
For more options, visit https://groups.google.com/d/optout.

-- 
You received this message because you are subscribed to the Google Groups "PHP 
Framework Interoperability Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to php-fig+unsubscr...@googlegroups.com.
To post to this group, send email to php-fig@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/php-fig/C3FB5291-3FE4-4639-AC19-F4A616A2C0BB%40buffalo.edu.
For more options, visit https://groups.google.com/d/optout.

Reply via email to