15

I've just learned about CQRS, and I would like to combine it in a project with a GraphQL based API. However, in order to do that, a question has come to my mind: according to CQRS, commands have to not return anything after its execution. However, according to GraphQL conventions, mutations have to return the updated state of the entity.

How should I deal with that? Are CQRS and GraphQL incompatible? The only solution that comes to my mind is, in order to resolve the mutation, first execute a command and later a query, in order to get the response object. Is there anything better than that? It doesn't look very efficent to me...

Thanks in advance

pitazzo
  • 1,125
  • 1
  • 13
  • 28

2 Answers2

13

How should I deal with that?

Real answer? Ignore the "have to not return anything" constraint; the underlying assumptions behind that constraint don't hold, so you shouldn't be leaning to hard on it.

How exactly to do that is going to depend on your design.

For example, if you are updating the domain model in the same process that handles the HTTP Request, then it is a perfectly reasonable thing to (a) save the domain model, (b) run your view projection on the copy of the model that you just saved, (c) and then return the view.

In other words, the information goes through exactly the same transformations it would "normally", except that we perform those transformations synchronously, rather than asynchronously.

If the model is updated in a different process, then things get trickier, since more message passing is required, and you may need to deal with timeouts. For instance, you can imagine a solution where you send the command, and then poll the "read side" until that model is updated to reflect your changes.

It's all trade offs, and those trade-offs are an inevitable consequence of choosing a distributed architecture. We don't choose CQRS because it makes everything better, we choose CQRS because it makes some things better, other things worse, and we are in a context where the things it makes better are more important than the things it makes worse.

VoiceOfUnreason
  • 52,766
  • 5
  • 49
  • 91
  • 4
    Adding a few cents, you can treat the Mutation return as a Query from CQRS. – Rodrigo Boratto Nov 18 '20 at 21:22
  • @RodrigoBoratto do you mean that do additional query using read model after mutation? – cYee Jul 01 '21 at 13:25
  • 1
    Depending on how you do your queries (and the use case), yes. If you have a cache layer, probably the mutation already changed it and you may need some entity to schema mapping before returning. What I usually do is just return the "schema ID" and let the GraphQL handle the rest, mostly because I use federated schemas and some "attributes" exists only on other services. – Rodrigo Boratto Jul 02 '21 at 13:51
  • is schema_id is the same as entity_id? `let the GraphQL handle the rest` do you mean client side refetching? – cYee Jul 13 '21 at 04:14
3

I am considering similar, i.e. using GraphQL predominantly for interfacing with the read-side of a system based on CQRS.

On the write-side, however, I am considering using a Web or REST API that has one end-point that accepts commands.

Remember, in CQRS you don't directly mutate entities but submit a command signalling your intent / desire to do something.

Alternatively, thinking out loud here, it may be possible to use mutations in GraphQL to create commands and track their status using subscriptions.

Ashley Aitken
  • 2,419
  • 2
  • 20
  • 24
  • 1
    I see your point, but I don't totally like the idea of having separate endpoints for queries and commands... Have you thought about breaking the GraphQL "rule" about returning the updated entity as result of the mutation and instead just returning a custom type with something like "command submitted"? – pitazzo Aug 02 '20 at 22:29
  • Yes, can all be done with GraphQL but (as my post suggested) I would probably just use one GraphQL command type, submit the command and get back command with accepted or rejected (by command handler) status updated and command identifier for future query or subscription regarding command status. – Ashley Aitken Aug 09 '20 at 16:05
  • I'd like to also throw into the discussion that what is returned by the mutation is up to you. It could also be nothing and be ok according to GraphQL. You'd just need to requery for the data from the client, instead of getting the updated response object, in order to have the client's local cache be correct. – m8a Jul 22 '21 at 14:24