CQRS Doesn't Have To Be Complicated | Clean Architecture, .NET 6

Поделиться
HTML-код
  • Опубликовано: 11 июл 2024
  • Get the source code for this video for FREE → the-dotnet-weekly.ck.page/cqrs
    ☄️ 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 a popular design pattern. In this video, I will show you how to implement CQRS using the MediatR library. We are going to create custom Command and Query abstractions, to make our CQRS implementation more explicit.
    Join my weekly .NET newsletter:
    www.milanjovanovic.tech
    Subscribe for more:
    ruclips.net/user/MilanJovano...
    Chapters
    0:00 What is CQRS?
    3:00 CQRS With MediatR
    5:27 Explicit Command Abstraction
    9:47 Configuring MediatR
    11:20 CQRS Command Side in Action
    14:59 Explicit Query Abstraction
    21:10 CQRS Query Side in Action
    23:27 Awesome Outro
  • НаукаНаука

Комментарии • 217

  • @MilanJovanovicTech
    @MilanJovanovicTech  Год назад +10

    Master the Clean Architecture: bit.ly/3PupkOJ

    • @seanleith5312
      @seanleith5312 8 месяцев назад

      My question is why "CQRS"?, those four letter should not be together in any meaningful way, why is that stupid name?

    • @MilanJovanovicTech
      @MilanJovanovicTech  8 месяцев назад

      @@seanleith5312 check this out: www.milanjovanovic.tech/blog/cqrs-pattern-with-mediatr

  • @danilodjokic5303
    @danilodjokic5303 Год назад +53

    Milan, quick tip. Just write a semicolon after the namespace and it will convert them to file scoped. Also ctrl+R & ctrl+G will get rid of all unused using statements.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +10

      The more you know. 😁 I appreciate the keyboard shortcut tips!

    • @AlmightySimian
      @AlmightySimian Год назад +7

      If you are using VS2022 you can also go into options and set up code cleanup to run on file save, set code cleanup to sort using and cleanup unused and to default the namespace to file scoped. Then you just have to Ctrl+S to apply a whole range of code formatting options that you can configure.

    • @Nk54fr
      @Nk54fr 6 месяцев назад

      Or you edit the snippet from VisualStudio folder and remove usings, set the class public sealed by default (or internal). Now you don't have to clean the file each time you add a new file ;) it's worth the effort so much

  • @rafapioli75
    @rafapioli75 Год назад +9

    Great explanation! I like your way to show this kind of content! Clean and direct to the point. Keep going!

  • @calinmarian2553
    @calinmarian2553 Год назад +3

    Thank you Milan, I really enjoy your videos, you have clear explanations, straight to the point, no useless text that would make me move the video player's progressbar :)) keep going man.

  • @SamFugarino
    @SamFugarino 5 дней назад +1

    This is pretty darn cool. Setup my own project and followed your instructions. I did have to dig through some of the other videos in the series, but I got everything working. I enjoyed your explanation. Your explanation was clear and easy to follow. Thanks for sharing.

  • @joshem32
    @joshem32 Год назад +8

    I totally love these videos, hopefully one day you’ll talk about authentication/authorization!!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +11

      Should be soon Jose. Many people asked for that, so I'll prioritize it higher.

    • @alvedingasal6782
      @alvedingasal6782 Год назад

      Hi Milan,
      Great video 😊 I want to ask you which hosting is best for .NET Core 6?

  • @lexdxw1989
    @lexdxw1989 Год назад +3

    Pretty good! The explanation is very clear. Gave you a like!

  • @majormartintibor
    @majormartintibor Год назад +17

    A separate video on how to actually use cancellation tokens would be nice.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +6

      Sure thing! That topic seemed to spark a lot of debate so I want to cover it in more detail

  • @saqibali7066
    @saqibali7066 Год назад +1

    Hi Milan ,
    Thanks for the series .

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      You can do that on my Patreon: www.patreon.com/milanjovanovic

  • @avecesar
    @avecesar Год назад +1

    Thank you for sharing!

  • @saulolima6874
    @saulolima6874 Год назад +1

    Thanks Milan!

  • @javedahmad9166
    @javedahmad9166 Год назад +2

    Great content!!
    Would love to see how to create abstraction for a query when the response consists of properties coming from different aggregates.

  • @jokersilva09
    @jokersilva09 10 месяцев назад +1

    My first video trying to introduce myself in the CQRS architecture since I need to work with it and got some concepts. Very well-explained, thank you!

  • @silvertek
    @silvertek Год назад +2

    Great Video! I cant get enough of your content. Can you explain a bit more about your Abstraction of Queries? As someone that primarily works with the data and DBA teams, I've seen a lot of different opinions on how queries should be handled to best balance performance, maintainability, and security.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      The most performant approach would be create a small service per query handler, which will fetch only the data that is necessary to satisfy that query.
      For example, let's say I have a GetMemberByIdQuery and respective handler.
      In the video, I'm using a repository.
      An alternative would be to inject an interface like this:
      IGetMemberByIdSession
      Implement this interface in the Persistence project, however we deem fit. We can use EF, we can write a SQL query, etc.
      Does this make sense?

    • @silvertek
      @silvertek Год назад +1

      @@MilanJovanovicTech Makes sense, thanks for the explanation.

    • @pilotboba
      @pilotboba Год назад

      @@MilanJovanovicTech IS that the specification pattern? Or is that something else?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@pilotboba Something else entirely.

    • @ahmadalsader1047
      @ahmadalsader1047 Год назад +1

      @@MilanJovanovicTech so in this scenario if you have a common behavior that you need in more than one handler, is it make a sence to make the IGet MemberByIdSession implementation inside the Persistence project and call it from both handlers?

  •  11 месяцев назад +1

    good job 👌

  • @jamesevans6438
    @jamesevans6438 Год назад +1

    Great video Milan, really enjoying these videos and there is always something I am learning. What are your views on vertical slice architecture in comparison to clean architecture?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Hey James :) Haven't used the vertical slice architecture ever, so I don't have any strong opinion on it. I don't mind it in general. But I prefer a layered architecture (like Clean) with stricter separation between concepts.

  • @IldarIsm
    @IldarIsm Год назад +4

    From start it is not clear why you create interface for query and command.
    It is great that you learn and describe your material as well, it is good fast method.
    I would remove ApiController, additional inheritance, additional object in memory without any gains.
    Your own unit of work is anitpattern. EF already implement UoW in dbcontext object. Separate repository is also additional noise. Mediatr handler is independent implementation for single action. You make it depends on the common repository, which has many reasons to change. Also in my experience often repositories does not allow to reuse some logic. In different place the same repository action may require different transaction scopes, which repository does not address any way.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +5

      Query/Command interfaces make it more explicit and expressive than using IRequest for representing both concepts.
      In the end, it's the same thing underneath, I just prefer having a little bit more expresiveness.

  • @Dragonite594
    @Dragonite594 Год назад +3

    I prefer to write my own solution for cqrs instead of making abstraction for mediatr it's not so hard. I prefer stick query handlers close to dbcontext instead of inject repository there. I like your videos, Keep going! :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      What is the added value you get out of implementing it yourself?

    • @RealDieselMeister
      @RealDieselMeister Год назад +4

      Because it is simple to implement and relying on a package for simple implementations can be a problem for long running solutions.
      Watch the origin of CQRS there was never need of some library or framework, because it is trivial to implement.

  • @SPribyt
    @SPribyt 4 месяца назад

    Good job!

  • @jmiltons
    @jmiltons Год назад +2

    Hi! Thanks for your work. I stumbled upon your channel and I'm enjoying. A suggestion: at the beginning you mentioned (3:06) you've already implemented the CQRS (kinda) but it is difficult to find where. I'd love to see since the beginning and it's not easy to find some order in youtube. I've taken a look in the playlist but it's not clear. I'll watch all your videos but it would be great if you have some list (maybe in video description) ordered, it'll be easier for new subscribers.
    Thanks a lot!!!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Keep in mind that I recorded this in my first month on RUclips, so I was pretty bad at giving out value 😅

  • @sikelelankohla1499
    @sikelelankohla1499 Год назад +1

    Thank you Milan, this rally was a great video! How would the MediatR handle the same command used in two different command handlers ?

  • @goorn4489
    @goorn4489 Год назад +1

    Hi Milan, thanks for great videos! Do you use Services along with CQRS handlers for applying reusable logic ?

  • @Sentrify
    @Sentrify 9 месяцев назад +2

    Hey Milan, thanks for this great demonstration of CQRS. Could I maybe suggest a video idea where you show the full variation of the pattern? Including separate APIs, Read/Write models and Read/Write databases? All in combination with Mediator of course within the Clean Architecture & Domain Driven Design. I would be very interested in this. Thanks again for your valuabe content!

    • @MilanJovanovicTech
      @MilanJovanovicTech  9 месяцев назад +2

      Sure, let me make a few hours long course about it 😅

    • @Sentrify
      @Sentrify 9 месяцев назад

      @@MilanJovanovicTech I would 100% watch it but would understand if that's a little too much 😂

  • @AdamWandoch
    @AdamWandoch Год назад +1

    Hi Milan, great content! How did you implement the Result class in this scenario ??

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      IsSuccess: bool
      IsFailure: bool => !IsSuccess
      Error: (string code, string name)
      And a few helper static methods for creating a Success/Failure result

  • @tomislavpapic3158
    @tomislavpapic3158 Год назад +1

    Hi Milan thanks for great series of NET tutorials :) I have a question about, what if you have some logic that multiple commands or queries uses in theirs handlers. For example some command update user and some other command have some business logic and update user as well(this is simple example but just think if business logic is more complex than this). How to organize that logic to avoid DRY principle?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Put it in a domain service perhaps? That would be one way to handle it

  • @Ruslan.Galiev
    @Ruslan.Galiev Год назад +1

    Great video Milan! What could you suggest if I want to return 404 Not Found responses instead of 400 Bad Request for cases when an entity not found?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Nothing is stopping you from returning NotFound in the controller

  • @alvedingasal6782
    @alvedingasal6782 Год назад +1

    Hi Milan,
    Great video 😊 I want to ask you which hosting is best for .NET Core 6?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      There isn't a "best", but Azure is probably easiest to set up

  • @newenglish4109
    @newenglish4109 Год назад +1

    Thanks for your great video... I have a question
    Why you didnt use mediatr' sender in your controller for run your commands and queries?
    You could inject mediater into your controller and use it

  • @adisilagy
    @adisilagy Год назад +2

    Milan,
    Does this pattern always define one handler for each single query or could you have a single interface that define several queries / command and one handler to handle them all?
    For example, let's say we have two queries related to the same domain entity. GetOrderById and GetOrderByMember does each will have a respective handler or can we implement one handler for both?
    Thanks in advance!
    Your videos are great and you explain it really well!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      It isn't a problem to handle multiple queries with the same class. I just stick to one query - one handler, by convention

    • @IldarIsm
      @IldarIsm Год назад

      you have to register handler for each query command. They have different logic and should have dif implementation. I do not understand what really you mean
      to update logic you change one handler file.

  • @gligom
    @gligom Год назад +2

    All this Clean, Super Clean architectural software design approach and this kind of video doesn’t say ‘You don’t need this huge complexity for your small to medium projects, or if you are a beginner’. If you just start your journey to software development road, skip this kind of ‘tutorials’ you will be overwhelmed and your brain will hurt. Just read the official documentation, make small applications, take your time. All this design principles and patterns are just tools that you will need on some, not all, projects. For small and medium projects this kind off approach will over complicate things. Trust me, you can build useful apps that don’t use any of this, keep them simple, stupid 🤪. I don’t say that this are not good at all, in big projects with big teams are a must, otherwise chaos will happen 😇. With time you will learn and figure out what and when you need to use it in order to accomplish your goal. Start small, learn the basics and build up your experience, otherwise you will lose precious time.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Let the viewers decide. I like to think everyone has some common sense in them to be able to critically view some concepts, and figure out if they make sense for them or not.

    • @gligom
      @gligom Год назад +1

      When giving an advice based on 30 years of experience has become a bad idea? What I said is my opinion and you, like a professional must respect other’s opinions. I didn’t said that everyone need to follow my advice or agree with me. I think that who wants to become a developer has the brain to decide. Please be open for debates, if don’t close the comments for your videos 🤓

  • @relaxlifeentertainment1282
    @relaxlifeentertainment1282 8 месяцев назад

    Nice work and can you share with what is the theme you are using in visual Studio IDE?
    Thanks

  • @10199able
    @10199able Год назад +4

    I understand all of this, except - whats the point of CQRS from the software point of view, if you have only one storage anyway. It worked without ICommand/IQuery interfaces & handlers just as well. I recently spent some hours debugging our current project, where for some reason data was not saved to dabatase, only to found that I inherited some service from the wrong interface (naming in our project could be better...).

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +3

      The point is logical splitting of reads/writes.
      The additional abstractions are to be more explicit.
      As for debugging, I agree on that one.

  • @PK-wb6cs
    @PK-wb6cs Год назад +1

    Hi Milan, what do you think about combining Domain models, DTOs and AutoMapper in request handlers in cqrs? Does it break pattern?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      In the same physical file? I think it's fine

    • @PK-wb6cs
      @PK-wb6cs Год назад

      @@MilanJovanovicTech domain models in Core project, commands and queries in Application project, Handlers, DTOs and mapper in Infrastructure. I've seen this approach couple times and I wonder if it's clean from solution perspective.

  • @yaroslavsmoliak5725
    @yaroslavsmoliak5725 Год назад +1

    Would be really interesting to see how to separate the storages to the read and write storage and how to sync them so the command will write to the `write` storage and reads will read from optimized `read` storage.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      You would write to write storage. Publish an event, and write to read storage. You end up with eventual consistency though.

    • @krccmsitp2884
      @krccmsitp2884 Год назад

      Separating read model and write model is foremost conceptual. You really need not to have separate implementations or even separate physical data storages-unless there is a requirement to do so. #yagni

  • @mptechie
    @mptechie Год назад +1

    I like my Controllers to be dumb and short, so the requests they receive are the command parameter (or command itself) and the return is an AutoMapper mapped Dto.
    Every API endpoint boils down to a one-liner like
    return Mapper.Map(commandRequest)
    Which can, arguably, also be simplified with a generic type...
    Then I use Swagger/OpenAPI to generate the TypeScript models for the frontend to strongly-type glue the two layers together.
    One drawback of this approach is modifications towards the frontend; builds will fail if the commands/queries signature changes are not adapted on the UI layer, which is also a benefit as you deny yourself space for error, mistakes are immediately apparent, there's no space for deprecations and the client will only receive a working build with no compile errors.

    • @mptechie
      @mptechie Год назад

      Disclaimer: I wrote this in the middle of watching the video, seems like we have roughly the same approach, nice work!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Mine is a bit more verbose, but it boils down to the same thing 😁

  • @AboutCleanCode
    @AboutCleanCode Год назад +2

    I wonder which benefit you actually see here using MediatR (or messaging in general) over directly calling APIs on some interface? As you use specific command, query and response objects the coupling between the controller and the handler would be similar from my perspective. I would be interested in your arguments for this design choice. thx!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Just a means to an end. It's simple to implement CQRS with it.

    • @andrewshirley9240
      @andrewshirley9240 Год назад +4

      @@MilanJovanovicTech It is also simple to implement CQRS without it. But hey, with mediatR we also get to lose IDE navigation on our function calls, yay!

  • @evilTano
    @evilTano Год назад +2

    Good video as always. I just I've a question about the queries when they are simple. For example, in your case, you write an entire Command and CommandHandler just to do a simple GetById query. I know this is just an example, but to me, it's an overkill. So if I've do something like this in my code, usually, I inject directly the repository interface into my Controller and I execute the Query. I know this force you to make a dependency from the controller to the repository interface, but is a little cost to pay to don't write a lot of code for a simple GetById Query.
    It's just my opinion of course, but I want to know what do you think. As always, thank you for your videos! :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Indeed, you end up with a lot of files with my approach. This is something I'm used to and I don't mind it

  • @amonra655
    @amonra655 Год назад +1

    Thanks for amazing video please can create example for singlr or hangfire in clean architecture I’m confused where we can put real time communication logic and again thanks very much ❤

  • @ChristianAltamiranoAyala
    @ChristianAltamiranoAyala 4 месяца назад +1

    Hi Milan, how do you handle exceptions when using mediatr?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Pipeline behavior, global handler, at the call site...

  • @AbdllahBinHatheemAliGeeSuth
    @AbdllahBinHatheemAliGeeSuth Год назад +1

    Thank you Milan,
    what the call name of library scan and register service?

  • @lukassinkus6162
    @lukassinkus6162 Год назад +1

    How do you handle error responses? Do you have a generic pipeline that wraps every mediator request with a try-catch and returns a Response.Failure() on error, or would each handler do that inside?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Take a look at today's video!

    • @lukassinkus6162
      @lukassinkus6162 Год назад +1

      @@MilanJovanovicTech Nice one, but I meant if an error happens inside of the handler that is not caught by validating the requests, i.e. an uncaught exception

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@lukassinkus6162 I see. So I actually recorded that for a video, but it's not ready yet.
      I create a middleware with a try-catch block, and handle exceptions there. I return a ProblemDetails to the API consumer.

  • @eliesamyoun1006
    @eliesamyoun1006 4 месяца назад

    Hello Milan. I'm struggling to implement a many to many relationship while adopting CQRS and MediatR in Clean Architecture. Do you have any video or reference? Including EF core migrations and Edit/Add/Delete commands.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Which part are you struggling with?

    • @eliesamyoun1006
      @eliesamyoun1006 4 месяца назад

      Thanks for the Reply Milan. My implementation of the domain entity models is wrong i think. And i also can't figure out how to update and delete some of the third many to many entity using command handlers.

  • @vagnerwentz1189
    @vagnerwentz1189 Год назад +1

    Why did you use as sealed record the Commands? Could you explain about it?

  • @richardhaughton9633
    @richardhaughton9633 Год назад +2

    Hi Thanks for this great information. Where is the Result Class coming From?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I implemented it myself. I show it in the Domain Validation video

    • @richardhaughton9633
      @richardhaughton9633 Год назад

      @@MilanJovanovicTech Thanks Milan! Great work

    • @richardhaughton9633
      @richardhaughton9633 Год назад

      What do you think about the ErrorOr library for domain/application flow control?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@richardhaughton9633 It's nice, but I prefer rolling my own

  • @aliakseiavechkin2794
    @aliakseiavechkin2794 8 месяцев назад

    @Milan Jovanović, I know this is a pretty old video, but can you please elaborate on how would you retrieve data in your command? Let's say before create a member you have first to retrieve some related data from DB. Will you create a query object inside you command handler? If yes - doesn't this break the idea of CQRS? My question is: sometimes we need to get data in order to write data. Where we should do this according CQRS pattern? Thank you in advance

    • @MilanJovanovicTech
      @MilanJovanovicTech  8 месяцев назад +1

      I would just expose a method on the repository and use it to get the required data.

  • @nicolaiblas
    @nicolaiblas Год назад +1

    Milan, what VS theme are you using?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      ReSharper dark theme

    • @nicolaiblas
      @nicolaiblas Год назад

      @@MilanJovanovicTech Thanks a ton! Love your videos, keep up the good work!

  • @TheTeladras
    @TheTeladras Год назад +2

    If you provided a github link to the repo, of this example, that would be super awesome.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      You can find something similar on my GitHub. Otherwise, I share the code with my Patreon supporters

  • @shunz5677
    @shunz5677 Год назад +1

    I have a question. Can I use DTO object (passing from controller) as a Command parameter? I have dto object in Post Controller (create-new) with a lot of parameters, so creating command with these primitive parameters again then mapping it and then mapping it again in command handler to domain entity is a lot of..mapping and repeating code. I have read in stackoverflow that it is ok solution to encapsulate DTO object in Command (so take it as a argument in constructor) if dto/request has a lot of properties. And then you can mapping it only once in handler - from dto to domain entity.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Sure. But then you're coupling your API contract and your command. If you're fine with that, go ahead.

    • @shunz5677
      @shunz5677 Год назад

      @@MilanJovanovicTech It is tricky form me. I saw a lot of projects and tutorials and usually they had:
      1) Command as a controller action argument that is directly passed to a handler. So Command is a DTO in an API basically.
      2) DTO as a controller action argument (like in common api without cqrs) that is mapped manually in a controller body to a command. It can become ugly if we have object with a lot of properties.
      3) DTO as a controller action argument that is passed directly to a new command. So Command is some kind of encapsulation to this api object/DTO. It is approach I asked about earlier.
      What is the best approach in your opinion? Because I cant find one good or a "book" solution and I like your ideas :)

  • @nicathidayetzade1482
    @nicathidayetzade1482 Год назад +1

    Is it really important to implement unitOfWork or is it up to developer? By the way, I use dbContext directly in my projects.

  • @aktierportfolio1365
    @aktierportfolio1365 Год назад +3

    Difficult to see the order of your videos and where to start.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I do have a few playlists that follow the topic in a somewhat chronological order

  • @marcelotieghi936
    @marcelotieghi936 7 месяцев назад +1

    I am new on this, you named that folder as "Messaging" because it return a message? Like "Member added with success".

  • @winstochurgle9133
    @winstochurgle9133 Год назад +2

    What is inside the "Result" class? Would you show us?

  • @juanitotomasjr.389
    @juanitotomasjr.389 Год назад +1

    Hello Sir, you are a very good instructor. Is there a way to download the source code of Gatherly?

  • @TienHoang-ui4zj
    @TienHoang-ui4zj 2 месяца назад

    Hello, Do we need to sealed when creating a record? because as far as I know the records are implicitly sealed by default

    • @MilanJovanovicTech
      @MilanJovanovicTech  2 месяца назад

      Are they implicitly sealed? 🤔

    • @TienHoang-ui4zj
      @TienHoang-ui4zj 2 месяца назад

      @@MilanJovanovicTech My bad, I was confused. Thanks for your great video 😄

  • @MDM666666
    @MDM666666 Год назад +1

    Which theme are you using in VS?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      ReSharper dark theme

    • @MDM666666
      @MDM666666 Год назад

      @@MilanJovanovicTech Thanks can I download this without buying ReSharper?

  • @achrafaffes1588
    @achrafaffes1588 Год назад +2

    good but CQRS is it CORS or other concept ? and thanks

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      CQRS = Command Query Responsibility Segregation
      CORS = Cross-Origin Resource Sharing

  • @mohameddabbour6961
    @mohameddabbour6961 Год назад +2

    great video ❤ is there link to git repo to explore the code?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Hi Mohamed, the source code is only available for my Patreons. However, you can find something similar here:
      github.com/m-jovanovic/event-reminder

    • @saqibali7066
      @saqibali7066 Год назад

      @@MilanJovanovicTech how to signup for to be a patreon ? 🙂

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@saqibali7066 You can do it here: www.patreon.com/milanjovanovic

  • @fabiosantos1679
    @fabiosantos1679 Год назад +1

    Milan jovanovic is possible to share videos regarding persistance project?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      Talk about that project more?

    • @fabiosantos1679
      @fabiosantos1679 Год назад

      @@MilanJovanovicTech
      Hi, yes, how did you full structure that persistance project. And, for example if you have to handle multiple databases sources how we can deal that in that project
      The best approach to handle the persistance

  • @dvsys751
    @dvsys751 3 месяца назад

    Hi, what theme u use in vs ?

  • @anton_roos
    @anton_roos Год назад +1

    Is it a bad idea or bad practice to not use Unit of Work?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I don't think it's a bad idea. It just makes everything a little easier

  • @Shanks-at-Work
    @Shanks-at-Work 5 месяцев назад

    Do you have a video where you teach approaches from scratch (as in from empty solution/project) ?
    This one is more like a part of follow-on series where you're just tweaking a bit inside an already completed full-fledged project.

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад +1

      This series is as close as it gets: ruclips.net/video/fe4iuaoxGbA/видео.html
      Otherwise, there's my PCA course

    • @Shanks-at-Work
      @Shanks-at-Work 4 месяца назад

      @@MilanJovanovicTech awesome, thanks Milan. Keep rocking.

  • @pieceofcode_
    @pieceofcode_ Год назад +1

    Hi I'm getting error at line: in .NET 6 Program.cs file
    builder.Services.AddMediatR(typeof(Program));
    Error:
    CS1503 Argument 2: cannot convert from 'System.Type' to 'System.Action'

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Maybe using a newer version of MediatR?

    • @pieceofcode_
      @pieceofcode_ Год назад

      @@MilanJovanovicTech yeah that’s a newer version I think 12

  • @Tamer_Ali
    @Tamer_Ali Год назад +1

    Hi Milan,
    When to use Repository Pattern over CQRS in Clean Architecture? because the first time I tried to use CQRS with in a simple app it was a bit slower
    CQRS with EF core or Dapper for enterprise app?

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      You can use whatever you like. There aren't any rules written in stone.

  • @ManuraSiriwardena
    @ManuraSiriwardena 3 месяца назад

    Is there any tutorials with multiple databases for read and write

  • @Nazaro4ka
    @Nazaro4ka 8 месяцев назад +1

    What the use of cancellation token?

  • @simbatwiga9631
    @simbatwiga9631 Год назад +1

    Hi Milan how do I go about using mediator pattern with factory pattern where the request body can call different handler at runtime based on some property in the JSON payload

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I can imagine a solution where you have a branching logic in IPipelineBehavior. But this make sense if this is a common use case in your project.
      Otherwise, just use if-else in your handler, and decide what to call

    • @simbatwiga9631
      @simbatwiga9631 Год назад

      Hi @@MilanJovanovicTech the if else logic will reside in the controller to call which request command?

  • @user-cr8ez3ig8q
    @user-cr8ez3ig8q 4 месяца назад

    I see you are creating all dependency with scoped lifetime. Is it recommended?

    • @MilanJovanovicTech
      @MilanJovanovicTech  4 месяца назад

      Not all, just the ones that have scoped dependencies

  • @faridmurshudov5796
    @faridmurshudov5796 Год назад +1

    Your videos are really great to learn. But please talk a little loud :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Honestly, you are one of the few people complaining. Are you sure the video volume is low? 🤔

  • @anthony2584
    @anthony2584 Год назад +1

    Are you in Udemy?? Cause I need to have your lessons!!

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      No, I'll create my own website for courses very soon. I'll keep you posted :)

  • @TheJabukasiga
    @TheJabukasiga Месяц назад

    What's the point of splitting each operation to new command/query handler when you put some logic services to avoid code duplication and that services are now containing logic for multiple purpose?
    So instead of having one repository, we have one service and multiple handlers

    • @MilanJovanovicTech
      @MilanJovanovicTech  Месяц назад

      So you want to replace a repository with a service?

    • @TheJabukasiga
      @TheJabukasiga Месяц назад

      ​@@MilanJovanovicTech In most use cases you have for example:
      Order service which is responsible for all the logic related with orders
      Order repository which is injected into service and is responsible for all save/read operations related with orders.
      And how I understand cqrs (in case where we have one db and not using cqrs for performance gains)
      You split all operations from OrderService into seperate Handlers and that makes sens in case of SOLID principle and SOC.
      But thenm, when you take some code back from those handlers and put back into one common service (what's the point of that?)
      It's true that you still have command/query separation but also break solid rule with that one service full of logic from that handlers).
      Also creating OrderRepository and injecting into handlers breaks all above rules, what's the benefit of that repository instead of injecting dbcontext or connection (in case of dapper) and executing queries from handler?
      I know that main reason for IRepository usage is the case when in future you would like to switch to other db provider but in real life it almost never happen.

  • @desaihiren2009
    @desaihiren2009 6 месяцев назад +1

    Great Video!
    But I think it's CQS and not CQRS (CQS != CQRS) :)

  • @surendrakumar-mk7sr
    @surendrakumar-mk7sr 9 месяцев назад

    Great presentation. Noticed when switching b/w the files, it's not highlighted the appropriate file in the solution explorer. Pls enable that option so that it'll be helpful to see what folders / files you are navigating.

  • @reggyA898
    @reggyA898 Год назад +1

    lol World of Warcaft

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      What's so funny?
      Classic WotLK, reliving my childhood. 😂

    • @reggyA898
      @reggyA898 Год назад

      @@MilanJovanovicTech I just finish arena session in wow, open my browser and seen ur video so funny coincident. Also in love with CQRS :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@reggyA898 I'm happy that we have similar interests. You'll love this channel for sure 😁

  • @malignate4sd
    @malignate4sd Год назад +1

    Just having a handler is not CQRS. You just created an abstraction over a function call.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Is creating a handler the only thing I did here? What else am I missing? 😅

    • @malignate4sd
      @malignate4sd Год назад +1

      ​@@MilanJovanovicTech Thanks for the fast response. The idea of CQRS is to use different models or storage for your read and your write side, but you never showed that. It is the idea that "Normalized models are great for writing and consistency but slow for reading due to joins", or that "Event sourcing is great for writing but almost impossible for reading". All the handlers and so on are nice to have if you want to have a consistent pattern for your whole application, but not necessary. The interesting part would be in the repository. I could even implement CQRS with the repository and the controllers only.

  • @muhammeteser9571
    @muhammeteser9571 Год назад +1

    Currently not paying I guess there is a technical problem. It say ,We’re sorry, but we were unable to add your card at this time. Please try again.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      You're talking about Patreon?

    • @muhammeteser9571
      @muhammeteser9571 Год назад

      @@MilanJovanovicTech YES

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      @@muhammeteser9571 If you plan to be a Patron long term, I suggest waiting a few days. Since it charges for the whole month upfront, and October is in a few days :)

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      This'll save you some money, and give you access to Patreon longee

  • @zikkrype
    @zikkrype Год назад +1

    I hate Unit return type in MediatR. It's so useless for most of the people. I can't see normal reason why author decided to return this instead Task. You need to write your own command bus wrapper to change remove this Task and make it just Task (e.g: public async Task HandleAsync(SomeRequest request, CancellationToken cancellationToken))

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      I'm sure he had his reasons

    • @zikkrype
      @zikkrype Год назад

      @@MilanJovanovicTech used for some kind of synchronization and because method MUST return something otherwise someone will die

  • @fullstackdotnet
    @fullstackdotnet Год назад +2

    A controversial comment... 😂

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      Don't be that controversial 🤣

    • @pilotboba
      @pilotboba Год назад +1

      I fully disagree.
      It's not controversial at all. :)

  • @NiamaImia
    @NiamaImia Год назад +2

    No-no-no. CQRS MUST be complicated. Plus you need DDD there for sure. Just for you to feel the pain and senseless of being. The more layers beetwen controller and DB you have the better. No matter if you actually reuse them somehow or not. Better if not. Just to charge your customer with more development costs and put release to eternity, because every time you need to go to all the 15 layers spread between 15 projects and make the same changes everywhere, and it takes 15x time-money. And then finally suddely your CQRS ends up with the same database and the same DbContext for reads and for writes which makes everything useless. (There is nothing more eternal than something temporal). Looks great (may be), but it is very annoying. Having worked for 3+ years for a such greenfield solution feel very tired now of words "CQRS" and "DDD". Very verbous, very routine, comes to spaghetti often, you need to be very scrupulous and attentive to not to forget smth smwhr, as the more moving parts the more probability of fail. The joy of simplicity and moving forward fast is definetely not about CQRS.

  • @ZhangJay
    @ZhangJay Год назад +1

    What's the benefit of CQRS?
    why it just make things complicated

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад

      The benefit is logical separation of reads and writes. Allowing you to model each of them differently.

    • @ZhangJay
      @ZhangJay Год назад

      @@MilanJovanovicTech Thanks for the replying.
      but Without CQRS, we can easily to seperate READ and WRITE as well, right?

  • @ed-ou812
    @ed-ou812 Год назад +1

    CSRQ … research that … cdbc related

  • @finickyflame
    @finickyflame Год назад +3

    I'm sorry, but you didn't exactly showed CQRS, you just showed how to use MediatR to a project that already had a repository pattern.
    The power of CQRS is to be able to separate the Read from the Write so they can use different storage mediums, different read write techniques (e.g using DbContext on the Read, and Repository on the write) to give more flexibility and to decouple them from the same implementation.
    Juniors or people trying to understand the subject will yet again find another tutorial that mixes CQRS and MediatR together, when they should not.

    • @MilanJovanovicTech
      @MilanJovanovicTech  Год назад +1

      CQRS isn't about having separate datastores at all, in my opinion. It's about thinking differently about reads and writes by separating them completely. How you go about implementing the actual read/write logic is completely up to you.

    • @finickyflame
      @finickyflame Год назад +1

      @@MilanJovanovicTech You are right and that's why I said it's "to be able to" do the separation. One of my concerns is more towards the title of your tutorial - it emphasize that it's not complicated to do CQRS and the content is about MediatR.
      CQRS could be as easy as simply having 2 classes, one for read and one for write without even using MediatR.
      Even so, one of the reason why the pattern could be considered complex is because you have to separate your read and your write, which greatly increase the number of components in your solution. It's something that should be taught to not be used everywhere, only needed by the architecture - otherwise you're just overcomplicating your solution for no benefits.

  • @deedev7279
    @deedev7279 Год назад +1

    MediatR is bad performance

  • @alexeybeloushko7240
    @alexeybeloushko7240 25 дней назад

    never return any objects form command or queries. it creates coupled code. command has to be interface. !CommandHandler where TRetType : class
    handle(T command, [NotNull] TRetType retval)
    {
    //retval.SomeDataMember=SomeValue;
    }
    now you can chain it, you dont need binders to DataContract. Reverse of controll

    • @MilanJovanovicTech
      @MilanJovanovicTech  25 дней назад

      Never seen this approach used in practice. Doesn't it make way for very strange code?

    • @alexeybeloushko7240
      @alexeybeloushko7240 25 дней назад

      @@MilanJovanovicTech it makes it functional. you can path mock objects etc. very usefull in testing. and return type can be errorsClasses or simple bool. Imagine decorated Command that holds 2 or more commands. if you return value or pass non polymorph operand, you have to create binder to project retval1 on retval2.

    • @alexeybeloushko7240
      @alexeybeloushko7240 25 дней назад

      @@MilanJovanovicTech Patterns, Principles, and
      Practices of Domain-Driven Design by Scott Millett page 701-704.