What is Command Query Responsibility Segregation (CQRS) with Derek Comartin - .NET 213

In this episode of Adventures in .NET, guest Derek Comartin talks to us about Command Query Responsibility Segregation. He explains CQRS benefits, downsides and usage in real world situations.

Hosted by:
Special Guests: Derek Comartin

Show Notes

In this episode of Adventures in .NET, guest Derek Comartin talks to us about Command Query Responsibility Segregation. He explains CQRS benefits, downsides and usage in real world situations.

Links


Picks

Transcript


Hello, and welcome to another episode of adventures in dot net. I'm Sean Klayber, your host. And with me today are your cohost, Caleb Wells. Hey, y'all. Hey, y'all.

Yeah. Y'all. What's up? It's southern, man. You're not as you're not as southern as wise.

So how you doing, Y? Right. Good. I yeah. I know.

Yeah. It's a whole different kind of southern. Yeah. Alright. Alright.

Thanks, guys. We've got a we got, I think, an interesting episode today. It's actually something I'm not that familiar with. So our guest is, our guest is Derek Co Martin. Hi, Derek.

How's it going? Hey. Good. Thanks for putting on the show. Appreciate it.

So our topic today, CQRS? It is. QRS. Command query responsibility segregation. Wow.

It's mouthful. We'd like you to tell us a little bit about yourself. But I'm I'm familiar with this, but every time I look at it, I'm like, that looks too complex. So can you tell us about yourself and then tell me why it's not complex? So yeah.

So I am a developer for a start up. I don't even know if you'd consider us a start up anymore. In Canada, we write transportation software. Before that, we're just a small team. We're all remote.

So this COVID 19 situation hasn't drastically changed our workflow really. So it's Good. It's still challenging, obviously, with everything going on. But so we're all remote. But prior to that, I used to work for just a variety of in different domains industries.

That's kind of, I guess, how I picked up on maybe CQRS and other types of patterns or ways of working in business domains. And that's really kinda how I got involved in just interested in domain driven design and things that kind of been stepping stones that I think for a lot of people that got into domain driven design was things like CQRS and event sourcing. So I've been in yeah. Never been into game development. Never been into any of those.

It's always been kinda line of business type apps for either when I was working for a consulting firm, for external customers, or you've like, now where it's kind of a SaaS service. So You know, it's it's the same for all of us. I worked for government in Australia, and Sean and I have spent most of our time doing, right, business apps or apps for schools or, you know, research or whatever. So, yeah, we're we're in the same same, same bucket. Yeah.

So it's it's funny that you say when you look at it, it's it seems complicated, and that is probably the the first thing I hear almost every time is that's complicated or we don't need to do that. That's it's over complicating things, but it I think the reason that is is like so many things in our industry, things start off one way, and people either interpret them wrong or then start, I guess, talking about I mean, they're using the same term, but in meaning something different. And I think that's probably happened a little bit with CRS. I mean, that's happened with pretty much everything I can think of. The biggest culprit to me is rest.

Like I don't think I think people are generally referring to h t p i's and say rest, but don't really mean rest. I just think there's a lot of terms like that we could say, like I always I have a talk where I'm talking with service boundary, and I say what's a service? Everybody's definition of that would probably be different now. Like there's I don't think there's any clear cut definition of what that is. I think that kind of happened to CRS a little bit which is if you probably search it, one of the, if you like look at Google images or etcetera, you'll probably find a diagram that has all these boxes and arrows, right, that some of these boxes and arrows will be a usually will contain a service bus, an event store, a separate read model, and a separate bus on the read side, and then people you start using the words like eventual consistency and all these all these things when really that is, not it really.

I mean it that usually is a part of it, but not it entirely. So at its simplest form, seeker s is really just an extension of CQS, which is command query separation, and all that really meant was that was from Bertrand Mayer, and all it really is is if you have a method, it either mutates state or it returns a value and doesn't mutate state. That's it. That's that's it. Right?

So if you have a method called do some work, it mutates state probably does perform some action, it doesn't return anything. And then conversely, if you have, I mean, get price or whatever the method may be, it's just gonna return you that value, and it's not gonna it's gonna have zero side effects. Nothing else is gonna change. It's completely safe to call. So that's really c q s.

CQRS takes it just a step further, which was from Greg Young, which is just taking that to a class level. And the way that this is most times implemented, really, is through most people the way people see this, really, in examples is really related to messaging. So thinking of messages as kind of invocations of things. So it's really CQRS as just as simple as separating reads and writes. That's it.

So is it is it similar to, I made patents such as, like, Redux and to help fuel, functions type, I guess? In a way, yes. I guess you could somewhat relate it, and I've seen posts that try because there's terminology that that's a mix match there that represent the exact same things. But the idea being is just, like, take away all that stuff, take away comparing it to anything else. It's really just having 2 paths.

One path for changing state, one path for getting state and making those completely distinct. How does that how would you get that to work with the, an interior application? You know? Alright. I think a lot of us are used to doing a business layer and a data layer and, you know, a view layer.

