Comments (6)
Thats actually I problem I encountered too. Did not find another solution yet as it would introduce a new layer in the architecture which then makes the whole thing useless.
from northwindtraders.
Hi.
There is no easy way to share logic between CQRS handlers.
What can I suggest:
- You can call one query from another query or command handler. And you use MediatR in this case sou all pipelines will be used again. Do you need it? Sometimes it can be a good idea but in most cases, you do not need it.
- Inject a QueryHandler to the other handler and run it. No duplicated code and no MediatR usage.
- You can create a base class for handlers and put shared logic here. If you use the base class for a couple of handlers - it works fine. But when you use the base handler for all your handlers - better to move this logic to a new abstraction layer.
- And the last and my favorite - add a new abstraction layer. Introduce new "Application services" and put duplicated logic from different handlers here. I do not like repositories because it is related to data access. And you can use application services from your handlers. You also can use any infrastructure (data access, cache, current user, ...) from the application services. But you can not use handlers from them.
It works great because in most cases you do not need a lot of application services. You need them only for duplicated logic. So most of your handlers will be independent. And some of them will use shared logic via application services.
What do you think about these approaches?
from northwindtraders.
Hi
Thanks for your answers, I'm not a fan on the first 3 tbh but the 4th is similar to what I've been doing. I've created a application service for each separate feature which looks like
public class SizeConversionsService : ISizeConversionsService
{
private readonly IMediator _mediator;
public SizeConversionsService(IMediator mediator)
{
_mediator = mediator;
}
public async Task<List<SizeConversionResult>> GetSizeConversion(SizingType type)
{
return await _mediator.Send(new GetSizeConversionsQuery { Type = type });
}
}
But in my case I was still using queries via mediator to benefit from the pipelines, I have a cache pipeline which saves the result in redis, so the query is in charge of cache duration etc. I then inject the ISizeConversionsService into any usecase which needs to know about sizes. With it being a service too like you say I'm able to inject in other things like current user, currency, settings etc.
My handlers would then look like this
public class Handler : IRequestHandler<GetBuyGridQuery, BuyGrid>
{
private readonly ISizeConversionsService _sizeConversionsService;
}
To solve my repeated sql call issue I added in caching into the handler. So for example I have a query which gets a product based on productId but a sub query would get the category, since we have far fewer categories than products repeatedly getting the same category from the database would add up to around 5k identical db calls an hour so I placed it around a cache
var category = await _cacheManager.GetOrCreateAsync($"GetProductByUrlQuery:GetCategory:CategoryId_{productResult.Product.CategoryId}",
() => GetCategory(productResult.Product.CategoryId), 3600);
This way repeated calls to the usecase for different products wouldn't result in the same sql query to get the same category, if that makes sense. I could have solved it using a application service instead and then caching would be shared if any other usecase needed it also, but wasn't sure which solution was better.
What do you think?
I also came across this link https://lostechies.com/jimmybogard/2016/12/12/dealing-with-duplication-in-mediatr-handlers/ which offers some alternatives also
Thanks
from northwindtraders.
Hi
So you wrap mediator calls by the application services. It is item No1 in my list of suggestions.
Why do you need this abstraction? I believe you can inject a mediator into the handler.
Less code => less bugs (and abstraction layers).
And I totally agree with Jimmy about his article. How to deal with duplicated logic - it depends...
There are too many different kinds of logic: data access, domain, or application logic.
But if I understand correctly we are talking about the application logic.
PS: you can make your handlers internal
because they are not a part of the Application Layer contract (Contract = Commands/Queries + DTO).
PPS: When you use GetOrCreateAsync it does not guarantee that GetCategory will be called exactly once.
from northwindtraders.
Great thank you for your reply, I was under the impression injecting mediator into the handlers and calling other handlers would go against the recommendation, do you think it's no different then to using a service class which then calls mediator. The thinking behind it was it was then a single location which calls the query handler everywhere else uses the service so if I needed to change how it's called it would be a single place.
Do you have any working examples of reusing queries in a clean way. Jimmy talks about methods in the dbContext but then how would do this with dapper like data calls.
Thanks for the tip on internal keyword will give that a go.
from northwindtraders.
Thank you for your interest in this project. This repository has been archived and is no longer actively maintained or supported. We appreciate your understanding. Feel free to explore the codebase and adapt it to your own needs if it serves as a useful reference. If you have any further questions or concerns, please refer to the README for more information.
from northwindtraders.
Related Issues (20)
- Consistent naming for behaviors HOT 1
- Why is CQRS put in Application and not in WebUI? HOT 2
- Do non-entities core models belong to the domain? HOT 2
- Use ProblemDetails/ValidationProblemDetails etc for exception middleware
- Use ProblemDetails/ValidationProblemDetails etc for exception middleware? HOT 1
- How can we write unit test cases for controllers extending base controller as we are not using constructor injection. any leads will be appreciated. HOT 1
- What's the point of having the IDateTime interface? HOT 2
- FromSqlRaw won't work in Application HOT 2
- ValueObject.Equals bug when objects have a different number of values HOT 1
- [Question] Should JWT/RefreshToken creation be on infrastructure? HOT 1
- [Discussion] Clean Architecture - is it really? HOT 4
- Why does it not look like shown in the XAML ControlsGallery App - DataGrid HOT 4
- Application layer access to DbContext? HOT 2
- A better way to do upsert HOT 1
- Suggestion: Request with record type
- something similar HOT 1
- IDbContext and repository pattern HOT 1
- How to call stored procedure for CURD operation? HOT 1
- This repo is pretty old and the projects don't event load in VS 2022 HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from northwindtraders.