Skip to content

Commit

Permalink
♻️ Use Immediate.Handlers instead of MediatR (#50)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hona authored Mar 23, 2024
1 parent 103c160 commit b240f60
Show file tree
Hide file tree
Showing 15 changed files with 80 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
namespace VerticalSliceArchitectureTemplate.Common.Domain;
using MediatR;

namespace VerticalSliceArchitectureTemplate.Common.Domain;

public abstract class BaseEntity
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore.Diagnostics;
using MediatR;
using Microsoft.EntityFrameworkCore.Diagnostics;
using VerticalSliceArchitectureTemplate.Common.Domain;

namespace VerticalSliceArchitectureTemplate.Common.Persistence;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

namespace VerticalSliceArchitectureTemplate.Features.Todos.Commands;

public sealed record CompleteTodoCommand(Guid Id) : IRequest;

public sealed class CompleteTodoCommandHandler(AppDbContext dbContext) : IRequestHandler<CompleteTodoCommand>
[Handler]
public sealed partial class CompleteTodo
{
public async Task Handle(CompleteTodoCommand request, CancellationToken cancellationToken)
public sealed record Command(Guid Id);
private static async ValueTask HandleAsync(Command request, AppDbContext dbContext, CancellationToken cancellationToken)
{
var todo = await dbContext.Todos.FindAsync([request.Id], cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

namespace VerticalSliceArchitectureTemplate.Features.Todos.Commands;

public sealed record CreateTodoCommand(string Text) : IRequest<Guid>;

public sealed class CreateTodoCommandHandler(AppDbContext dbContext) : IRequestHandler<CreateTodoCommand, Guid>
[Handler]
public sealed partial class CreateTodo
{
public async Task<Guid> Handle(CreateTodoCommand request, CancellationToken cancellationToken)
public sealed record Command(string Text);

private static async ValueTask<Guid> HandleAsync(Command request, AppDbContext dbContext, CancellationToken cancellationToken)
{
var todo = new Todo
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

namespace VerticalSliceArchitectureTemplate.Features.Todos.Commands;

public sealed record DeleteTodoCommand(Guid Id) : IRequest;

public sealed class DeleteTodoCommandHandler(AppDbContext dbContext) : IRequestHandler<DeleteTodoCommand>
[Handler]
public sealed partial class DeleteTodo
{
public async Task Handle(DeleteTodoCommand request, CancellationToken cancellationToken)
public sealed record Command(Guid Id);

private static async ValueTask HandleAsync(Command request, AppDbContext dbContext, CancellationToken cancellationToken)
{
var todo = await dbContext.Todos.FindAsync([request.Id], cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@

namespace VerticalSliceArchitectureTemplate.Features.Todos.Commands;

public sealed record UpdateTodoCommand(Guid Id, string Text) : IRequest;

public sealed class UpdateTodoCommandHandler(AppDbContext dbContext) : IRequestHandler<UpdateTodoCommand>
[Handler]
public sealed partial class UpdateTodo
{
public async Task Handle(UpdateTodoCommand request, CancellationToken cancellationToken)
public sealed record Command(Guid Id, string Text);

private static async ValueTask HandleAsync(Command request, AppDbContext dbContext, CancellationToken cancellationToken)
{
var todo = await dbContext.Todos.FindAsync([request.Id], cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
namespace VerticalSliceArchitectureTemplate.Features.Todos.Domain.Events;
using MediatR;

namespace VerticalSliceArchitectureTemplate.Features.Todos.Domain.Events;

public record TodoCompletedEvent(Guid TodoId) : INotification;
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
namespace VerticalSliceArchitectureTemplate.Features.Todos.Domain.Events;
using MediatR;

namespace VerticalSliceArchitectureTemplate.Features.Todos.Domain.Events;

public record TodoCreatedEvent(Guid TodoId) : INotification;
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using VerticalSliceArchitectureTemplate.Features.Todos.Domain;

namespace VerticalSliceArchitectureTemplate.Features.Todos.Queries;

[Handler]
public sealed partial class GetAllTodos
{
public sealed record Query(bool? IsCompleted = null);

private static async ValueTask<IReadOnlyList<Todo>> HandleAsync(Query request, AppDbContext dbContext, CancellationToken cancellationToken)
{
var todos = await dbContext.Todos
.Where(x => request.IsCompleted == null || x.IsCompleted == request.IsCompleted)
.ToListAsync(cancellationToken);

return todos.AsReadOnly();
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

namespace VerticalSliceArchitectureTemplate.Features.Todos.Queries;

public sealed record GetTodoQuery(Guid Id) : IRequest<Todo>;

public sealed class GetTodoQueryHandler(AppDbContext dbContext) : IRequestHandler<GetTodoQuery, Todo>
[Handler]
public sealed partial class GetTodo
{
public async Task<Todo> Handle(GetTodoQuery request, CancellationToken cancellationToken)
public sealed record Query(Guid Id);

private static async ValueTask<Todo> HandleAsync(Query request, AppDbContext dbContext, CancellationToken cancellationToken)
{
var todo = await dbContext.Todos.FindAsync([request.Id], cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Immutable;
using Microsoft.AspNetCore.Mvc;
using VerticalSliceArchitectureTemplate.Features.Todos.Commands;
using VerticalSliceArchitectureTemplate.Features.Todos.Domain;
using VerticalSliceArchitectureTemplate.Features.Todos.Queries;
Expand All @@ -13,9 +14,9 @@ public static void MapEndpoints(IEndpointRouteBuilder endpoints)
.WithTags(nameof(Todo));

group.MapPost("",
async (CreateTodoCommand command, ISender sender, CancellationToken cancellationToken) =>
async (CreateTodo.Command command, CreateTodo.Handler handler, CancellationToken cancellationToken) =>
{
var id = await sender.Send(command, cancellationToken);
var id = await handler.HandleAsync(command, cancellationToken);
return Results.Created($"/todos/{id}", id);
})
.Produces<Todo>(StatusCodes.Status201Created)
Expand All @@ -24,9 +25,12 @@ public static void MapEndpoints(IEndpointRouteBuilder endpoints)
.WithTags(nameof(Todo));

group.MapPut("/{id:guid}",
async (Guid id, UpdateTodoCommand command, ISender sender, CancellationToken cancellationToken) =>
async (Guid id, UpdateTodo.Command command, UpdateTodo.Handler handler, CancellationToken cancellationToken) =>
{
await sender.Send(command with { Id = id }, cancellationToken);
await handler.HandleAsync(command with
{
Id = id // TODO: Remove this duplication
}, cancellationToken);
return Results.NoContent();
})
.Produces(StatusCodes.Status204NoContent)
Expand All @@ -36,9 +40,9 @@ public static void MapEndpoints(IEndpointRouteBuilder endpoints)
.WithTags(nameof(Todo));

group.MapPut("/{id:guid}/complete",
async (Guid id, ISender sender, CancellationToken cancellationToken) =>
async ([FromRoute] Guid id, CompleteTodo.Handler handler, CancellationToken cancellationToken) =>
{
await sender.Send(new CompleteTodoCommand(id), cancellationToken);
await handler.HandleAsync(new CompleteTodo.Command(id), cancellationToken);
return Results.NoContent();
})
.Produces(StatusCodes.Status204NoContent)
Expand All @@ -48,9 +52,9 @@ public static void MapEndpoints(IEndpointRouteBuilder endpoints)
.WithTags(nameof(Todo));

group.MapDelete("/{id:guid}",
async (Guid id, ISender sender, CancellationToken cancellationToken) =>
async ([FromRoute] Guid id, DeleteTodo.Handler handler, CancellationToken cancellationToken) =>
{
await sender.Send(new DeleteTodoCommand(id), cancellationToken);
await handler.HandleAsync(new DeleteTodo.Command(id), cancellationToken);
return Results.NoContent();
})
.Produces(StatusCodes.Status204NoContent)
Expand All @@ -60,17 +64,17 @@ public static void MapEndpoints(IEndpointRouteBuilder endpoints)
.WithTags(nameof(Todo));

group.MapGet("/{id:guid}",
(Guid id, ISender sender, CancellationToken cancellationToken)
=> sender.Send(new GetTodoQuery(id), cancellationToken))
(Guid id, GetTodo.Handler handler, CancellationToken cancellationToken)
=> handler.HandleAsync(new GetTodo.Query(id), cancellationToken))
.Produces<Todo>()
.Produces(StatusCodes.Status404NotFound)
.ProducesProblem(StatusCodes.Status500InternalServerError)
.WithTags(nameof(Todo));

group.MapGet("",
(bool? isCompleted, ISender sender, CancellationToken cancellationToken)
=> sender.Send(new GetAllTodosQuery(isCompleted), cancellationToken))
.Produces<IImmutableList<Todo>>()
([AsParameters] GetAllTodos.Query query, GetAllTodos.Handler handler, CancellationToken cancellationToken)
=> handler.HandleAsync(query, cancellationToken))
.Produces<IReadOnlyList<Todo>>()
.Produces(StatusCodes.Status404NotFound)
.ProducesProblem(StatusCodes.Status500InternalServerError)
.WithTags(nameof(Todo));
Expand Down
2 changes: 1 addition & 1 deletion src/VerticalSliceArchitectureTemplate/GlobalUsings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
global using VerticalSliceArchitectureTemplate.Common.Exceptions;
global using VerticalSliceArchitectureTemplate.Common.Features;
global using VerticalSliceArchitectureTemplate.Common.Persistence;
global using MediatR;
global using Immediate.Handlers.Shared;
8 changes: 8 additions & 0 deletions src/VerticalSliceArchitectureTemplate/Program.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Reflection;
using VerticalSliceArchitectureTemplate;
using VerticalSliceArchitectureTemplate.Host;

var appAssembly = Assembly.GetExecutingAssembly();
Expand All @@ -8,6 +9,13 @@
builder.Services.AddEfCore();

// Host
builder.Services.AddHandlers();
builder.Services.AddBehaviors();
builder.Services.AddSwaggerGen( options =>
{
options.CustomSchemaIds(x => x.FullName?.Replace("+", ".", StringComparison.Ordinal));
});

builder.Services.AddMediatR(configure => configure.RegisterServicesFromAssemblyContaining<Program>());
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Immediate.Handlers" Version="1.2.0" />
<PackageReference Include="MediatR" Version="12.2.0"/>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.0"/>
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.0"/>
Expand Down

0 comments on commit b240f60

Please sign in to comment.