Right? How does how do you factor CQRS into that? Well, I think it I mean, for me, once I really got into it, it changed the way that I completely wrote apps. So when you're talking about kind of that layered approach and whatever your layers may be, right, like just the ones you described or however people wanna layer things, usually those layers are like app wide. Right?

So you'll maybe have, like you said, say there's some data access layer. That's probably some layer or some project, however you wanna organize it, that is used across your application. Right? Like everything ultimately uses it, and then subsequently like same thing with, your business logic layer, and then one calls the other, and it goes all the way to the database. The difference when you start getting into CQRS is that you're more times thinking about the c part, the command part, ends up being you start thinking more about the intent of what you're trying to do.

What's the actual action that you're trying to perform within the system? Like what is the user actually trying to do? And sometimes those are They could just be purely CRUD based, but more often than than not, if you really start digging around with what you're trying to do, those are well defined things that our users are trying to do. Right? Like an example of this would be, I'm in the trope, transportation industry.

So when a truck drives up to your house to deliver your Amazon Prime package, right, when they do something and they physically drop that package off, that's a delivery. Like, that's a command I'm delivering. Delivering is the command. I'm delivering this. It's not a update some date time in a record somewhere to set the delivery time.

Right? Like, there's no there's a specific action related to that. So when you start thinking about commands and actions as intent of a user wanting to do something, you start building your apps in more of a vertical way in terms of actions and features rather than you do start thinking like what you're referring to as layers. So you get away from necessarily having application wide layers, and you start thinking of having vertical slices of features. That's good.

Good. Because, you know, when you first defined it, I was thinking in my mind, you know, how is this different than just CRUD? But you're saying it's a higher level thinking of of that not so low level basic of just insert, update, delete, create. It's it's it's higher than that. Yeah.

It's really getting into, yeah, intent. And when you start doing that, it permeates everything. It's it's kind of viral in a sense because then your UI starts having to do that. So now your UI isn't this cruddy form, right? Like I said, maybe there's some field that you have to specify some date time.

Now it's it's more task based about what's a user's task, what's the workflow, and those obviously those like that task based UI ultimately were then is what you're invoking commands for or queries to fetch that data back after the fact for like what you want to display to users, etcetera. So with CQRS, are there like frameworks that help you implement this, or is this more of just a software pattern that, you follow? Yeah. I would say it's more of a pattern that you follow. You don't you absolutely don't need a framework at all.

There will be libraries that will help you do it kind of in a standardized way. The easily the most popular is mediator from Jimmy Bogart. So it does exactly what I'm talking about. You can and it's all in process. It's really simple.

Like it's not there's nothing that are using there's no queues. There's no service bus. It's just it allows you to create request objects that you then pass to the mediator, and then subsequently the mediator library then invokes a handler that handles that message, that handles that request. So you think of your request really just is it contains it's kind of the command pattern. You have a an object that contains all the properties that really you could think of them as method, parameters.

And when you send it to the mediator, meteor basically constructs the right, handler for it which is just the class that handles that method method or message, and then just invokes it basically. Where this is probably the most used I think, the mediator library in particular is in web apps. It's where I've seen it used the most, but it's not it could be used anywhere really. That's similar to like the NGINX type pattern? No.

So it's not like it it'd be more of for example, if you're thinking about ASP NET MVC and you have a controller action, instead of putting pretty much anything in the controller action, really what you'd be doing is immediately calling the mediator. So you're basically constructing a request, and let's say, excuse an example here. Let's say you were typical e commerce example. Say you were adding something to your shopping cart, right? You're adding a product to your shopping cart.

So you have some controller. You have some route. When that route executes, basically that action, all you're really gonna do is construct a new object called add item to cart with maybe the card ID and the product ID that you're adding to it, and then you're passing that object to the mediator. Then the mediator is doing all the work behind the scenes. The benefit there is that your code, like your application code, has no reliance anymore on ASP NET.

It has nothing to do with ASP NET. And there's a bunch of benefits there which is primarily couplings. Now you're not coupling to a framework per se. You just have your code is your code. Right?

Like you have no reference to HTTP context. None of that. Like you're just you're segregating your code out, and you're letting mediator kinda pass that along, and it understands on how to, you know, I mean, make that invocation. So, yeah, you are kind of dependent on mediator, but the beauty of it if you go on GitHub and look at it, it's I wouldn't say it's trivial, but it's not very it's not a huge dependency in my mind. It it's pretty straightforward to how it works.

So it's really great for kinda decoupling. How I determine it, it's decoupling between boundaries. Right? You have your app. You want your app to be your app, and you wanna your framework code to be framework code.

Right? Like, that's kind of the input is ASP Net, for example. And then you just basically pass it off so you're doing it with your code. Kinda like OpenID connect kinda thing. You're passing off the responsibilities of authentication and authorization off to somebody else, And then they will.

Yeah. I mean, ultimately it gets back to your code. Right. But but yeah, it's to me, I, the way I always say it is. I, it depends on the app that you have to write it.

