21

I still remember good old days of repositories. But repositories used to grow ugly with time. Then CQRS got mainstream. They were nice, they were a breath of fresh air. But recently I've been asking myself again and again why don't I keep the logic right in a Controller's Action method (especially in Web Api where action is some kind of command/query handler in itself).

Previously I had a clear answer for that: I do it for testing as it's hard to test Controller with all those unmockable singletons and overall ugly ASP.NET infrastructure. But times have changed and ASP.NET infrastructure classes are much more unit tests friendly nowadays (especially in ASP.NET Core).

Here's a typical WebApi call: command is added and SignalR clients are notified about it:

public void AddClient(string clientName)
{
    using (var dataContext = new DataContext())
    {
        var client = new Client() { Name = clientName };

        dataContext.Clients.Add(client);

        dataContext.SaveChanges();

        GlobalHost.ConnectionManager.GetHubContext<ClientsHub>().ClientWasAdded(client);
    }
}

I can easily unit test/mock it. More over, thanks to OWIN I can setup local WebApi and SignalR servers and make an integration test (and pretty fast by the way).

Recently I felt less and less motivation to create cumbersome Commands/Queries handlers and I tend to keep code in Web Api actions. I make an exception only if logic is repeated or it's really complicated and I want to isolate it. But I'm not sure if I'm doing the right thing here.

What is the most reasonable approach for managing logic in a typical modern ASP.NET application? When is it reasonable to move your code to Commands and Queries handlers? Are there any better patterns?

Update. I found this article about DDD-lite approach. So it seems like my approach of moving complicated parts of code to commands/queries handlers could be called CQRS-lite.

Sklivvz
  • 5,242
  • 19
  • 34
SiberianGuy
  • 4,753
  • 6
  • 34
  • 46
  • 1
    I fail to see how CQRS makes things any more testable / is of any help with singletons/controllers/etc. They are largely unrelated. Also, you're still creating a command in your (counter?)example, which is odd. – guillaume31 Jan 03 '17 at 20:45
  • If you keep your code with infrastructure code (i. e. get client ip address), it's hard to test. If you isolate the logic into a query/command, it's much easier. The code sample was a little bit confusing, so I updated it. – SiberianGuy Jan 04 '17 at 10:18
  • 1
    Queries and commands have no logic. They are dumb DTOs. Then you have your command/query *handlers*, but they are exactly the same as application services, interactors, business services, you name it... in a non-CQRS context. Placing applicative / use case logic in a separate object than the Controller is not a CQRS specific thing. – guillaume31 Jan 04 '17 at 11:10
  • Also, the question of Singleton vs injected dependency is completely orthogonal to CQRS. You could have a Controller calling a CommandHandler singleton that calls a Repository singleton... – guillaume31 Jan 04 '17 at 11:14
  • @guillaume31, my bad. I meant command handlers. Obviously command definition contains no logic. – SiberianGuy Jan 04 '17 at 11:25
  • 1
    @guillaume31, CQRS tends to be more cumbersome than repository/aplication service calls. They usually need 2-3 classes (i. e. query, queryhandler, queryresult), infrastructure, etc. – SiberianGuy Jan 04 '17 at 11:26
  • Unless you're using primitive values everywhere, the 2-3 classes you mention are all needed in a non-CQRS context as well. You need a type for the data that gets in (query = incoming DTO) and one for data that gets out (query result = outgoing DTO). Only thing is, with CQRS the number of classes is doubled (for read and write side) and they have different names. – guillaume31 Jan 04 '17 at 12:54
  • As far as infrastructure, none is required to do vanilla CQRS. – guillaume31 Jan 04 '17 at 12:55

2 Answers2

23

Is CQRS a relatively complicated and costly pattern ? Yes.

Is it over-engineering ? Absolutely not.

In the original article where Martin Fowler talks about CQRS you can see a lot of warnings about not using CQRS where it's not applicable:

Like any pattern, CQRS is useful in some places, but not in others.

CQRS is a significant mental leap for all concerned, so shouldn't be tackled unless the benefit is worth the jump.

While I have come across successful uses of CQRS, so far the majority of cases I've run into have not been so good...

Despite these benefits, you should be very cautious about using CQRS.

...adding CQRS to such a system can add significant complexity.

My emphasis above.

If your application is using CQRS for everything, than it's not CQRS that's over-engineered, it's your application. This is an excellent pattern to solve some specific problems, especially high performance/high volume applications where write concurrency may be a major concern.

And it may not be the entire application, but only a small part of it.

A live example from my work, we employ CQRS in the Order Entry system, where we cannot lose any orders, and we do have spikes where thousands of orders come at the same time from different sources, in some specific hours. CQRS helped us to keep the system alive and responsive, while allowing us to scale well the backend systems to process these orders faster when needed (more backend servers) and slower/cheaper when not needed.

CQRS is perfect for the problem where you have several actors collaborating in the same set of data or writing to the same resource.

Other than that, spreading a pattern made to solve a single problem to all your problems will just create more problems to you.


Some other useful links:

http://udidahan.com/2011/04/22/when-to-avoid-cqrs/

http://codebetter.com/gregyoung/2012/09/09/cqrs-is-not-an-architecture-2/

Machado
  • 4,090
  • 3
  • 25
  • 37
  • 4
    Agree on everything except the "relatively complicated and costly pattern" part. CQRS is just separating reads from writes. You can do it with duct tape, zero middleware/complicated tooling, and keeping the same database as before, for all it cares. – guillaume31 Jan 03 '17 at 21:05
  • @guillaume31, that's a fair point. I think it quite corelates to the idea from the original question: "in Web Api where action is some kind of command/query in itself". But have you seen anyworking CQRS samples (github preferably) that do not use separate classes for commands and queries? That's what I find when I try to see how other people implement CQRS in .NET. – SiberianGuy Jan 04 '17 at 10:20
  • Yes, but it's not overengineering. It's the basic premise of the pattern - separating queries from writes, read model from domain model. – guillaume31 Jan 04 '17 at 13:02
  • The CQRS approach might be over*kill* in a lot of contexts, but that's an entirely different story. It's not overkill because of all the little technical details that you wrongly attribute to CQRS in your Q, it's overkill because you don't always need what CQRS brings you. – guillaume31 Jan 04 '17 at 13:02
  • I think Fowler's point is only valid to a certain point. SQL Views is one type of CQRS and it is with us for ages and no one says they are very complex. Since there are many flavours of CQRS, i.e. event sourcing projections, that might be seen as complex in some areas, whilst can be trivial in another. – Alexey Zimarev Jan 23 '17 at 11:27
6

CQRS is not a one-for-one replacement for Repositories, exactly because it's a complex, costly pattern which is only useful in some cases.

Here's what Udi Dahan has to say in merit:

Most people using CQRS (and Event Sourcing too) shouldn’t have done so.

[...]

I’m sorry to say that most sample application you’ll see online that show CQRS are architecturally wrong. I’d also be extremely wary of frameworks that guide you towards an entity-style aggregate root CQRS model.

(source)

Martin Fowler:

[...] you should be very cautious about using CQRS. Many information systems fit well with the notion of an information base that is updated in the same way that it's read, adding CQRS to such a system can add significant complexity.

(source)

Finally, Greg Young:

CQRS can be called an architectural pattern.

[...]

This is very important to understand most architectural patterns are not good to apply everywhere. If you see architectural patterns in an architectural guideline you probably have a problem

[...]

The largest failure I see from people using event sourcing is that they try to use it everywhere.

(source)

Sklivvz
  • 5,242
  • 19
  • 34