Giter Site home page Giter Site logo

Comments (6)

hypervtechnics avatar hypervtechnics commented on June 10, 2024

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.

andrewtsw avatar andrewtsw commented on June 10, 2024

Hi.
There is no easy way to share logic between CQRS handlers.
What can I suggest:

  1. 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.
  2. Inject a QueryHandler to the other handler and run it. No duplicated code and no MediatR usage.
  3. 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.
  4. 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.

andrew1555 avatar andrew1555 commented on June 10, 2024

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.

andrewtsw avatar andrewtsw commented on June 10, 2024

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.

andrew1555 avatar andrew1555 commented on June 10, 2024

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.

jasontaylordev avatar jasontaylordev commented on June 10, 2024

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)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo 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.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.