Discovering The Truth About CQRS - No MediatR Required

  Рет қаралды 17,198

Milan Jovanović

Milan Jovanović

Күн бұрын

☄️ 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)

Пікірлер: 107
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
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 ай бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech 7 ай бұрын
Nice summary!
@gianlucalocri
@gianlucalocri Жыл бұрын
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 Жыл бұрын
Thank you so much, Gianluca! I'll make sure to keep the video quality & code to a high standard. 🚀
@MizenDaCat
@MizenDaCat Жыл бұрын
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 Жыл бұрын
I hope this'll help people seeking salvation from MediatR 😁
@lucasfelixcarvalho2057
@lucasfelixcarvalho2057 Жыл бұрын
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
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
I still think MediatR is great!
@kodindoyannick5328
@kodindoyannick5328 3 ай бұрын
Awesome content! This come clarify much things in my mind about CQRS. Thank you Milan.
@MilanJovanovicTech
@MilanJovanovicTech 3 ай бұрын
Glad it was helpful!
@justtomi-qp8qj
@justtomi-qp8qj Жыл бұрын
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 Жыл бұрын
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. 🙂
@microtech2448
@microtech2448 10 ай бұрын
Thanks for the simple and to the point video.
@MilanJovanovicTech
@MilanJovanovicTech 10 ай бұрын
Glad it was helpful!
@TrockeyTrockey
@TrockeyTrockey 4 күн бұрын
One note: for CQS, you usually could have different model for Reading and Writing. In your sample, the model was the same: Book. In real life, you usually need to have different model, like for read cold be BookWithDetails (example to carry, number of books available at store)
@MilanJovanovicTech
@MilanJovanovicTech 4 күн бұрын
CQS or CQRS?
@saulolima6874
@saulolima6874 Жыл бұрын
Thank you! Clearly and precise.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Glad you liked it!
@abdulnaserramadan37
@abdulnaserramadan37 Жыл бұрын
Clearly and simple, Thank you.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Glad you liked it! :)
@majormartintibor
@majormartintibor Жыл бұрын
Very nice and clear video. Well done.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Thanks a lot! :)
@misaghn8027
@misaghn8027 7 ай бұрын
You are the best. thank you for this beautiful video.
@MilanJovanovicTech
@MilanJovanovicTech 7 ай бұрын
Thank you too!
@magashkinson
@magashkinson Жыл бұрын
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 Жыл бұрын
@@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 9 ай бұрын
Correct. inclusive that very video is missing the point by a lot.
@noble_mickmick8889
@noble_mickmick8889 3 ай бұрын
Thank you it's very clear now :)
@MilanJovanovicTech
@MilanJovanovicTech 3 ай бұрын
You're welcome!
@PatricSjoeoe
@PatricSjoeoe Жыл бұрын
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.
@dannevesdantas
@dannevesdantas 2 ай бұрын
Exceptional explanation!
@MilanJovanovicTech
@MilanJovanovicTech 2 ай бұрын
Glad it helped!
@MrPayTune
@MrPayTune Жыл бұрын
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 :)
@piusagboola7303
@piusagboola7303 24 күн бұрын
🎯 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
@MilanJovanovicTech
@MilanJovanovicTech 23 күн бұрын
Cool
@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
@nikogj9495
@nikogj9495 Жыл бұрын
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 Жыл бұрын
@@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 11 ай бұрын
@@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!
@arunnair7584
@arunnair7584 Жыл бұрын
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.
@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 ай бұрын
Yup, you'd need to split them into separate services. Most of the time, you probably won't need that.
@nove1398
@nove1398 Жыл бұрын
Very clear explanation at all levels of the CQRS co fusion. Nicely done
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Thanks buddy!
@faisal3374
@faisal3374 Жыл бұрын
by creating that much objects (Command) doesn't it effect the performance ?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
Not nearly as much as one round trip to the database will
@ala24531
@ala24531 Жыл бұрын
What other benefits does CQRS add other than separating the code into logical chunks?
@iamjashin
@iamjashin Жыл бұрын
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 Жыл бұрын
“What is the first thing that comes to mind when you hear CQRS?” Is this a collaborative domain?
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
What would an example of that be?
@CesarDemi81
@CesarDemi81 Жыл бұрын
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 Жыл бұрын
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!
@kinax2
@kinax2 9 ай бұрын
why are you using Guid, isn't long is better?
@MilanJovanovicTech
@MilanJovanovicTech 9 ай бұрын
Better is debatable
@IAmFeO2x
@IAmFeO2x Жыл бұрын
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.
@MilanJovanovicTech
@MilanJovanovicTech Жыл бұрын
That's a nice explanation, Kenny!
@margosdesarian
@margosdesarian 4 ай бұрын
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 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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 Жыл бұрын
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!
@MilanJovanovicTech
@MilanJovanovicTech 2 ай бұрын
Let's do a Part 2 then!
@andreisv
@andreisv 11 ай бұрын
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."
EF Core In The CQRS Query Side... Or Something Else?
13:18
Milan Jovanović
Рет қаралды 10 М.
They RUINED Everything! 😢
00:31
Carter Sharer
Рет қаралды 24 МЛН
DELETE TOXICITY = 5 LEGENDARY STARR DROPS!
02:20
Brawl Stars
Рет қаралды 14 МЛН
CQRS Doesn't Have To Be Complicated | Clean Architecture, .NET 6
24:09
Milan Jovanović
Рет қаралды 100 М.
The Only .NET Scheduler You Should Be Using!
16:38
Nick Chapsas
Рет қаралды 45 М.
The 2 MediatR features people don't know about but should
13:48
Nick Chapsas
Рет қаралды 34 М.
Event Sourcing • Martin Fowler • YOW! 2016
28:06
GOTO Conferences
Рет қаралды 23 М.
Fix Your Controllers By Refactoring To Minimal APIs
14:56
Milan Jovanović
Рет қаралды 33 М.
Mi primera placa con dios
0:12
Eyal mewing
Рет қаралды 719 М.
😱НОУТБУК СОСЕДКИ😱
0:30
OMG DEN
Рет қаралды 3,5 МЛН
ВЫ ЧЕ СДЕЛАЛИ С iOS 18?!
22:40
Overtake lab
Рет қаралды 82 М.
WWDC 2024 - June 10 | Apple
1:43:37
Apple
Рет қаралды 10 МЛН
TOP-18 ФИШЕК iOS 18
17:09
Wylsacom
Рет қаралды 606 М.
С ноутбуком придется попрощаться
0:18
Up Your Brains
Рет қаралды 431 М.