Skip to content

Commit e4d678f

Browse files
committed
Add tenant application layer slice
1 parent 2414029 commit e4d678f

24 files changed

Lines changed: 521 additions & 0 deletions
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Ums.Application.Abstractions.Messaging;
2+
3+
using MediatR;
4+
using Ums.Domain.Kernel;
5+
6+
public interface ICommand : IRequest<Result>
7+
{
8+
}
9+
10+
public interface ICommand<TResponse> : IRequest<Result<TResponse>>
11+
{
12+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
namespace Ums.Application.Abstractions.Messaging;
2+
3+
using MediatR;
4+
using Ums.Domain.Kernel;
5+
6+
public interface ICommandHandler<in TCommand> : IRequestHandler<TCommand, Result>
7+
where TCommand : ICommand
8+
{
9+
}
10+
11+
public interface ICommandHandler<in TCommand, TResponse> : IRequestHandler<TCommand, Result<TResponse>>
12+
where TCommand : ICommand<TResponse>
13+
{
14+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
namespace Ums.Application.Abstractions.Messaging;
2+
3+
using MediatR;
4+
using Ums.Domain.Kernel;
5+
6+
public interface IQuery<TResponse> : IRequest<Result<TResponse>>
7+
{
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
namespace Ums.Application.Abstractions.Messaging;
2+
3+
using MediatR;
4+
using Ums.Domain.Kernel;
5+
6+
public interface IQueryHandler<in TQuery, TResponse> : IRequestHandler<TQuery, Result<TResponse>>
7+
where TQuery : IQuery<TResponse>
8+
{
9+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace Ums.Application.Abstractions.Persistence;
2+
3+
using Ums.Domain.Identity.Tenant;
4+
using Ums.Domain.Kernel.ValueObjects;
5+
6+
public interface ITenantRepository
7+
{
8+
Task<Tenant?> FindByIdAsync(TenantId tenantId, CancellationToken cancellationToken = default);
9+
Task<Tenant?> FindByCodeAsync(Code code, CancellationToken cancellationToken = default);
10+
Task AddAsync(Tenant tenant, CancellationToken cancellationToken = default);
11+
Task UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default);
12+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
namespace Ums.Application.Abstractions.Persistence;
2+
3+
public interface IUnitOfWork
4+
{
5+
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
6+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
namespace Ums.Application.Common.Behaviors;
2+
3+
using FluentValidation;
4+
using MediatR;
5+
using Ums.Domain.Kernel;
6+
7+
public sealed class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
8+
where TRequest : notnull
9+
{
10+
private readonly IEnumerable<IValidator<TRequest>> _validators;
11+
12+
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators)
13+
{
14+
_validators = validators;
15+
}
16+
17+
public async Task<TResponse> Handle(
18+
TRequest request,
19+
RequestHandlerDelegate<TResponse> next,
20+
CancellationToken cancellationToken)
21+
{
22+
if (!_validators.Any())
23+
{
24+
return await next();
25+
}
26+
27+
var context = new ValidationContext<TRequest>(request);
28+
var validationResults = await Task.WhenAll(
29+
_validators.Select(validator => validator.ValidateAsync(context, cancellationToken)));
30+
31+
var errors = validationResults
32+
.SelectMany(result => result.Errors)
33+
.Where(error => error is not null)
34+
.Select(error => $"{error.PropertyName}: {error.ErrorMessage}")
35+
.Distinct()
36+
.ToArray();
37+
38+
if (errors.Length == 0)
39+
{
40+
return await next();
41+
}
42+
43+
return CreateValidationResult<TResponse>(string.Join("; ", errors));
44+
}
45+
46+
private static TResult CreateValidationResult<TResult>(string error)
47+
{
48+
if (typeof(TResult) == typeof(Result))
49+
{
50+
return (TResult)(object)Result.Failure(error);
51+
}
52+
53+
if (typeof(TResult).IsGenericType &&
54+
typeof(TResult).GetGenericTypeDefinition() == typeof(Result<>))
55+
{
56+
var valueType = typeof(TResult).GetGenericArguments()[0];
57+
var failureMethod = typeof(Result<>)
58+
.MakeGenericType(valueType)
59+
.GetMethod(nameof(Result.Failure), new[] { typeof(string) });
60+
61+
return (TResult)failureMethod!.Invoke(null, new object[] { error })!;
62+
}
63+
64+
throw new InvalidOperationException($"ValidationBehavior cannot create a failure result for '{typeof(TResult).Name}'.");
65+
}
66+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
namespace Ums.Application.Common;
2+
3+
using Ums.Shell.Ddd;
4+
5+
internal static class DomainEnumerationParser
6+
{
7+
public static T? FromName<T>(string? name)
8+
where T : DomainEnumeration
9+
{
10+
if (string.IsNullOrWhiteSpace(name))
11+
{
12+
return null;
13+
}
14+
15+
return DomainEnumeration
16+
.GetAll<T>()
17+
.FirstOrDefault(value => string.Equals(value.Name, name.Trim(), StringComparison.OrdinalIgnoreCase));
18+
}
19+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
namespace Ums.Application;
2+
3+
using FluentValidation;
4+
using MediatR;
5+
using Microsoft.Extensions.DependencyInjection;
6+
using Ums.Application.Common.Behaviors;
7+
8+
public static class DependencyInjection
9+
{
10+
public static IServiceCollection AddApplication(this IServiceCollection services)
11+
{
12+
var assembly = typeof(DependencyInjection).Assembly;
13+
14+
services.AddMediatR(configuration => configuration.RegisterServicesFromAssembly(assembly));
15+
services.AddValidatorsFromAssembly(assembly);
16+
services.AddTransient(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
17+
18+
return services;
19+
}
20+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
namespace Ums.Application.Tenants.ActivateTenant;
2+
3+
using Ums.Application.Abstractions.Messaging;
4+
5+
public sealed record ActivateTenantCommand(Guid TenantId) : ICommand;

0 commit comments

Comments
 (0)