efacilitation/eventric

Queries

Closed this issue · 10 comments

Hello,

I really don't understand implementation of queries...
I want to create a eventric app for the server side of my app. And client-server interaction will be through commands/queries.
About command, no problems.
But for me a query should not emit events and should access to projections. So I read eventric specs... eventric code but I'm a bit confused...

First of all, why stores know projections...? The only thing I can imagine is for one day implement snapshots... if it is... it's not lean ;) !!
Then I can't do nothing from a queryHandler ! Except emit DomainEvents :D ! Even if I want to reach a projection I can't if it isn't registered to a store...

I just think that you don't really use queries, and this is just a quick copy-paste of commands if you can see what I mean :)

So I just wanted to know what's your point, just to be sure I don't miss something huge. I can try a pull request if you agree with what I said.

Still I think this project is the begining of something really nice.

Regards.

Hi,

a query should not emit events

Yes, a query should definitely not emit domain events and most certainly also not emit application events. You are absolutely right.

a query [..] should access to projections

Do you mean should or should not? Because query handlers do have access to projections and should.

So I just wanted to know what's your point

We added queries / query handlers to encapsulate reading actions such as projection access. Consider having a projection in a web shop for a shopping cart. You want to ensure that only the owner of the cart can actually view its contents (authorization). With a query handler you have the possibility to add authorization middleware. What do you think about it? Side note: Query handlers were actually added quite some time before the projections as they are now implemented.

I just think that you don't really use queries

That is perfectly fine. You can use eventric without using query handlers.

Thank you, I think we're on the same page :)

A query MUST access projections we're agree :)
And to avoid every wrong move, queryHandlers shoudn't have access to emitDomainEvent. But this doesn't really matter

About projection access, _di.$projectionStore(storeName, projectionName, cb)... I don't understand why should I know a store to access a projection from my query? (This is also true form adapters, commands, services.... all _di context objects) I don't get relations between projections & stores...

In fact even access projections from context is difficult... I need the projection id... why name isn't the id ?

Ok I read specs form projections... I found it twisted but I think I got it (meaningless var names are confusing :) ).

In fact I don't have to access projections directly as in eventric todo. My projection has to use a store to store data. And my query has to access this store not the projection.

In fact I just didn't got the point between stores and projections (I think the todo misoriented me... even if use my brain had be usefull... ! vars to keep projections data is quite light !! ).

Ok then I can go on ! Thank you very much for your time !.

Just wanted to add, that it might be really confusing to have the "_di-Container" injected in basically everything. We definitely have to segment that to make things more clear. Basically how it should be:

CommandHandler: $adapter, $query, $repository, $projection, $domainService, $emitDomainEvent
QueryHandler: $adapter, $query, $projection, $projectionStore
Aggregate: $emitDomainEvent
Projection: $store_, $subscribe_

About Queries and Projections in general: Think about Queries as "the passive read side" which can lookup the projection store to return information on request, while Projections can be "the (re)active read side" which not necessarily need to store its information into a store (inmemory might be faster, for sure).

Thanks for your ongoing interest and support in the project. 👍

Yes thank you for your great work.
I can see clearly now the rain is gone ! ;)
I agree with that table :)

Just a question about aggregates, to check if an email is unique for example, I need projections... so this is my commands dutty ? But that means that my validations will be splitted between commands & aggregates? Or should I have to use a service every time I have to validate ?

From my perspective Aggregates should do validations which concern them based on their possible state. So stuff like "title too long?" might be inside the "Product" Aggregates editTitle() method.

If you have to validate upon the state of the command itself, that should happen in the command handler. if you have to work with multiple aggregates normally the "Domain Service" is the way to go. Additionally we decided that its OK to have queries directly in the Command Handler too. This is actually a hot topic on itself, since one have to deal with eventual consistency here.

Thats how we did it in our application:

CommandHandler

  CreateAccount: (params, callback) ->
    @$query 'isEmailUnique', email: params.email
    .then (isUnique) =>
      if not isUnique
        return callback (new Error 'EMAIL_NOT_UNIQUE'), null
      cryptoAdapter = @$adapter 'crypto'
      @$repository('Account').create
        cryptoAdapter: cryptoAdapter
        email: params.email
        password: params.password
      .then (accountId) =>
        @$repository('Account').save accountId
      .then (accountId) ->
        callback null, accountId
      .catch (error) ->
        callback error

QueryHandler:

  isEmailUnique: (params, callback) ->
    @$projectionStore 'mongodb', 'Accounts'
    .then (mongodbCollection) ->
      mongodbCollection.count email: params.email, (error, count) ->
        callback error, count is 0

Hope that helps.

That's what I understood... but get validations in the same place is nice too... :)
I get that consistency could be a problem since we can't be sure that projection is up to date...

What about get a store for aggregates? As in projections... But not pubicly available (as if it is a private var). Just for aggregates to store this kind of atomic data.

  • This allow to centralize validations -> then get both 'title too long' and 'email already exist' throwed errors in the same place
  • This also allow to solve constistency issues.

I'm not sure that commands... perhaps even services...? Should then access data that is not consistent via queries or projections (you didn't allowed projection access... then why queries).
But as I don't really have another case than validation in mind to use queries from commands, I just ask... :)

What about get a store for aggregates?

As you already said, an Aggregate is something like a projection itself. If you would share State between different Aggregates (or "instances" of Aggregates), there would be no guarantee about consistency of single Aggregates (since they might be influenced by the store).

Should then access data that is not consistent via queries or projections (you didn't allowed projection access... then why queries).

Access to ProjectionStores and Projections is possible.. but, as you mention it. With our latest change to ProjectionIDs its currently not possible to access projections which got automatically initialized from the Context (after adding using addProjection). Whoops. Need to fix that. :-)

Queries and Commands additionally will get $projection injected again, which will give you access to the projection itself. Opened #9. With this implemented you wouldn't need to define a query, but could e.g. store all emails in the projection (member variable) and then do something like $projection ('someId').then (projection) -> if not projection.emails['foo@example.com'] in the command handler - for alot of accounts, you would need some RAM, though. ;) If you're interested in contributing, go for it!

btw, the http://www.cqrs.nu/Faq is a really good resource too:

Do I put logic in command handlers?
Yes. Exactly what logic depends on your factoring.
The logic for validating the command on its own merits always goes in the command handler. If the command handler is just a method on the aggregate, then the next step is simply to use the state of the aggregate to do further validation. In a more functional factoring, where the aggregate exists independently of the command handlers, the next step would be to load the aggregate and do validation against it. [..]

So, Validating is really not a completely central concern, since there are different forms of it. From my point of view, checking for the uniqueness of an email address is not really a concern of one single Account Aggregate. So, the check should happen in the UI/UX itself (using a Query or Projection) or be a concern of the command handler itself.

Can I call a read side from my command handler?
No.

This one is actually interesting. We got in touch with the cqrs.nu people and had a conversation about this point. Basically they're right, one should not cross the write and read side like that. But since the alternative would be to make the check only in the UI, we decided that it would be OK to query in a command handler. But this should be really an exceptional use case.

About eventual consistency: eventric has something called "Waiting Mode". If you call enableWaitingMode on the context after initialize, it is enabled. Now Commands will only resolve (complete) after all Projection handle*-Methods have called their "done-Handler" (callback). This is actually crucial for testing concerns. We would not recommend using the WaitingMode in production (yet).

Thank you for all these details !
I like more and more eventric !

I hope to get issues to contribute to this great project... :)
For now, I introduced it on 2 projects, and discover it with pleasure...

Thank you.

Great to have detailed conversations about the project 👍