Like, if your apps not, again, my context here is that me usually making apps that are fairly large. Like, these are long lived 5 year old project that I'm working in. They're not small by any stretch. So the benefit for us with doing something like this right from the beginning was when we started this project, we were in the pre.netcore days. It this was all in project k, ASP NET 5, or whatever the heck it was.

D n x or whatever. D n x. Yeah. Yeah. So Yeah.

We kinda and then at that time, right, you had Katana and Owen, which was really the precursor to ASP NET. So we kind of see in the writing on the wall where, okay, this is where this is going and we don't see a future and where ASP Net was. There's this new thing coming. So because we were using this, it allowed us to migrate from ASP Net like on the regular one to ASP Net Core fairly simply. And then in that transition, we actually moved from using web API to Nancy, which is now deceased, but still.

But the fact is because we have no reliant we had really no drastic reliance on the actual framework. Like, our code is totally segregated from ASP dot NET. My team is in the process of developing an app from scratch. We actually just finished up a new application, took two and a half years, and we're we're doing a new one. And we're figuring out which patterns are gonna suit us best.

Right? And, of course, you're gonna have for us, right, you're gonna have controller of of some sort. We're using Angular for the front end, but a controller of some sort for the APIs or whatnot. But we usually use services and repositories, that kind of layering. What you're suggesting is the controllers are only gonna handle the things that that have to do with HTTP or the or the view piece of it.

Right? I can. And you're not gonna use services or repositories at all. Well, you can. So that's the thing.

Right? Another analogy for this though, when I was talking about layers is Mhmm. When we think of, like, a layered architecture or, like, application wide, just think of a layered piece of cake. What I'm saying is once you kinda get into this vertical mode is you still kinda have the cake, but instead cut a piece out. So that piece is that vertical slice.

Within that piece, you can have your layers. Right? So you can use like, there's still gonna be shared responsibilities. Like, you may have a repository or different services that you use. But the thing what happens with this is that because you're developing in features and a feature could just be, like, one command or one query, is that now that individual command or query defines it it defines its own dependencies.

So an example of this is let's say you're using Entity Framework, and your team's like, okay, we want to move to Entity Framework core. There's nothing stopping you from doing that one feature at a time. Gotcha. Okay. Right?

Like, you're not changing a whole data access layer to reimplement. Like, you're not saying, okay. We're going to the data access layer. We're changing this dependency, and it's gonna affect everything that says big bang. And we'll figure out what breaks and and fix it one at a time.

Yeah. You're basically you can migrate things one feature at a time. So that's one of the benefits is basically just each feature owning their dependencies and defining what those dependencies are. And then, yeah, within them, you're still gonna have layering within that. So for example, like, you may do some validation, authorization, you have some execution, maybe you have some logging, whatever the case may be.

But all that's defined within that kind of one particular vertical request, like that one feature. Reminds me of one of the applications that I've worked on where it's it's a mash of web forms, MVC, API, web API, Angular, any framework, and Superclient all in one project. Yeah. Each one has its own responsibilities. And as I can, I migrate things to newer technologies over time?

Yeah. That's the thing. Right? Like, it's it can without being, I guess, diligent, and if you are gonna move stuff, then you end up in this weird place potentially. I mean, there's always trade offs here.

Right? So you got this trade off of, hey, I can move everything 1 at a time, but then if you don't do it, then you're in this world of, you know what I mean? You got all these things going on. Yeah. A lots of technical debt.

Yeah. And then so you're you're trying to move stuff forward, but at the same time, depending on what the size of your app is, would you rather be in a world, like, in a situation where you can slowly maybe make new stuff, like implement new features completely segregate it completely versus interweaved into this, existing code? Because that's the biggest advantage is when you're dealing kind of in this vertical mode, especially how you organize the files the same way, like your source code is when we're like when our team's working, we're saying, okay, we gotta go update, you know, I mean this particular functionality. Generally, it's touching one file, and that file pertains to everything to do with that particular action. So it contains maybe the the command itself, the handler.

There's usually multiple handlers. It's kind of like a pipeline. So it's doing logging, whatever the case may be, and it's all usually segregated within one particular file. So it's not like you're jumping around from the data access project to to the business logic layer project to some models folder project, wherever the case may be. You're really confined to one particular place.

I was gonna ask. So you're you're not you're you're not using the idea of view models and entity models as separate files then anymore? So, like, something like, if you were using an ORM and you have Mhmm. Like, a product customer or whatever entity that's using against entity framework, yeah, that's gonna be shared. So you will that will be something that we shared across features.

But for the most part, besides that, you're really kind of segregating into one particular spot. In that project, I was I was thinking about, you know, I'm blocked. I can't get to dotnetcore with with web forms. So I I can't make that upgrade within it. So I was gonna make another application pool within IIS, make that a dot net core and then share the data layer and authentication between the two pools.

