Search code examples
c#asp.net-coredomain-driven-designsoamediatr

Replacing service layer with MediatR - is it worth to do it?


Do you think it might be reasonable to replace my service layer or service classes with MediatR? For example, my service classes look like this:

public interface IEntityService<TEntityDto> where TEntityDto : class, IDto
{
    Task<TEntityDto> CreateAsync(TEntityDto entityDto);
    Task<bool> DeleteAsync(int id);
    Task<IEnumerable<TEntityDto>> GetAllAsync(SieveModel sieveModel);
    Task<TEntityDto> GetByIdAsync(int id);
    Task<TEntityDto> UpdateAsync(int id, TEntityDto entityDto);
}

I want to achieve some sort of modular design so other dynamically loaded modules or plugins can write their own notification or command handlers for my main core application.

Currently, my application is not event-driven at all and there's no easy way for my dynamically loaded plugins to communicate.

I can either incorporate MediatR in my controllers removing service layer completely or use it with my service layer just publishing notifications so my plugins can handle them.

Currently, my logic is mostly CRUD but there's a lot of custom logic going on before creating, updating, deleting.

Possible replacement of my service would look like:

public class CommandHandler : IRequestHandler<CreateCommand, Response>, IRequestHandler<UpdateCommand, Response>, IRequestHandler<DeleteCommand, bool>
{
    private readonly DbContext _dbContext;

    public CommandHandler(DbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public Task<Response> Handle(CreateCommand request, CancellationToken cancellationToken)
    {
        //...
    }

    public Task<Response> Handle(UpdateCommand request, CancellationToken cancellationToken)
    {
        //...
    }

    public Task<bool> Handle(DeleteCommand request, CancellationToken cancellationToken)
    {
        ///...
    }
}

Would it be something wrong to do?

Basically, I'm struggling what to choose for my logic flow:

  • Controller -> Service -> MediatR -> Notification handlers -> Repository
  • Controller -> MediatR -> Command handlers -> Repository

It seems like with MediatR I can't have a single model for Create, Update and Delete, so one way to re-use it I'd need to derive requests like:

public CreateRequest : MyDto, IRequest<MyDto> {}        
public UpdateRequest : MyDto, IRequest<MyDto> {} 

or embed it in my command like:

public CreateRequest : IRequest<MyDto>
{
    MyDto MyDto { get; set; }
}

One advantage of MediatR is the ability to plug logic in and plug it out easily which seems like a nice fit for modular architecture but still, I'm a bit confused how to shape my architecture with it.


Solution

  • Partly this was answered here: MediatR when and why I should use it? vs 2017 webapi

    The biggest benefit of using MediaR(or MicroBus, or any other mediator implementation) is isolating and/or segregating your logic (one of the reasons its popular way to use CQRS) and a good foundation for implementing decorator pattern (so something like ASP.NET Core MVC filters). From MediatR 3.0 there's an inbuilt support for this (see Behaviours) (instead of using IoC decorators)

    You can use the decorator pattern with services (classes like FooService) too. And you can use CQRS with services too (FooReadService, FooWriteService)

    Other than that it's opinion-based, and use what you want to achieve your goal. The end result shouldn't make any difference except for code maintenance.

    Additional reading: