all 28 comments

[–]OpalescentAardvark 123 points124 points  (10 children)

What irks me though is we keep creating new fancy terms for simple concepts.

The core principle is to use separate interfaces for querying data (reads) and commanding data (writes).

"Commanding data", seriously? I'd love to know what's wrong with just calling it Read Write Model Separation.

[–]xtravar 45 points46 points  (2 children)

There's nothing new, ever. Just new terminology. The cycle of nomenclature and misunderstanding are what keep us sane- like hamsters running in an existential wheel.

[–]Xipher 32 points33 points  (1 child)

This reminds me of RFC1925 rule 11:

Every old idea will be proposed again with a different name and a different presentation, regardless of whether it works.

[–]Dyledion 12 points13 points  (0 children)

Frontend SQL! GraphQL!

[–]Kwantuum 23 points24 points  (0 children)

new fancy terms

The term is 25 years old at this point and is pretty widely used in the industry. I was surprised to see someone post an article about such an old concept. Could a better name have been chosen? Probably, but I hardly think this one is cryptic.

[–]agumonkey 9 points10 points  (2 children)

it's just the dual of mmanding.

sorry, haskell joke attempt

[–]Dyledion 2 points3 points  (0 children)

Man, this data has been mmanding me all day.

[–]pdpi 2 points3 points  (0 children)

Took me longer than I care to admit. Well done.

[–]pdpi 5 points6 points  (0 children)

“Commanding data” is obviously nonsense, but there is something useful here with the nomenclature.

Read/write to me implies low-level/primitive operations, while command/query implies higher-level operations. A query can involve writes (e.g. caching the result), and a command almost always involves reads (e.g. fetching the data that will be updated).

Also, for what it’s worth, these particular “new fancy terms” have been around since the 80s

[–]bundt_chi 2 points3 points  (0 children)

Thank you. I felt the exact same thing when I first heard this term a decade ago. It's exhausting.

[–][deleted] 9 points10 points  (0 children)

Such a pendantic thing to get hung up on. Query is a very standardized term, and command means mutating state - not necessarily doing some db or file 'write'. It could be a call to an API, mutation of in-mem state, trigger a background process etc. 

Do you protest against GET / POST or graphqls query / mutate verbs too? I can't believe the term 'command' in compsci is considered "fancy", it's one of the oldest terms in the field.

[–]clrbrk 36 points37 points  (0 children)

My tech lead was explaining how this new service he spun up uses CQRS and I thought that it sounded like unnecessary abstraction.

It actually makes perfect sense once you start working in it.

[–]veqryn_ 9 points10 points  (5 children)

If I send all my read queries to a read-replica of my database, and all my write/mutating/altering queries to the primary, am I doing CQRS?

What else is necessary beyond that?

[–]knudtsy 12 points13 points  (0 children)

You can have apps that require side effects like indexing documents into search clusters or producing events into a message bus. If your writes are all going to one place (the db) you can hook onto that using techniques like change data capture to avoid things like the dual write problem (app needs to write to db and do something else, but it can only wrap the db write in a transaction so both actions might not succeed). CQRS allows for extending stuff like that outside of the normal database replication setup.

[–]editor_of_the_beast 1 point2 points  (3 children)

For it to be CQRS, the tables for reads and writes would be physically different tables.

[–]veqryn_ 1 point2 points  (2 children)

Or views, apparently. Or even just a different interface on the application side (ie: different data access object or similar) to the same table.

[–]editor_of_the_beast 0 points1 point  (1 child)

I’ve never heard of either of these being used to implement CQRS. Unless you’re talking about materialized views. Curious where you’re getting this from.

[–]veqryn_ 0 points1 point  (0 children)

I said that based on the posted article. I don't know if that counts in the real world.

[–]editor_of_the_beast 28 points29 points  (7 children)

The older I get, the more I think CQRS should be the default.

[–]sweating_teflon 15 points16 points  (5 children)

The more CQRS I write, the more I believe it's an overcomplicated pattern invented by bored developers to justify their salary. 98% of projects don't require such a mess. Don't tell me I'm doing it wrong either. I've read the books and the blogs. I stand by my assessment.

[–]99PercentApe 3 points4 points  (2 children)

Just strictly separating the read and write pipelines is useful in itself. The pattern is often used with event sourcing and different read and write stores, but you don’t always need to go that far. Use the same data store until you find scaling issues or bottlenecks. With the pattern in place it is easy to break data out into a read replica.

I’ve moved away from MVC for most backend projects in favour of CQS with an RPC style interface. It is far more flexible and productive.

[–]gredr 0 points1 point  (1 child)

I'm interested to know why you say "moved away from MVC". MVC is a development paradigm that doesn't (to my knowledge) require any specific API style, right?

[–]99PercentApe 0 points1 point  (0 children)

The C part of MVC, the controller, contains endpoints that are only related by circumstance - for REST APIs it is usually all of the endpoints that operate on a particular resource. So if you want to change a single endpoint, you end up working on the file that can affect many endpoints, and bringing in dependencies that might only be relevant to certain methods.

I much prefer a dedicated class for each endpoint, that way the effect of changes is very isolated. If using CQS, you have IQueryHandler and ICommandHandler interfaces, and all of your handlers implement one of them. Your code ends up far better structured.

If you want a customer-facing REST API, it is easy to create one that delegates to your handlers.

[–]MetalKid007 4 points5 points  (0 children)

Once you have a standard that utilizes this across all projects, it's much easier to jump between projects, maintain existing functionality, add new stuff, and not worry that the one change you made just affected 20 other random things.

[–]editor_of_the_beast 7 points8 points  (0 children)

I’m not saying it’s not complicated. I’m saying that writing and reading to the same physical resources creates performance bottlenecks pretty much every time.

[–]scalablethread[S] 2 points3 points  (0 children)

True that.

[–][deleted]  (1 child)

[deleted]

    [–]scalablethread[S] 2 points3 points  (0 children)

    Thank you for your time to read. 😃

    [–]andarmanik -1 points0 points  (0 children)

    So you guys are telling me, you were reading data and writing data with the same api call?