Discovering The Truth About CQRS - No MediatR Required

Поділитися
Вставка
  • Опубліковано 5 чер 2024
  • ☄️ Master the Modular Monolith Architecture: bit.ly/3SXlzSt
    📌 Accelerate your Clean Architecture skills: bit.ly/3PupkOJ
    🚀 Support me on Patreon to access the source code: / milanjovanovic
    CQRS, which stands for Command Query Responsibility Segregation, is the most misunderstood pattern.
    You don't need any of these things:
    - Separate read and write databases
    - Event sourcing
    - MediatR
    In this video, I'll show you why CQRS is really simple and how to implement it straightforwardly.
    Join my weekly .NET newsletter:
    www.milanjovanovic.tech
    Read my Blog here:
    www.milanjovanovic.tech/blog
    Subscribe for more:
    / @milanjovanovictech
    Chapters
    0:00 In-Memory Repository + API
    2:07 Before CQRS, there was just CQS
    3:54 What CQRS actually is
    8:03 Granular CQRS (without MediatR)
  • Наука та технологія

КОМЕНТАРІ • 105

  • @MilanJovanovicTech
    @MilanJovanovicTech  Рік тому +3

    If you want to learn more about .NET and software architecture, consider subscribing to my newsletter.
    → Join 22,000+ engineers here: www.milanjovanovic.tech/

  • @jensingels5958
    @jensingels5958 7 місяців тому +10

    Thank you for the video. I would like to clarify the concept of CQRS (Command Query Responsibility Segregation) to address your points more clearly.
    CQRS is indeed about segregating the responsibilities of handling commands and queries, but it doesn't necessarily mandate a strict separation of only commands handling state changes and queries returning data. The core principle is to recognize that commands (actions that change the system's state) and queries (actions that retrieve data without altering the state) have different requirements and should be treated differently.
    In the context of CQRS:
    Commands typically represent operations that modify the system's state, such as creating, updating, or deleting records. These operations may or may not return an ID or other information depending on the specific use case or system design. It's perfectly acceptable to return data if it's necessary for the application's requirements, and CQRS doesn't forbid this.
    Queries, on the other hand, focus on retrieving data from the system without altering it. They are responsible for providing information to the client.
    In summary, CQRS encourages separating the handling of commands and queries because they have different purposes and requirements, but it doesn't strictly enforce a specific way of returning data or IDs. The exact design and implementation can vary depending on the application's needs and architecture.

  • @gianlucalocri
    @gianlucalocri Рік тому +5

    You know what Milan?
    Today I took the step. You are officially my first ever patreon creator I'm subscribed to.
    Your videos are very useful to me so I decided to support your work!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому +1

      Thank you so much, Gianluca! I'll make sure to keep the video quality & code to a high standard. 🚀

  • @MizenDaCat
    @MizenDaCat Рік тому +5

    Thank you! This is exactly what I've been thinkning while researching the subject for a new project. Every article on Vertical Slice Architecture or CQRS always starts out with Mediatr. But I don't need all of that extra depedency or complexity in this particular project.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому +3

      I hope this'll help people seeking salvation from MediatR 😁

  • @lucasfelixcarvalho2057
    @lucasfelixcarvalho2057 Рік тому +4

    Awesome! .. we as (new) developers sometimes forget that libraries and packages just put something fancy and complicate things around some concept that was written on books many years ago

  • @justtomi-qp8qj
    @justtomi-qp8qj Рік тому +11

    Thanks for sharing! Nice to you took a step further and implemented commands and queries in Mediatr fashion.
    What I am missing here, and most of the videos talking about CQRS is WHY. Why do we even have CQRS, why separation, and it's not due to single responsibility principle - that's just an outcome.
    Having separated Writes / Reads is also WHAT but not Why. So I would appreciate if you're willing to explain that :)
    Thanks again, informative!

    • @arunnair7584
      @arunnair7584 Рік тому +2

      I use it because it results in smaller and simpler classes, and design. Additionally queries can bypass the business/domain layer. Before the advent of RESTful API (which I abhor for various reasons), for every package or DLL I used to have 2 facades - one for writing, one for reading. Separated Writes / Reads makes you use separate databases - one that is optimized for writing (SQL and NOSQL are an option) and another for querying. You querying DB design, avoids all types of joins, as it is designed for a simple select query.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      Then you should understand why separation of concerns/single responsibility is good, I'd start there

    • @vincentcifello4435
      @vincentcifello4435 Рік тому

      Just Tomi See my attempt at clarification above.

    • @arunnair7584
      @arunnair7584 Рік тому

      @@MilanJovanovicTechI have been SRP since around 1998 or so. 🙂

  • @kodindoyannick5328
    @kodindoyannick5328 3 місяці тому +1

    Awesome content! This come clarify much things in my mind about CQRS. Thank you Milan.

  • @magashkinson
    @magashkinson Рік тому +4

    very often I see that cqrs is understood as simply separating queries and commands, but it's so simple to separate it into a whole architectural pattern. This is often shown through implementation using MediatR, but all such implementations lose the point of separating queries and requests. The bottom line is that queries for obtaining data have completely different requirements and materialized views of the database or a completely different type of database that is synchronized with the database for writing can be used for them. It would be nice if you showed such an example and talked about it

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      That's an optimization that isn't practical for most applications out there, but I am working on showing some of those patterns. I have a video on materialized views coming out soon.

    • @pilotboba
      @pilotboba Рік тому +1

      @@MilanJovanovicTech If you are event sourcing CQRS really comes into its own, since writing events and reading from projections makes the need to separate the models much more obvious.

    • @luc9volts
      @luc9volts 8 місяців тому +1

      Correct. inclusive that very video is missing the point by a lot.

  • @PatricSjoeoe
    @PatricSjoeoe Рік тому +3

    We are using CQRS without MediatR and works very well :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      Are you using a similar approach to this?

    • @PatricSjoeoe
      @PatricSjoeoe Рік тому

      @@MilanJovanovicTech Kind of. We have two interfaces for commandhandler and queryhandler that will be injected when we dispatch the query or command. :)

    • @PatricSjoeoe
      @PatricSjoeoe Рік тому

      We going from data-logic-web design to more vertical slice and using CQRS for communicate between different modules in the service.

  • @microtech2448
    @microtech2448 9 місяців тому +1

    Thanks for the simple and to the point video.

  • @saulolima6874
    @saulolima6874 Рік тому +1

    Thank you! Clearly and precise.

  • @abdulnaserramadan37
    @abdulnaserramadan37 Рік тому +1

    Clearly and simple, Thank you.

  • @majormartintibor
    @majormartintibor Рік тому +1

    Very nice and clear video. Well done.

  • @misaghn8027
    @misaghn8027 6 місяців тому

    You are the best. thank you for this beautiful video.

  • @akjha627
    @akjha627 6 місяців тому

    Thank you first !!!
    Finally! I have been waiting to see a video where it is well explained that CQRS DOESN'T require MediatR like its shown in many YT videos.
    CQRS pattern version where there are separate classes for Read or Write will be most suitable for a majority of projects.
    IMO, CQRS where each of the commands and queries are in their own classes is just an explosion of classes. If somebody disagrees with that, there could be two reasons =>
    1) Either they don't have a lot of queries/commands; in that case you shouldn't even have separate classes in the first place for each query/command.
    2) Or you have a lot of queries/commands, which obviously results in an explosion of classes. Even if you just have CRUD, 10 domain entities will result in 40 staggering classes.
    Having just 2 different classes at most for Read/Write should fulfill the architecture requirement for many projects where Read/Write happens through different DBs.
    Also, if the above is followed, there really is no need for a library like MediatR, unless there are other uses.

    • @MilanJovanovicTech
      @MilanJovanovicTech  6 місяців тому

      MediatR offers more than just splitting commands/queries, that's why I like using it

  • @MrPayTune
    @MrPayTune Рік тому +1

    I enjoy watching your videos are night before going to bed. Thanks for sharing. I have one question, How would you handle a dbcontext that is static?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      When would you have a static DbContext? I think that's a bad idea since it will get bloated over time (the ChangeTracker)

    • @MrPayTune
      @MrPayTune Рік тому

      @@MilanJovanovicTech sorry i forgot the "not" somehow. so not static. Im not crazy enough yet to use a static db :)

  • @noble_mickmick8889
    @noble_mickmick8889 3 місяці тому

    Thank you it's very clear now :)

  • @dannevesdantas
    @dannevesdantas 2 місяці тому +1

    Exceptional explanation!

  • @arunnair7584
    @arunnair7584 Рік тому +2

    I first started using a CQRS like approach around 2007. Never ever felt the need for MediatR.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      I like using MediatR for the other features it has

    • @arunnair7584
      @arunnair7584 Рік тому

      @@MilanJovanovicTech I get why people use it. But most of our CQRS code is generated at compile time and the generated Command dispatcher classes is used to dispatch the commands. The mapping of command to command handlers is done in xml. Code generation saves you a lot of headaches.

    • @pilotboba
      @pilotboba Рік тому

      @@arunnair7584 I believe there is a library that duplicates the functionality of MediatR using code generators. I don't recall the name of it.

    • @arunnair7584
      @arunnair7584 Рік тому

      @@pilotboba I see. I have never come across that. Anyway our apps require sub second response times. And where ever we can find a performance boost, we move the domain logic into stored procedures. This is mainly to squeeze out every bit of performance from the system, We also have a plug and play type of architecture, where new features can be added. The architecture has served us well for more than a decade now.

    • @pilotboba
      @pilotboba Рік тому

      @@arunnair7584 I don't see how this affects perf. Perhaps a small bit if you aren't managing your lifetimes correctly and see a lot of l2 and l3 garbage collection.
      But, more, smaller classes isn't going to cause any noticeable perf issues. I think Nick even covered this in one of his videos.

  • @nikogj9495
    @nikogj9495 Рік тому +4

    The litterature about CQRS motivates the separation of queries and commands because you would eventually need separated *models* from reading and updating the information.
    Yet, I have not found different *models* in your video, you only broke the repository's implementation. Still, there is still only one Book entity that is used in both query and command scenarios.
    I would have liked seeing different models (because for me, that's the most important part) of that Book concept, one model that is used for reading Books, and some other model for updating Books. Once you do that, I don't even think it's necessary to apply CQS at the repository level anymore.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      What does a different model mean for you?

    • @nikogj9495
      @nikogj9495 Рік тому +1

      @@MilanJovanovicTech The way I understood it, is mainly the entities. For example, for querying scenarios, having a BookDisplay, BookSummary, etc.., and for commands, Book.

    • @jamesroot9777
      @jamesroot9777 10 місяців тому

      @@nikogj9495 I think what you're referring to is the response and request models for reading, creating and updating the Book entity.

  • @user-rm4qp3ub1e
    @user-rm4qp3ub1e 5 місяців тому

    Hey, nice video! Just a doubt, is instantiate a "command" class good in these scenarios ? I mean the garbage collector will make his job when the request ended no ? because for me even
    looks clearer than the mediaTr approach that uses reflexion so you can't go to de command directly from the call

    • @MilanJovanovicTech
      @MilanJovanovicTech  5 місяців тому

      No indirection is a benefit

    • @user-rm4qp3ub1e
      @user-rm4qp3ub1e 5 місяців тому

      ​ @MilanJovanovicTech Yep, thats what I wanted to say, I prefer instantiate the class reather than use MeditR, sorry my english is terrible hahaha! keep going with your tutorials!

  • @nove1398
    @nove1398 Рік тому +1

    Very clear explanation at all levels of the CQRS co fusion. Nicely done

  • @Blisstim
    @Blisstim 7 місяців тому

    Let's say I want the command methods to be separatly scalable from the query commands. In that case I would have to create 2 different microservices right?

    • @MilanJovanovicTech
      @MilanJovanovicTech  7 місяців тому +1

      Yup, you'd need to split them into separate services. Most of the time, you probably won't need that.

  • @piusagboola7303
    @piusagboola7303 15 днів тому +1

    🎯 Key Takeaways for quick navigation:
    00:00 *📚 CQRS is not about mediator, separate databases, or event sourcing; it's about command query separation (CQS).*
    02:17 *🔄 Command Query Separation (CQS) emphasizes separating methods for reading (queries) and updating (commands) data in your code.*
    04:06 *🧱 CQRS builds upon CQS by segregating commands and queries into separate classes or objects, enhancing clarity and focus in your codebase.*
    06:51 *🛠️ Separating commands and queries enhances code clarity, enforces the single responsibility principle, and doesn't necessitate separate databases or event sourcing.*
    08:09 *🔧 Extending CQRS involves creating separate objects for each command, further emphasizing logical separation and maintainability.*
    12:44 *📝 CQRS boils down to logically separating objects responsible for writing data from those responsible for reading it, promoting code clarity and maintainability.*
    Made with HARPA AI

  • @faisal3374
    @faisal3374 Рік тому +1

    by creating that much objects (Command) doesn't it effect the performance ?

  • @ala24531
    @ala24531 Рік тому +1

    What other benefits does CQRS add other than separating the code into logical chunks?

    • @iamjashin
      @iamjashin Рік тому +1

      There are two main ones: First it enables you to use different database as a source of data for queries. (although one may argue that you can accomplish the same in service).
      The second (More important one) is that instead of having nasty services with thousands lines of code and dependency injection nightmare we end up with clean classes which obey single responsibility principle

    • @EekChocolate
      @EekChocolate Рік тому

      That IS the benefit. When you have a bunch of complex business logic that needs be performed during the handling of one request, you may want to separate it into its own class to make your source code less of a cluttered mess. Let's use books as an example like Milan did in this video. Say you're running a web app for a large bookstore. When that bookstore has a new book added to its inventory, the staff don't just record it in the database and forget about it. There are a number of other things that can happen when a book is added:
      * the book is added to the "New Books" section of the bookstore's website's front page
      * the book is added to a weekly newsletter publication
      * the book is added to a search engine and recommendations algorithm
      * the book is removed from a list of expected arrivals from an integrated inventory management system
      Et cetera. Et cetera. Like others have said, the CQRS pattern is meant for large, enterprise-scale applications where seemingly simple events can have a ripple effect across the entire application. In those cases, you want to compartmentalize those events and have classes that handle them individually so that you can test them more easily.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      That is the benefit, you just have to wrap your head around it

  • @Tamer_Ali
    @Tamer_Ali Місяць тому

    Should I use Filter instead of PipelineBehavior in this situation?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Місяць тому

      Or regular middleware?

    • @Tamer_Ali
      @Tamer_Ali Місяць тому

      ​@@MilanJovanovicTech what if we want to get the result of the request to do caching for example?

  • @vincentcifello4435
    @vincentcifello4435 Рік тому +1

    “What is the first thing that comes to mind when you hear CQRS?”
    Is this a collaborative domain?

  • @CesarDemi81
    @CesarDemi81 Рік тому +2

    The problem with the sample you created there is that it is extremely simplistic and unrealistic. Any real world app will have some dependencies, like DbContext if using EF Core or a DbConnection for Dapper or any other kind of service the app depends on, so now you will have to inject all of that in the command, which is expected to only hold the data it will provide later to the handler, but also with dependencies that will need to be provided in each of the endpoints. For this you will then create some generic stuff to reuse everywhere so you don't repeat yourself so much, and what you end up with? With another home-made version of MediatR 🙃
    In the end, you can do CQS/CQRS without MediatR, that's correct, but you end up either making your code more cumbersome or you end up reinventing the wheel to make your own (dumbed-down?) version of MediatR... So implementing MediatR is more a convenience/advantage than a requirement.

    • @thatojadezweni
      @thatojadezweni Рік тому +1

      not necessarily. a route that one can take it to treat the various commands/queries as standalone services in a sense and inject the command/query in each endpoint then simply inherit from some “IUseCase”-like interface and just register each “service” that implements it via DI with a bit of reflection.

    • @CesarDemi81
      @CesarDemi81 Рік тому

      @@thatojadezweni like I said: reinventing the wheel all over again with your own home-made version...

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому

      You have to use simple examples when explaining concepts, otherwise from my experience, people will completely miss the point.

    • @CesarDemi81
      @CesarDemi81 Рік тому

      @@MilanJovanovicTech I do get that and for the most things that's all right, is just that people later tend to take the simplest samples to the letter and I was just pointing out why in the majority of the real scenarios MediatR is chosen for implementing CQS/CQRS... It was not precisely a critic to your video (which was very nice, btw), but an observation comparing to real life implementations, mainly due to many of the comments oversimplifying this.
      Cheers!

  • @IAmFeO2x
    @IAmFeO2x Рік тому +3

    I‘m sorry to say this, but this video totally misses the point. CQRS is about having different models for querying and manipulating data, and not splitting up data access methods into different Command and Query classes.
    Consider a scenario where a company develops a new product and several departments need to work together in a process which might look like this: marketing identifies that a new product might be a good fit for the market, so they create it. The technical planning team then adds plans on how to build the new thing, production then adds infos about the steps needed and machines to use and how fast it can be produced, the safety department adds infos about regulation in different countries, and management finally reviews and gives green light.
    This complicated process involves several tables in a relational database, and in the different steps of the process, you would have seperate models for accessing these tables for the different domains. You would have several entities accessing the same tables/views. For example, the safety department shouldn't be able to manipulate data for the technical planning team, but it can view the data to be able to do all things concerning regulation.
    This is explicitly expressed in the data access model: if you are using EF Core, you would have dedicated entity classes which only encompass the columns the safety department can actually change, and dedicated read entities containing the columns that the safety department are able to see. That's what CQRS is about.

  • @kinax2
    @kinax2 8 місяців тому +1

    why are you using Guid, isn't long is better?

  • @margosdesarian
    @margosdesarian 4 місяці тому +1

    What a load of old ballcocks - in every system i have built a delete action returns a class that lets the caller know whether it succeeded or failed, and if it failed what the reason was. Because in anything more complicated than a todoitem application there might be many reasons why a delete should be rejected - the idea of returning a void in this case is just dumbing down.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 місяці тому

      There's a difference between "theory" and what actually makes sense in practice

    • @margosdesarian
      @margosdesarian 4 місяці тому

      @@MilanJovanovicTech Yeah - sorry for the flame

  • @vivekkaushik9508
    @vivekkaushik9508 Рік тому +12

    Clearly CQRS is not meant for 70-80% projects out there which are medium to small scale projects. It only adds complexity without much benefits.

    • @booby163
      @booby163 Рік тому +2

      Do you think if it is a simple to medium project we should implement business logic directly in the controllers/endpoints or injecting a service with CQS approach instead?

    • @Andy01010
      @Andy01010 Рік тому +4

      I’d say it’s the other way around. Most apis would benefit from it - separating concerns is always a better default than not. You can knock up a command/query handling via DI very easily, nothing fancy but it will work very very well, no need for a fully featured Mediatr.

    • @nunodecarvalho8658
      @nunodecarvalho8658 Рік тому +1

      CQS and CQRS are good for every project, it’s a choice it’s not a rule or salvation. If you do it just don’t use the wrong tools to make it hard.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Рік тому +3

      The benefits are conceptual, and to me they are worth it

    • @vivekkaushik9508
      @vivekkaushik9508 Рік тому

      @@booby163 It depends. If there is scope of improvement or further development then YES we should be separating concerns however we must not complicate our project from the very beginning with all this DDD, TDD, BDD, CQRS, MediatR etc stuff.

  • @leftjabrighthook
    @leftjabrighthook 2 місяці тому

    Sure simple if you use a static database like you did. That is not REAL life though. No one does that. Lets see how simple this video is if you need to inject services into your handle method. I'm no fan of Mediatr but come on, Mediatr is a lot more simple then the method shown here. Hell, you should have made it more simple and cleaner by making your Handle methods static!

  • @andreisv
    @andreisv 11 місяців тому +1

    Where the create book record stands? It should return the newly created guid at least 🤔

    • @MilanJovanovicTech
      @MilanJovanovicTech  11 місяців тому

      That's okay

    • @andreisv
      @andreisv 11 місяців тому

      @@MilanJovanovicTech could it then return the newly created entity then? We had a debate with one of my coworker on this, I stood for just the guid so the next call would retrieve the full record while he said it is a redundant call

  • @AnythingGodamnit
    @AnythingGodamnit Місяць тому

    No one seems to explain *why* commands can’t return data, nor address the elephant in the room: if they can’t return data, how would your example look if books had autogenerated IDs? If clients send an update command, why should they then need to query to see the results? If the command is long running, how the hell can the client even be informed about progress if it has no handle for the execution? I know you said be pragmatic but that just makes the entire premise of commands never returning data completely bogus. Sorry, a bit frustrated after spending over an hour watching CQRS videos and everyone is just talking about the obvious and showing crazy levels of abstraction to support the pattern but never actually talking about the more interesting questions that arise when applying this principal in the real world.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Місяць тому

      Check the first two sections here: www.milanjovanovic.tech/blog/cqrs-pattern-with-mediatr

    • @MilanJovanovicTech
      @MilanJovanovicTech  Місяць тому

      "This doesn't mean a command can never return a value. A typical example is popping a value from a stack. It returns a value and changes the state of the system. But the intent is what matters here.
      CQS is a principle. You can follow this principle if it makes sense, but be pragmatic."