So that would be, you know, one part of it that gets moved to dotnetcore. That would be its own vertical slice, but still have some legacy stuff that's still over in web forms and NBC and Angular and things like that that over time, I'll move one piece at a time. Pretty much. Yep. That's and that's what I think it kinda gets overlooked because, again, for us, being able to to slowly keep with the pace of dot net in its entirety, and now dot netcoreand.net5.

I don't know what we would be doing with with the size of that what the app is right now and being able because our migration to being able to run on dot net core wasn't a quick process, but it was a slowly picking away at things that enabled us to do this. So like I said, being able to, I mean, using obviously, there's a lot of dependencies that you need to potentially migrate to with you depending on what you're using, so you need a little bit of luck there. But, yeah, it's just being able to decouple stuff so that you can deal with it independently. And if you can't move it, you can't move it, or it might be a real challenge to do it, but that doesn't hold everything back. So it sounds like this would be a good pattern, or would work well with microservices.

Yeah. So I guess where that lies then is it's starting to it's taking the concept of what we're talking about of really vertical feature slices, and how you want to group them, and what you define as, like, we were saying earlier, like, a service. What's a service? So a service to me, I guess, to define that a little bit is I view it as a it's a boundary. It owns certain business capabilities.

It owns a set of yeah. It owns a set of business capabilities. I don't think there's necessarily a size to that per se, like it's I don't think there's any formula to say, oh, that's too big or too small. It's really about business capabilities of what it needs to own. But having said that, it's really just yeah.

It's those it's those features. It's those capabilities. So once you do start getting into thinking about vertical slices, then, yeah, if you're thinking about services, microservices, you're not thinking about it technically. You're thinking about it in terms of capabilities, which I think is kind of the biggest, to me, confused part about microservices and services is there's so much focus on the technical aspect to it when I think it's generally more about it should be more focused towards capabilities. Like, what is it actually trying to do?

This this question may may take us off the rails or maybe a little involved, But I'm curious to get your your viewpoint on this since you're you're well versed in SecureS. In in the app, we finished last year. One of the things we decided to do was right we want to keep our controllers as slim as possible. We wanted to handle all of our, business logic and validation and model state inside the service. So we're actually passing the model state from the controller into the service, and we can trigger it based on, you know, complex validation depending on where we're at.

Right? And then we pass that up, or we can actually do the, you know, model state is valid inside the service and then pass the errors up to the controller. Right? So it states then. I'm assuming that wouldn't work with CQRS.

How how how are y'all how is your team handling something something like that handling model state validation? Well, what you're I guess, it's not really if you're and again, if you're taking the request and just passing it up to some other layer, but still using the model state, which is really still from MVC. Right? So, I mean, you still have that dependency. It's still something from MVC.

Right. That's not really anything to do necessarily with CQRS that's a problem. That's just, like, whether you want that couple Yeah. Yeah. I would So but just, like, the idea of a request coming in.

Right. So if the model that you pass into a command is is not valid in some some reason, you would just you would create your own validation error and let it bubble up. Correct. Yep. Is that correct?

Okay. Yeah. So and then there's some there's some really cool middleware. I can't remember exactly what all of them are called, but Christian has one called I don't even know exactly what the prep like, the the NuGet package is called. But ultimately, you can map different results or different exceptions to different problem JSON outputs, and it's really configurable and customizable.

So basically, your again, when we say about, like, segregating your app, your app's not really aware of, like, HTTP responses or anything. Right? You kinda define what your validation errors are, and then let middleware and configuring that middleware determine how you wanna respond with that back to the client. We were using fluent validation, and it was actually handling all of that and triggering the validation. We would just piggyback on on model state because, we didn't have to create our own is valid stuff in that case.

But I can see I I can I can absolutely see where you can can separate the 2, fairly easily? So if all this talk was, like, decoupling and things like that, would it would this pass in, like, you know, testing a lot easier, or is there a certain way to do it? To me, it does. The reason I say that is because I mean, I guess it depends how you implement it. Oh, yeah.

It like anything, I think it just depends what your implementation is. So for us, what that looks like, our writing tests that usually what I handle so a command will just be an object, and there'll be one handler class that will handle that object from mediator, for example, or whatever the dispatcher is. And so most of the time, what our handlers look like are they have a constructor, which take x number of dependencies to do whatever they need to do, and then one method called handle that takes the request that came in from mediator and does whatever it needs to do. So generally, our tests are fairly straightforward, because we're just testing one handler for the most part. Like every test is test this handler.

Whatever it's supposed to do, it does. I'll jump back a little bit where I mentioned like layers is the real benefit of using something like mediator, and there's other things that you can use. I'll give a throw out there to some other libraries. 1 is brighter, so there's brighter command, which does a similar type thing. It's a little bit more advanced in the sense that it can use it can be done using stuff out of process.

So you can put commands to queues and for example. But it can do everything in line too. And the cool thing about this and Mediator does this as well is you can essentially create a pipeline for a request. So what that means is when I was talking about layers, you can have essentially different a request go through different handlers. The easiest example of this is with ASP NET Core and middleware.

You have a request come in and it goes through middleware. And middleware just calls the next piece of middleware. Right? That's the pipeline of a request. So you can do the same thing with Meteor where maybe the first, handler that comes in, maybe you wanna do some validation.

