Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CQRS and CRUD misconception #28

Closed
bugproof opened this issue Jun 18, 2018 · 21 comments
Closed

CQRS and CRUD misconception #28

bugproof opened this issue Jun 18, 2018 · 21 comments

Comments

@bugproof
Copy link

bugproof commented Jun 18, 2018

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

@gregoryyoung
Copy link
Owner

UpdateEntity Customer { id : 44465, active : false }

is very different than

DeactivateCustomer {id 44465 }

@bugproof
Copy link
Author

bugproof commented Jun 18, 2018

@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.

@gregoryyoung
Copy link
Owner

gregoryyoung commented Jun 18, 2018

> <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.

@bugproof
Copy link
Author

bugproof commented Jun 18, 2018

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?

@MHacker9404
Copy link

MHacker9404 commented Jun 18, 2018 via email

@bugproof
Copy link
Author

bugproof commented Jun 18, 2018

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
Copy link

rganz commented Jun 18, 2018

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

@gregoryyoung
Copy link
Owner

@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

@bugproof
Copy link
Author

@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.

@gregoryyoung
Copy link
Owner

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

@bugproof
Copy link
Author

bugproof commented Jun 18, 2018

@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.

@gregoryyoung
Copy link
Owner

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

image

@bugproof
Copy link
Author

bugproof commented Jun 18, 2018

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.

@gregoryyoung
Copy link
Owner

gregoryyoung commented Jun 18, 2018

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)

@bugproof
Copy link
Author

bugproof commented Jun 18, 2018

@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
Copy link
Owner

Like what SimpleCQRS does? #TrollLOLLOL :P

https://github.com/gregoryyoung/m-r/blob/master/SimpleCQRS/Commands.cs

@bugproof
Copy link
Author

@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;

@gregoryyoung
Copy link
Owner

gregoryyoung commented Jun 18, 2018

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 ;-)

@bugproof
Copy link
Author

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

@gregoryyoung
Copy link
Owner

gregoryyoung commented Jun 18, 2018

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.

@gregoryyoung
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants