ASP.NET Core Clean Architecture
文章目录
- 项目地址
- 一、项目主体
- 1. CQRS
- 1.1 Repository数据库接口
- 1.2 GetEventDetail 完整的Query流程
- 1.3 创建CreateEventCommand并使用validation
- 2. EFcore层
- 2.1 BaseRepository
- 2.2 CategoryRepository
- 2.3 OrderRepository
- 3. Email/Excel导出
- 3.1 Email
- 1. Email接口层
- 4. 定义response
- 5. JWT token
- 6. 添加日志
- 7. 版本控制
- 8. 分页
- 二、测试
- 1. Unitest
- 2. Integration Tests
项目地址
-
教程作者:ASP.NET Core Clean Architecture 2022-12
-
教程地址:
https://www.bilibili.com/video/BV1YZ421M7UA?spm_id_from=333.788.player.switch&vd_source=d14620e2c9f01dee5d2a104075027ad1&p=16
- 代码仓库地址:
- 所用到的框架和插件:
一、项目主体
- Application层
1. CQRS
1.1 Repository数据库接口
- Application层的Contracts里的Persistence,存放数据库的接口
IAsyncRepository
:基类主要功能,规定 增删改查/单一查询/分页
namespace GloboTicket.TicketManagement.Application.Contracts.Persistence
{public interface IAsyncRepository<T> where T : class{Task<T?> GetByIdAsync(Guid id);Task<IReadOnlyList<T>> ListAllAsync();Task<T> AddAsync(T entity);Task UpdateAsync(T entity);Task DeleteAsync(T entity);Task<IReadOnlyList<T>> GetPagedReponseAsync(int page, int size);}
}
ICategoryRepository.cs
:添加自己独特的GetCategoriesWithEvents 方法
namespace GloboTicket.TicketManagement.Application.Contracts.Persistence
{public interface ICategoryRepository : IAsyncRepository<Category>{Task<List<Category>> GetCategoriesWithEvents(bool includePassedEvents);}
}
IEventRepository.cs
:添加Event自己的方法
namespace GloboTicket.TicketManagement.Application.Contracts.Persistence
{public interface IEventRepository : IAsyncRepository<Event>{Task<bool> IsEventNameAndDateUnique(string name, DateTime eventDate);}
}
IOrderRepository.cs
: 没有自己的方法,直接继承使用
namespace GloboTicket.TicketManagement.Application.Contracts.Persistence
{public interface IOrderRepository: IAsyncRepository<Order>{}
}
1.2 GetEventDetail 完整的Query流程
-
项目层级
-
EventDetailVm.cs
:用于返回给接口的数据
CategoryDto.cs
:表示在GetEventDetail里需要用到的Dto
GetEventDetailQuery.cs
:传入ID的值,以及返回EventDetailVm
GetEventDetailQueryHandler.cs
:返回查询
- 返回API的结构类似于
{"eventId": "123e4567-e89b-12d3-a456-426614174000","name": "Rock Concert","price": 100,"artist": "The Rock Band","date": "2023-12-25T20:00:00","description": "An amazing rock concert to end the year!","imageUrl": "https://example.com/images/rock-concert.jpg","categoryId": "456e7890-f12g-34h5-i678-901234567890","category": {"id": "456e7890-f12g-34h5-i678-901234567890","name": "Music"}
}
1.3 创建CreateEventCommand并使用validation
- 设置验证类
CreateEventCommandValidator.cs
using FluentValidation;
using GloboTicket.TicketManagement.Application.Contracts.Persistence;
using System;
using System.Threading;
using System.Threading.Tasks;namespace GloboTicket.TicketManagement.Application.Features.Events.Commands.CreateEvent
{public class CreateEventCommandValidator : AbstractValidator<CreateEventCommand>{private readonly IEventRepository _eventRepository;public CreateEventCommandValidator(IEventRepository eventRepository){_eventRepository = eventRepository;RuleFor(p => p.Name).NotEmpty().WithMessage("{PropertyName} is required.").NotNull().MaximumLength(50).WithMessage("{PropertyName} must not exceed 50 characters.");RuleFor(p => p.Date).NotEmpty().WithMessage("{PropertyName} is required.").NotNull().GreaterThan(DateTime.Now);RuleFor(e => e).MustAsync(EventNameAndDateUnique).WithMessage("An event with the same name and date already exists.");RuleFor(p => p.Price).NotEmpty().WithMessage("{PropertyName} is required.").GreaterThan(0);}private async Task<bool> EventNameAndDateUnique(CreateEventCommand e, CancellationToken token){return !(await _eventRepository.IsEventNameAndDateUnique(e.Name, e.Date));}}
}
- Command类:
CreateEventCommand.cs
using MediatR;namespace GloboTicket.TicketManagement.Application.Features.Events.Commands.CreateEvent
{public class CreateEventCommand: IRequest<Guid>{public string Name { get; set; } = string.Empty;public int Price { get; set; }public string? Artist { get; set; }public DateTime Date { get; set; }public string? Description { get; set; }public string? ImageUrl { get; set; }public Guid CategoryId { get; set; }public override string ToString(){return $"Event name: {Name}; Price: {Price}; By: {Artist}; On: {Date.ToShortDateString()}; Description: {Description}";}}
}
CreateEventCommandHandler.cs
:处理Command,并且使用validator
- 自定义验证逻辑:查询在
IEventRepository
接口里
2. EFcore层
-
数据库接口层:Core层的Contracts里的Persistence
-
实现层:Infrastructure层的Persistence
2.1 BaseRepository
BaseRepository.cs
:定义
2.2 CategoryRepository
CategoryRepository.cs
:继承BaseRepository,以及实现接口
2.3 OrderRepository
OrderRepository.cs
使用分页
3. Email/Excel导出
3.1 Email
1. Email接口层
- Core层的Infrastructure
4. 定义response
5. JWT token
6. 添加日志
7. 版本控制
8. 分页
二、测试
- 使用框架
Moq用来模拟数据
Shouldly 用来断言
xunit 测试框架
1. Unitest
- Automatically 代码片段测试,快速
- 测试的是Public API
- 独立运行 run in isolation
- 结果断言
2. Integration Tests
- end to end test between different layers
- more work to set up
- often linked with database