So that thing is specifically only doing validation on the request. So maybe it's looking at the property saying, okay. Is the model itself correct? Maybe I need to do some database lookups or something else specifically to see make sure this request coming in might in my state in the database is actually what it should be to actually even perform this action. So that may be a handler.

That's validation. And if that passes, it goes to the next one, which may be your actual execution, which changes the state. And then maybe from there, subsequently, you have another handler that is the last one in the pipe that is logging the request or something like that. Right? Or maybe it sends out an email.

Who knows what it does after the fact? So there's kind of like this pipeline that you can create with requests, and that's how you can kind of implement, I guess, separating the concerns of what the request does. So it's not like you have just one handler that does all this stuff. So when you get back to testing, you're testing a very narrowed class, right, that has a limited number of dependencies and that it does very something very specific. So you test each of these things in the pipeline, and then maybe your dry lock and integration should test that would test all at once.

Yep. Yeah. For sure. We test them all, individually for sure. And then we do do some that are going end to end, basically.

So the handlers, do they know what to do based upon the type of objects that he getting passed to them or a property on it? Or do you have, you pass it to a different handler depending on what you need to do? Yeah. So when you are, for example, you'll have a class called, you'll create a class called add item to cart. Right?

And like I said, it will have a property on it called product ID and cart ID maybe. And then you'll have a subsequent class that will be called the add item to cart handler, and it will implement, for example, in the case of, mediator, it will implement a I request handler and that takes a type of T and that T is your add item to cart, class. So that handle method will be an instance when an execute mediator executes it of that object that will have the cart ID and the product ID that you're working with. So again, you can think of that object as really as the parameters to what you would normally write if you had some other class that was like your shopping cart class, and it had a add to cart method with those parameters on it. It's just basically you're now making objects messages be kind of the the way to invoke your functionality rather than knowing about the class that you need to call and actually implementing that method call specifically to it.

You're totally unaware. Like, so from the controller, you're totally unaware of the handler. You just only know about the the object that you're creating that is the request. Right. So you have an add item to art.

I add item to cart object instead of just an item in cart object and have a, an action property on it called add. Yep. Do it by the object type and detect that. Okay. Makes sense?

How does this impact the code you write? Not necessarily from a feature perspective of the or the vertical slices, but, you know, one of the things that comes up for me is, dry. You know, don't repeat yourself. How do you right? How are you taking these features and and making them small enough to where they're usable and understandable without having duplication across multiple commands.

Yeah. I hear that one a lot too. The my my usual question to that or answer to it is Yeah. I think there's this tendency to once you go into this mode to think that you only have handlers and you only have commands, while you still can have, you mean, classes that represent shared functionality. So if you need to, for example, fetch a particular object a certain way and that needs to be done in 10 different places, then by all means, create something that's gonna represent that to get, I mean, use that around and use that as a dependency in your handlers.

But I would also caveat this with the thing and again, this is my experience. This is certainly not everyone in every app people have lived in, but I would I often am curious as people go into if you are working in a layered approach, go into your data access layer and turn on CodeLens or whatever the heck it's called in Visual Studio or Rider or Versus Code, whatever you use. That shows you the number of references that you have to a method. And over time, at least what I've experienced is, especially with data access, is because, like, say in some controller action, it needs to fetch data very specific way that people will end up writing a method in some repository somewhere that does that exact query that they need in a very specific way that's not used anywhere else. And you end up with all these methods that have one reference, and only left referenced in one place.

Right? And then from there, what I've experienced, and I probably written myself, is then somebody creates a method that takes a insane amount of arguments or some way to construct the the predicate of what the the query actually should be, and you've pretty much just like reimplemented link. Right? Like, so that's generally where it goes to. So when you mentioned, like, oh, well, repeating yourself, by all means, create reusable classes that you that you need to reuse.

However, I think you'd be surprised on, like, say you're writing a bunch of queries, how each place generally needs to fetch data a very unique way. I I guess I'd say that I think you'd be surprised on how many unique places that you need to fetch data. And then those are self contained within that particular feature or that handler. Yeah. I can see that.

How would someone get started then if, if if I wanted to to learn more about this, pattern or start to implement it? I'm trying not to give myself self promotion here, but Go ahead for it. So on my blog on my blog, codeopinion.com, I have a I just had a ton of stuff on this. And then I have some talks that I've done at different conferences about it. Pretty much what we're talking about now for the most part.

And as well as Jimmy Bogard has a bunch of talks that talk about vertical slices. And and when we're talking about CRS and just the simplicity of commands and queries and just it being that simple, kind of fitting into this mode, he has, again, his blog likely as well as he's made had various talks that he's done that are right in line with what we're talking about now. So those would those are probably the 2 easiest things to pick up on. Just head over to YouTube and probably search for, yeah, Jimmy's name or my name. Nice.

