mhinze / ShortBus

In-process mediator with low-friction API
MIT License
210 stars 41 forks source link

Query re-use best practice? #31

Open DarranShepherd opened 10 years ago

DarranShepherd commented 10 years ago

Is it good practice, or frowned upon for queries or commands to call other queries or commands?

Say I want a command that adds all Foo items with a category property set to Bar to a database table. Should my command have a category property and the command handler use a GetFoosByCategory query to get the desired items? Or should the command have an IEnumerable property and leave it up to the calling code to get the list by calling the GetFoosByCategory query? Or indeed refactor the code from GetFoosByCategory into a FooRepository.FindByCategory and call this from both query and command?

patroza commented 10 years ago

In my current project setup we view Commands and Queries as fully encapsulating a use case, or in better words, the handlers are orchestrating the use case from start to finish.

This includes calling in the appropriate infrastructure services, calling them to obtain the appropriate domain models, and then calling a single method on the domain model which executes the use case.

Then the last call for commands is generally the SaveChanges method, with possibly following a return of e.g the ID. And in case of a query; the results formatted in the appropriate (view) models (usually transformed by levaraging AutoMapper).

The UI then acts upon this result, by either rendering it or marking the outstanding operation as complete, like closing the input dialog etc.

In terms of actual database queries I usually use extension methods for re-usable bits, and just use those in my handlers.

I've come to love this approach a lot, and am applying it in MVC, WebAPI, SignalR and even WPF. This is just one approach though, I think one can be flexible in how to use the command/query approach.

PS we don't use concrete repositories, we're leveraging the EntityFrameworks built-in Unit of Work and Repository features.

patroza commented 10 years ago

We're also evaluating a domain command/query approach to leverage domain services in domain objects, where the same kind of objects might need varying services to complete their work, making it virtually impossible to have generally shaped role interfaces. By just requiring invariants and a mediator in the method signature, we can again share the same method signature across these objects.

I do feel this approach pulls us towards a service locator kind of situation, which I am not particularly fond of. So we are looking and striving for better design/solutions to this problem.

We want to encapsulate as much control in our domain objects as possible, creating a rich domain model (as opposed to an anemic one with all the logic spread out over services), while at the same time not ending up with god objects at the same time.

Examples of this are launching processes, writing configuration files, etc. Some of our objects need to just launch a process, and some first need to write configuration files before launching a process. And we can imagine in the future needing even more variations in different implementations of these objects.

Writing a method signature which requires all these services, while only certain implementations really use them, is generally bad and potentially infinitely blow up the method signature in the future. While on the other hand having wildly varying method signatures, makes dispatching from objects consuming them wildly more complex, unless going into ninja practices of reflection and dynamically assigning the dependencies (service locating them in fact).

patroza commented 10 years ago

Btw I can really recommend these blog posts by Jimmy Bogard (and the lostechies blog in general :))

http://lostechies.com/jimmybogard/2013/10/10/put-your-controllers-on-a-diet-redux/ http://lostechies.com/jimmybogard/2013/10/22/put-your-controllers-on-a-diet-defactoring/ http://lostechies.com/jimmybogard/2013/10/23/put-your-controllers-on-a-diet-a-survey/ http://lostechies.com/jimmybogard/2013/10/29/put-your-controllers-on-a-diet-gets-and-queries/ http://lostechies.com/jimmybogard/2013/12/19/put-your-controllers-on-a-diet-posts-and-commands/ http://lostechies.com/jimmybogard/2014/04/08/using-automapper-to-perform-linq-aggregations/ http://lostechies.com/jimmybogard/2014/05/07/projecting-computed-properties-with-linq-and-automapper/ http://lostechies.com/jimmybogard/2014/05/13/a-better-domain-events-pattern/