⭐基于.net 5的DDD项目结构
整洁架构(Clean Architecture) / 洋葱架构 示意图:
public class OrderItem : BaseEntity
{
public int ProductId { get; private set; }
// todo:
}
public class Address : ValueObject
{
public String Street { get; private set; }
public String City { get; private set; }
// todo:
}
public class Order : BaseEntity, IAggregateRoot
{
public Address Address { get; private set; }
public int? BuyerId { get; private set; }
private readonly List<OrderItem> _orderItems;
public IReadOnlyCollection<OrderItem> OrderItems => _orderItems;
// todo:
}
IRepository
泛型仓储,提供了一系列基础方法,配合规约使用。
public class GetTodoByIdQueryHandler : IRequestHandler<GetTodoByIdQuery, TodoResponse>
{
private readonly IRepository<Todo> _todoRepository;
public GetTodoByIdQueryHandler(IRepository<Todo> todoRepository)
{
_todoRepository = todoRepository ?? throw new ArgumentNullException(nameof(todoRepository));
}
public async Task<TodoResponse> Handle(GetTodoByIdQuery request, CancellationToken cancellationToken
{
// todo:
// _todoRepository.ListAsync
// _todoRepository.AddAsync
// _todoRepository.UpdateAsync
// _todoRepository.DeleteAsync
// _todoRepository.GetByIdAsync
// ......
return response;
}
}
使用ardalis/Specification
实现。
public sealed class TodoSpec : Specification<Todo>
{
// 默认
public TodoSpec()
{
Query.OrderByDescending(x => x.Id);
}
// 支持条件过滤
public TodoSpec(string filter)
{
if (!string.IsNullOrEmpty(filter))
{
Query.Where(x => x.Name.Contains(filter));
}
Query.OrderByDescending(x => x.Id);
}
// 支持条件过滤和分页
public TodoSpec(string filter, int skip, int take)
{
if (!string.IsNullOrEmpty(filter))
{
Query.Where(x => x.Name.Contains(filter));
}
Query.OrderByDescending(x => x.Id).Skip(skip).Take(take);
}
}
使用:
await _todoRepository.ListAsync(new TodoSpec());
await _todoRepository.ListAsync(new TodoSpec("xx"));
await _todoRepository.ListAsync(new TodoSpec("xx", 0, 10));
// todo:
基于MediatR
实现。
public class NewUserEvent : BaseEvent
{
public string Email { get; }
// todo...
public NewUserEvent(string email)
{
Email = email;
}
}
public class User : BaseEntity, IAggregateRoot
{
public string UserName { get; }
public string Password { get; }
public string Email { get; }
// todo...
public User(string userName, string password, string email)
{
UserName = userName;
Password = password;
Email = email;
AddDomainEvent(new NewUserEvent(email)); //add event
}
}
public class NewUserSendEmailHandler : INotificationHandler<NewUserEvent>
{
public async Task Handle(NewUserEvent notification, CancellationToken cancellationToken)
{
// todo:
// send email...
}
}
// todo:
基于MediatR
实现。
-
以下的 Handler 相当于应用服务(Application Service)
-
Command,Query 相当于 输入DTO
-
Response 相当于 输出DTO
public class CreateTodoCommand : IRequest<TodoResponse>
{
public string Name { get; set; }
public string Desc { get; set; }
// todo:
}
public class CreateTodoCommandHandler : IRequestHandler<CreateTodoCommand, TodoResponse>
{
public async Task<TodoResponse> Handle(CreateTodoCommand request, CancellationToken cancellationToken)
{
// todo:
return response;
}
}
public class TodosController : ControllerBase
{
private readonly IMediator _mediator;
public TodosController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
public async Task<ActionResult<TodoResponse>> CreateTodo(CreateTodoCommand createTodoCommand)
{
var response = await _mediator.Send(createTodoCommand);
return CreatedAtAction(nameof(GetTodo), new {id = response.Id}, response);
}
}
public class GetTodoByIdQuery : IRequest<TodoResponse>
{
public int Id { get; set; }
}
public class GetTodoByIdQueryHandler : IRequestHandler<GetTodoByIdQuery, TodoResponse>
{
public async Task<TodoResponse> Handle(GetTodoByIdQuery request, CancellationToken cancellationToken
{
// todo:
return response;
}
}
public class TodosController : ControllerBase
{
private readonly IMediator _mediator;
public TodosController(IMediator mediator)
{
_mediator = mediator;
}
[HttpGet("{id:int}")]
public async Task<ActionResult<TodoResponse>> GetTodo(int id)
{
var response = await _mediator.Send(new GetTodoByIdQuery(id));
return Ok(response);
}
}
使用[Permission]
标记。
[Permission(nameof(CreateTodoCommand), "创建Todo", "Todo管理")]
public class CreateTodoCommand : IRequest<TodoResponse>
{
public string Name { get; set; }
public string Desc { get; set; }
// todo:
}
泛型仓储IRepository
中的Add
,Update
,Delete
等方法默认实现了UOW
。
也可以将一个Request
标记为[UnitOfWork]
,这样会开启事务,保证请求期间的所有操作在一个UOW
中。
[UnitOfWork]
public class CreateTodoCommand : IRequest<TodoResponse>
{
public string Name { get; set; }
public string Desc { get; set; }
// todo:
}
使用ICurrentUser
服务。
public class GetTodoByIdQueryHandler : IRequestHandler<GetTodoByIdQuery, TodoResponse>
{
private readonly ICurrentUser _currentUser;
public GetTodoByIdQueryHandler(ICurrentUser<Todo> currentUser)
{
_currentUser = currentUser ?? throw new ArgumentNullException(nameof(currentUser));
}
public async Task<TodoResponse> Handle(GetTodoByIdQuery request, CancellationToken cancellationToken
{
// todo:
// _currentUser.IsAuthenticated
// _currentUser.Id
// _currentUser.UserName
// ......
return response;
}
}
使用FluentValidation
实现。
public class CreateTodoCommandValidator : AbstractValidator<CreateTodoCommand>
{
public CreateTodoCommandValidator()
{
RuleFor(x => x.Name).NotEmpty().WithMessage("名称不能为空");
}
}
使用AutoMapper
实现。
public class DomainToResponseProfile : Profile
{
public DomainToResponseProfile()
{
CreateMap<Todo, TodoResponse>();
}
}
public class GetTodoByIdQueryHandler : IRequestHandler<GetTodoByIdQuery, TodoResponse>
{
private readonly IMapper _mapper;
public GetTodoByIdQueryHandler(IMapper mapper)
{
_mapper = mapper ?? throw new ArgumentNullException(nameof(mapper));
}
public async Task<TodoResponse> Handle(GetTodoByIdQuery request, CancellationToken cancellationToken
{
// todo:
return _mapper.Map<TodoResponse>(todo);
}
}