Or look at his library mediator because it's it's got really simplistic examples that it, it will show exactly how this is implemented. Okay. So do we have time to, cover event sourcing? That's another topic we're thinking about. I think it's a segue.

So where we've been doing this, right, or what we've been talking about is okay. It's really simple. There's just command objects, query objects, potentially you're just separating reads and rights. I think where to start at the beginning, why it seems so confusing is because that's where people generally, once they start getting a hang of this and how this works, it naturally starts going down this path of where people have gone before. And that heads into event sourcing for whatever reason, you don't need Seeker S to event source and you don't need event sourcing to use Seeker S but the kind of a lot of the discussions end up being both a lot of times.

So when I was talking about that diagram that has a command coming in and maybe it references like a domain model in the sense of maybe like an aggregate route in this, in a domain driven design sense. And then that gets passed down to some message bus and all this stuff that then creates an event and an event store and this whole whack load of stuff. That's where people end up landing at some point, not necessarily for implementation for real, but just for as you're kinda learning, and you can see what the possibilities are once you started from this very simplistic CQS idea of separation of reason rights. It kind of opens up the door to a lot of things and event sourcing is one of them. So when people are talking about event sourcing, although I think that's getting a little confusing now too, It is really about using a series of facts that represent state.

So instead of representing state in a database as say a table or a document store with an object or a row that says this is what this is now. Instead, you represent it by a series of events. So things that have actually happened in the system. So the typical example people use is with a a bank account. So my analogy to this is it, if you go to your bank's website and you look at your balance for whatever account you have some number there, I generally ask, do you think that's how the bank stores your balances?

They just have a balance. You make a transaction. You see they update some record in some, database saying, oh, this is your new balance. You just you remove $5, so this is your new balance. No.

You really your your balance is a projection. It's basically a it's taking all the withdrawals and deposits, and that's where your that's how you got to where you are right now. Right? If you ever wanted to see on your bank statement, where was I on January 1, 2020, all you'd have to do is is start from when the bank account was opened, and then basically go through all the transactions to that point in time, and that's your state as of that time. Right?

So it's just using things that have happened as fact, like the the occurred, for sure the occurred, to basically build up the state that you want it to look like. So that is it in a nutshell. I guess it's just I'm I'm I'm simplifying, I guess, maybe how you implement it, but that's what it is. It's just using events. So using CQRS and event sourcing, how does it change you as a developer?

What what do you think are the the benefits that you've learned from using? Because you've been using it for several years. Right? I would think it goes back to thinking about less about CRUD. I think you're thinking more about actual the intent of what somebody's actually trying to do in the system and what the outcome of them performing that action is in real world terms.

Right? So when I use again, like, in transportation, when there's an event, for example, would be a pickup. Like, somebody's picking up the freight at the warehouse to go deliver it to your house. Right? Like, that the pickup is the command.

You know, when shipment picked up, something past tense, something that's happened. That would be the event arrival. Maybe they're arriving at my house to deliver. Arrive is the command arrived is the event that's that's happened. The delivery is the command.

That's the intent, what they're trying to do. It's the driver's trying to do. They drop it off. They take that picture that you get that fire the event when they said submit that's meant that command. But once they actually saved that event, that event was, it was delivered.

Right? So all these series of events are things that actually happen. It's not like you have one record in a database that has all these date time columns. There's a lot more that goes with it, and there's a lot of information that you can derive from it if you're just if you're storing actual occurrences of things that have actually happened. It flips things on its head because we're, right, we're so used to thinking of prod.

Right? And and how you manage that. It's a bit of a shift. It is. For sure it is.

And there's technical things there because that's where like, where once you start getting into CQRS and why it fits with event sourcing or I should say event sourcing fits really well with CQRS sometimes too is that the problem is if you if you're just using your events to get to current state, that means that every time you need to get current state, you have to get all those events and essentially replay them to get to where you are. So how exactly do you write a report? Like that would be a nightmare, right? So that's where CRS plays in is that when you have events, certain events occur, you would have other things that will deal with those events that will persist that in a database, for example, to keep up with current state. So like we said, like, with your bank account, yes, there probably is a database with your current balance in it that's constantly getting update all when transactions occur.

But the point of truth isn't that database. It's actually the series of events. So what that allows you to do though is let's say that you wanted to show on the screen very quickly and easily a like, your current balance, and maybe there's some built in report to your bank account. I I mine doesn't, but it'd be awesome if it did. That showed maybe, like, a trending graph of what your account balance is, maybe over the last month.

Will that actually be really easy for them to constantly persist, like your events, your transactions? But then every so often, when, you know, I mean, certain event occurs, just creating different records by month of what your balance is at that time. Like, if it looks at, oh, we've hit a new month, we haven't hit that record yet. What's the balance persisted? You know what I mean?

So you you kinda have this read model, hence the read part of CQRS, where you're dealing with this, what what people call a projection. It's it's taking all your events and turning it into a particular state that you wanna look at. So your state your projection is kinda like a view, like Yes. Through account balance. But then, like, your your source truth is your all your transactions.

