Skip to main content
added 151 characters in body
Source Link

Edit

I've put all this into a nuget package and on github under "MetalCore.CQS" at https://github.com/MetalKid/MetalCore.CQS

End Edit

Edit

I've put all this into a nuget package and on github under "MetalCore.CQS" at https://github.com/MetalKid/MetalCore.CQS

End Edit

Bumped by Community user
Bumped by Community user
edited tags
Link
dfhwze
  • 14.2k
  • 3
  • 40
  • 101
formatting
Source Link
dfhwze
  • 14.2k
  • 3
  • 40
  • 101

First, CQS = Command Query Separation. Essentially, queries return data and do not modify state and commands modify state but return no data. There is a gray area here where you need to run a command but then return the data (i.e. Queues, Stacks, primary keys from identity fields). Thus, I created a "CommandQuery" for those scenarios where just a command won't be good enough. CQS normally has ICommandHandler<>ICommandHandler<> and IQueryHandler<>IQueryHandler<> interfaces to support decorators. Decorators are classes that can add functionality to these classes without changing them. i.e. The calls are chained. They implement the same interface, but I use SimpleInjectorSimpleInjector to make those classes run before/after the actual implementation. All code becomes narrow and easy to maintain. Also, you can add more decorators to all commands/queries with one line of code.

Since we can route the different verbs to different classes, we can focus on only 1 method at a time per class. I'm using Mediators everywhere so the code looks the same across all classes. Mediators tie a request 1:1 with an implementation via a specific generic interface. I thought this was really the Service Locator anti-pattern, but it isn't because you still know the mediator dependency. I also use async through the entire call stack because Task.CompletedTaskTask.CompletedTask, if needed, has very little overhead and not blocking threads is the way to go.

All my queries/commands/command queries end up returning IResult because I found throwing exceptions is really expensive. Thus, this lets me deal with any issues. IResult is returned by Command and doesn't return any info about the actual command itself, so it doesn't violate CQS. Query returns IResult< T >IResult<T> that stores the data.

The validation decorator will run validation on multiple threads (if you want) and async will not block. I didn't use expressions since a lot of times validation involves multiple properties and has a lot of overhead. The BrokenRule()BrokenRule() parameter is the "Relation" and it can be used to tie the rule somewhere on the UI.

If you know the queries you affect, you just have to give it the query type and it'll take care of the rest. I made it asyncasync in case it took some IO to figure out all the types.

First, CQS = Command Query Separation. Essentially, queries return data and do not modify state and commands modify state but return no data. There is a gray area here where you need to run a command but then return the data (i.e. Queues, Stacks, primary keys from identity fields). Thus, I created a "CommandQuery" for those scenarios where just a command won't be good enough. CQS normally has ICommandHandler<> and IQueryHandler<> interfaces to support decorators. Decorators are classes that can add functionality to these classes without changing them. i.e. The calls are chained. They implement the same interface, but I use SimpleInjector to make those classes run before/after the actual implementation. All code becomes narrow and easy to maintain. Also, you can add more decorators to all commands/queries with one line of code.

Since we can route the different verbs to different classes, we can focus on only 1 method at a time per class. I'm using Mediators everywhere so the code looks the same across all classes. Mediators tie a request 1:1 with an implementation via a specific generic interface. I thought this was really the Service Locator anti-pattern, but it isn't because you still know the mediator dependency. I also use async through the entire call stack because Task.CompletedTask, if needed, has very little overhead and not blocking threads is the way to go.

All my queries/commands/command queries end up returning IResult because I found throwing exceptions is really expensive. Thus, this lets me deal with any issues. IResult is returned by Command and doesn't return any info about the actual command itself, so it doesn't violate CQS. Query returns IResult< T > that stores the data.

The validation decorator will run validation on multiple threads (if you want) and async will not block. I didn't use expressions since a lot of times validation involves multiple properties and has a lot of overhead. The BrokenRule() parameter is the "Relation" and it can be used to tie the rule somewhere on the UI.

If you know the queries you affect, you just have to give it the query type and it'll take care of the rest. I made it async in case it took some IO to figure out all the types.

First, CQS = Command Query Separation. Essentially, queries return data and do not modify state and commands modify state but return no data. There is a gray area here where you need to run a command but then return the data (i.e. Queues, Stacks, primary keys from identity fields). Thus, I created a "CommandQuery" for those scenarios where just a command won't be good enough. CQS normally has ICommandHandler<> and IQueryHandler<> interfaces to support decorators. Decorators are classes that can add functionality to these classes without changing them. i.e. The calls are chained. They implement the same interface, but I use SimpleInjector to make those classes run before/after the actual implementation. All code becomes narrow and easy to maintain. Also, you can add more decorators to all commands/queries with one line of code.

Since we can route the different verbs to different classes, we can focus on only 1 method at a time per class. I'm using Mediators everywhere so the code looks the same across all classes. Mediators tie a request 1:1 with an implementation via a specific generic interface. I thought this was really the Service Locator anti-pattern, but it isn't because you still know the mediator dependency. I also use async through the entire call stack because Task.CompletedTask, if needed, has very little overhead and not blocking threads is the way to go.

All my queries/commands/command queries end up returning IResult because I found throwing exceptions is really expensive. Thus, this lets me deal with any issues. IResult is returned by Command and doesn't return any info about the actual command itself, so it doesn't violate CQS. Query returns IResult<T> that stores the data.

The validation decorator will run validation on multiple threads (if you want) and async will not block. I didn't use expressions since a lot of times validation involves multiple properties and has a lot of overhead. The BrokenRule() parameter is the "Relation" and it can be used to tie the rule somewhere on the UI.

If you know the queries you affect, you just have to give it the query type and it'll take care of the rest. I made it async in case it took some IO to figure out all the types.

Bumped by Community user
Bumped by Community user
Bumped by Community user
Bumped by Community user
Bumped by Community user
added 5337 characters in body
Source Link
Loading
Bumped by Community user
Bumped by Community user
Bumped by Community user
Bumped by Community user
Bumped by Community user
Notice removed Draw attention by CommunityBot
Bounty Ended with no winning answer by CommunityBot
edited tags
Link
Loading
Tweeted twitter.com/StackCodeReview/status/953799999791517696
Notice added Draw attention by Daniel Lorenz
Bounty Started worth 50 reputation by Daniel Lorenz
Source Link
Loading