gregoryyoung/m-r

CQRS and CRUD misconception

bugproof opened this issue ยท 21 comments

There are many articles and videos like "CQRS vs CRUD". They're not even the same things. Because you can use CQRS with CRUD (for example, CreateCommand, UpdateCommand, DeleteCommand) and mostly all applications are CRUD. Can you clarify?

Is implementing CRUD using CQRS misusing CQRS?

Is CQRS suitable for implementing REST/resource-based APIs with CRUD mental model, or it's a better fit for RPC APIs?

How do you use CQRS with DDD, how do you map to domain models? Do you map directly from your commands to domain models? Or do you use commands just as DTO holders? see https://stackoverflow.com/questions/50908260/cqrs-with-mediatr-and-re-usability-of-commands

UpdateEntity Customer { id : 44465, active : false }

is very different than

DeactivateCustomer {id 44465 }

@gregoryyoung but when you create REST API it always looks like the first example. Does it mean CQRS doesn't make sense when implementing REST API? I mean technically it can be done by creating something like UpdateCustomerCommand and using it to deactivate customer using your first example.

> <account>
>     <account_number>12345</account_number>
>     <balance currency="usd">100.00</balance>
>     <link rel="deposit" href="/account/12345/deposit" />
>     <link rel="withdraw" href="/account/12345/withdraw" />
>     <link rel="transfer" href="/account/12345/transfer" />
>     <link rel="close" href="/account/12345/close" />
> </account>

as example. The handlers for the operations defined then are what actually creates the command internally.

Oh, I get it now. But does it mean CQRS can't be used with CRUD? (means no operations, just passing the state/object) For example, closing the account would be setting isClosed to true. Does using it in this context make sense?

CQRS/CQS doesn't say anything about the single responsibility of a command. It tells to just isolate write methods from read methods.

Does it mean I don't need command-specific objects like DeactivateUserCommand ?

void DeactivateUser(int userId) will be a command too right? even despite I don't have any input model named DeactivateUserCommand

or more general

void UpdateUser(User user) will be a command too right?

rganz commented

When I started with CQRS I struggled with the same "mismatch", what helped me is this:

@rganz at first I thought that was to me and was going to be forced to point out the irony that I wrote the first article :P

@rganz Yeah there's a confusion when talking about CQRS in Task-oriented UIs vs CRUD oriented UIs. The definition of the pattern looks different for both. For example, the definition for CRUD oriented UIs presents CQRS as:

CustomerWriteService

void MakeCustomerPreferred(CustomerId)
void ChangeCustomerLocale(CustomerId, NewLocale)
void CreateCustomer(Customer)
void EditCustomerDetails(CustomerDetails)

CustomerReadService

Customer GetCustomer(CustomerId)
CustomerSet GetCustomersWithName(Name)
CustomerSet GetPreferredCustomers()

While for Task oriented UIs the definition for Command changes:

Commands
The method through which the Application Server will be told what to do is through the use of a
Command. A command is a simple object with a name of an operation and the data required to perform
that operation. Many think of Commands as being Serializable Method Calls. Listing 1 includes the code
of a basic command.

From: https://cqrs.files.wordpress.com/2010/11/cqrs_documents.pdf

It seems like it applies differently in different usage contexts.

The definitions for me are the same, how do you find them different?

@gregoryyoung There are no "SomeActionCommand" classes in your example of CQRS with CustomerWriteService and CustomerReadService and original definition describes commands as operations not data models/structures of data.

The definition says just to segregate methods that read from those that write and you're done.

But later in "Task based user interfaces" section it introduces new definition of command. Giving examples like:

public class DeactivateInventoryItemCommand {
 public readonly Guid InventoryItemId;
 public readononly string Comment;
 public DeactivateInventoryItemCommand (Guid id, string comment) {
 InventoryItemId = id;
 Comment = comment;
}

Does it mean there is a different definition of Command in CQRS depending on usage?

There is no clear definition of what command really is, first it says just to use different interfaces and you're done and then all of sudden it talks of commands as of data structures where you define some data to pass to perform some action.

My slide for CQRS does not normally have reads going through the domain so I am a bit confused.

image

One definition says that

A command is a simple object with a name of an operation and the data required to perform that operation. Many think of Commands as being Serializable Method Calls. Listing 1 includes the code
of a basic command.

Other definition says that:

CQRS is a pattern that segregates the operations that read data (queries) from the operations that update data (commands) by using separate interfaces

One definition speaks of a command as of a "simple object". The second definition speaks of a command as operation that update data.

The original definition of CQS talks about commands as of methods, not objects.

CQRS can be applied to methods or messages. To be fair the difference is rather not worth discussing as some languages use messages to implement method calls :) refactor method to message is widely available (refactor parameters to object)

@gregoryyoung I think it's worth discussing because "Command" gets a different meaning when applied to messages if by that you mean event-driven systems that handle things after receiving some message.

To make more sense it would be nice to provide example like:

class MakeCustomerPreferredCommand
{
   public int CustomerId { get; set; }
}

class CreateCustomerCommand
{
  public Customer Customer { get; set; }
}

class EditCustomerDetailsCommand
{
   public CustomerDetails CustomerDetails { get; set; }
}

That presents commands as objects instead of methods/operations.

@gregoryyoung Can't this duplication be a bit simplified?

public class CheckInItemsToInventory : Command {
public Guid InventoryItemId;
public readonly int Count;
public readonly int OriginalVersion;

public class RemoveItemsFromInventory : Command {
public Guid InventoryItemId;
public readonly int Count;
public readonly int OriginalVersion;

Could be but you probably don't want to. They are defining schema. What happens when you have 3 levels of inheritance etc and need to change something higher up? How much time did you save through you reuse? Its frankly also a PITA to go searching through inheritance hierarchies. There are as well tools that generate this stuff for you ;-)

@gregoryyoung I have CRUD based UI does it make sense to apply CQRS?

Not usually though it can. Normally you are more interested in behaviours, why something happens is often important. If you just need CRUD why not just maintain a SQL transaction file?

To be clear "Volume changed" on our order in the stock market. Why did volume change? We could have changed it, the market could have changed it (varying rules etc), it could have traded, it could be a regulatory cancellation, etc.

if you don't have the separate events I bet you get a bunch of code figuring out which type it was in many places.

That was my original point. Often no. I have discussed this a lot in talks there are simpler ways of reaching the same goal such as an audit table on updates. I am not trying to put you off the idea (it has benefits) but often they will not be realized IME