Correct. And you can create like a number of different views. So let's say it's another business requirement to show, I don't know, all your, what your balance was, you know, in a previous month or whatever. You can just do that just by using the events. Yeah.

Would that be, you know, accurate? Yeah. So if you have, like, say you never recorded that in that fashion, you can then take all your existing events and write new code that will pile through them basically. And this depending on how many you have, this could take quite a while. But you could kind of make that new view of whatever the new requirement is, whatever piece of data that you wanna show now that you you need to calculate through as the transactions are happening.

And you kinda get there to show it the way you wanna show it now. They always example of this is if somebody comes up to you and says, hey. Well, we want this information. You implement the change, and then it's always going forward. If you're always recording events, you can always go back in time.

Right? It's not from set point of your change. It's always going forward to a certain degree. Right? If you if there's data that you need to record an event that you don't have, well, that's kind of a problem.

But but generally, yeah, it's it allows you to kind of think of the data in different ways later. I think it's good. It's kind of a good philosophy that you if you have all the data separate like that, you can you can always do something with it, you know, like it. I guess it makes it makes it means, satisfying any future requirements a lot easier. It does.

But it's at a trade off. Right? Because and I think the biggest one like, versioning events is a whole separate topic in how you wanna version these things because once you've kinda defined an event and you stored it and you need to likely look at it in the future, you need to have code that always handles that, you know what I mean, that structure of that event. It can't go away, or you have to write kind of backwards compatible code to deal with it. Mhmm.

Right? So it's not just all roses. Right? There's still Mhmm. A lot of stuff that comes with it.

And like you mentioned with just thinking about commands and intent, going from a model of persisting events versus persist always persisting current state in a database is a giant leap for a lot of developers. I think it depends on the developer and what they're used to and maybe what types of applications that they've worked in. Because if you are I always say people always ask, well, what's the domains that, like, event sourcing fits best for? Because I don't think personal I've there's varying opinions on this. Mine is, I don't think it everywhere in our application.

We do it in one very specific place, but I think the place it fits best in is things that naturally seem to be time series, like I was mentioning with a delivery, like like I was mentioning with a delivery, like a shipment. Right? There's a there's a set of things that happen that you would talk about it that way. So it just seems natural that you would record it that way. Same thing when we're talking about bank account transactions.

There's just you would think of it that way naturally. When I first got into event sourcing at the time, kind of one of my mentors was a an accountant. And I was explaining to the accountant what I was starting to learn, and he looked at me with the most puzzled look as if, like, how are you just figuring this out? Like, that's how that's how it should always work. Right?

Because that to him, in his quotes, that's just how everything works, and the idea of just recording current state was like, what? That doesn't make any sense. So I think it depends kinda maybe the apps you've lived in and but, yeah, if you're just used to kinda crud, it's it's definitely a leap. So is this anything, that's similar to, like, blockchain? So at a very low level, a piece of it, yes.

Because you're talking about recording state that is immutable. The rest of it, no. Because there's no, like, consensus. There's no distributed aspect to it at at its heart. It's really just about recording state that's immutable or like events, I guess, that are the states over time.

Yeah. Yeah. And those are immutable. Yeah. You're not, you're not going back and changing an event like it's happened.

Like your, like your bank statement, you know, your balance. You don't go in there and change a transaction. Yeah. You basically have, but it brings up a point is that it's, that's actually another difficulty is when you unimaginably who writes bugs, nobody, right. Do create some sort of bug where you do need to go back and fix an issue is that then it's not just writing an update statement to a fix a record in a database.

It is writing some code that has some compensating event, right? Just like you would have with a bank error. I mean, there's a like, if you if you deposit a $100 or the deposit and the fat finger, the amount, and you really meant to deposit 90, whatever the case may be, generally, what they'll do is a full reversal. Right? They'll take back out the a 100 and then add the 90.

So usually if that's a whole nother issue is like, it seems really easy when you have issues right now with data. Oh, I just go in and fix the data in the database. That's the end of it. But the event sourcing, there's a whole nother set of ways you do that, and it's doesn't necessarily seem as easy. So in that case, you'd literally just have 2 separate events, wouldn't it?

I have to to do that. You wouldn't be able to go back to the the old event and change it. No. You're not changing that old event. You're basically yeah.

You're having to create a new event that is compensating, kind of reversing what that original event was that was done in error or whatever the case may be. Sounds like a lot of fun. So are there situations, scenarios where, you know, you wouldn't wanna use CQRS or event sourcing? I think if you're I I that's a good question because for me, it's been, I can only relate to my own experiences where I've done it in places that in hindsight now where it probably made it more difficult than it was for those, the exact reasons that I mentioned, like events changing over time, like needing to kind of version them over time and the complications that those brought. I think the thing that the place is not to do it, I guess, would say on how well the first the team is in the, in the idea in general, maybe it's not something you want to tackle on something super critical at first, because it is a big leap.

It could also violate the KISS principle too. You know, something is just really simple. You don't first implement CQRS. Having said that, though, right, it's very difficult to I guess, sometimes when you it's the hammer problem. Right?

When you kinda learn it, you're like, I just wanna do this everywhere. And I was guilty of it for sure at at initially, I think everybody is when you kinda learn something new. But then at the same time, it's if you don't unfortunately, I kinda feel like if you don't do it wrong, you're not gonna know where to do it right. That's why I guess me saying this, it it probably won't hit home to a lot of people and there's probably a ton of other use cases. But for me, it's always been where things are naturally time series has always been the easy fit where things are occurring and people in the business talk about it that way.

Like, these are things that happen. Like, they're they're solid points in time. Great. Anybody have some final questions? No.

I've I've got a lot to, to think about. Right? Yeah. How how am I gonna sell this to the other developers on the team? Well, start small.

Right. Alright. So if there's no other questions, I guess we should move on to PIX. Let me go first since I you guys are still mine if, you know, if I if I let you go before me. So so just this week, I was I was watching, a new demo of the Unreal 5 engine, and they it was just mind blowing what they were doing on this demo.

It was off of a PS 5, so it's not something you can actually, you know, use on your machine yet. But, they were just showing modeling and lighting and effects off of billions of triangles within scenes and, you know, like, real time things. So, the little demo is kind of this person going through a caved environment and climbing up a wall and seeing all these things in that environment. So it's not that long, but, do check it out. Alright, Caleb.

What's your pick? My tip this week has to do with Mark Miller, my my good friend from Twitch. Mister Konrush. Yes. Mister Konrush.

I am enjoying his strict streams. There's a lot of fun and games going on. But his strict stream, he's actually building an application that he's using in in another stream, another context. It's, they're doing a d and d campaign on Twitch, and they're called Dragon Humpers. It's it's not it's adult content.

Right? It's not it's not necessarily kid friendly. Sounds like Mark. Right. Yeah.

It does, doesn't it? But, I've I've caught a couple of them, and it's it's funny. Sometimes they actually dress up in costume. They they really get into it. So if you get a chance, check it out.

It's it's, it's fun. Nice. Alright. Why? What's your pick?

Yeah. Definitely check out that dragon hump and sink. I love this. So my pick this week is it's it's it's not a switch game, actually. It's called, ring yeah.

Yeah. It's called Ring Fit Adventure. So Heck yeah. It's actually yeah. You got it?

Yeah. It's actually really, it's actually really hard to find a copy. You might actually have struggled to find it if you're wherever you are. But in Australia, it's actually quite hard. But I managed to get a copy.

But it's like a it's like an adventure game where you have this, like, you just gotta go around fighting monsters and things with this, by doing a series of exercises with this, like, ring that comes with the game. And, yeah, I've been kind of using it every day while I'm working from home, you know. And it's just a good way for me to just motivate myself to to do exercise. My son, Gideon, and I, we actually played Ring Fit Adventure this morning. We're Oh, yeah.

Yeah. To, like, our 4th battle with Dragax or whatever his name is. He's a Yeah. Yeah. Purple demon bodybuilder.

Yeah. It's it's it's good stuff. It's good. I mean, you actually get pretty sweaty and swaddled on that and pretty sore resourceful. Yeah.

Alright, Derek. Do you have something that you wanna let our listeners know about that you're interested in lately? So I, throughout the week, I always kind of just tag links to myself with stuff that I pay attention to. So I'm stealing one from my list, which is super techie still or software related. But That's cool.

With all the COVID stuff going, there is, I mean, so many conferences that unfortunately have to do whatever they need to do to, because obviously they're likely not having it in person, but Build Stuff is a conference that has been doing all these e meetups, and then just having speakers kind of do it online. They post it on YouTube. And so check out the Build Stuff YouTube channel. They have one that was, I don't know how recent it is. I think it's maybe like this past week with Sam Newman, and he is talking about coupling cohesion microservices, and it's really cool.

So it definitely is apropos since what we're talking about a little bit today. So I always enjoy his content, and it's pretty cool to see something like build stuff, doing this online. Very cool. And, Microsoft Build is next week as we record this. So if anybody is not registered for that, jump on there.

It's free. So I'm talking about the panelists, not the listeners, because it's new to it. All right. Hopefully they had a good time at build. Everybody that's listening out there.

So, Derek, if one of people want to get in touch with you, you mentioned your blog. Is there you're on Twitch or or or Twitter? Or Yes. I'm on Twitter at Code Opinion. That's the codeopinion.com is my blog.

Code Opinion on Twitch. However, I do stream to it, but I also stream it to YouTube, which really where most of the other content is that I kinda do separately that is edited. So, probably YouTube. Great. And if any one of our listeners wanna get in touch with me or the show, they can reach out to me on Twitter.

I am at dotnetsuperhero. Thanks, Derek, for spending your time with us today. Really like it. Yeah. Thank you.

Appreciate it so much. Alright. Alright. We'll catch everybody next time on the next edition of adventures in dot net.
Album Art
What is Command Query Responsibility Segregation (CQRS) with Derek Comartin - .NET 213
0:00
50:14
Playback Speed: