ASP.NET Core .NET8 CleanArchitecture 产品级完整项目模板
一、解决方案分层结构(大厂标准 4 层洋葱架构)
plaintext
YourProject.sln
├─ YourProject.Domain # 领域层(无任何第三方基础设施依赖)
├─ YourProject.Application # 应用层(业务用例、CQRS、DTO、抽象仓储)
├─ YourProject.Infrastructure # 基础设施(EF、Redis、第三方SDK、仓储实现)
└─ YourProject.Api # 表示层 WebAPI(控制器、鉴权、中间件、Swagger)
二、每层完整代码骨架(可直接复制新建类库)
1. 领域层 YourProject.Domain
项目引用
仅引用 Microsoft.Extensions.DependencyInjection.Abstractions,禁止引用 EF、Redis、Api 等
目录结构
plaintext
Domain
├─ Entities # 数据库实体
├─ ValueObjects # 值对象(DDD)
├─ Events # 领域事件
├─ Interfaces # 仓储抽象、领域服务接口
└─ Exceptions # 自定义业务异常
1.1 领域实体示例 Entities/User.cs
csharp
运行
namespace YourProject.Domain.Entities;
public class User
{
public long Id { get; set; }
public string UserName { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
public DateTime CreateTime { get; set; }
public bool IsDeleted { get; set; }
}
1.2 仓储抽象接口 Interfaces/IUserRepository.cs
csharp
运行
using YourProject.Domain.Entities;
namespace YourProject.Domain.Interfaces;
public interface IUserRepository
{
Task<User?> GetByIdAsync(long id, CancellationToken ct = default);
Task AddAsync(User user, CancellationToken ct = default);
Task UpdateAsync(User user, CancellationToken ct = default);
Task DeleteAsync(User user, CancellationToken ct = default);
Task<User?> GetByNameAsync(string userName, CancellationToken ct = default);
}
1.3 业务自定义异常 Exceptions/BusinessException.cs
csharp
运行
namespace YourProject.Domain.Exceptions;
public class BusinessException : Exception
{
public int Code { get; }
public BusinessException(int code, string message) : base(message)
{
Code = code;
}
}
2. 应用层 YourProject.Application
项目引用
- 项目引用:
YourProject.Domain - NuGet:MediatR、FluentValidation、AutoMapper.Extensions.Microsoft.DependencyInjection
目录结构
plaintext
Application
├─ Dtos # 输入输出DTO
├─ CQRS
│ ├─ Commands # 修改操作(Add/Update/Delete)
│ └─ Queries # 查询操作
├─ Validators # FluentValidation校验器
├─ Mappings # AutoMapper映射配置
└─ DependencyInjection.cs # 应用层DI注册扩展
2.1 DTO 示例 Dtos/UserDto.cs
csharp
运行
namespace YourProject.Application.Dtos;
public class UserDto
{
public long Id { get; set; }
public string UserName { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
public DateTime CreateTime { get; set; }
}
// 创建用户入参
public class CreateUserInput
{
public string UserName { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string Phone { get; set; } = string.Empty;
}
2.2 CQRS Command 示例 CQRS/Commands/CreateUserCommand.cs
csharp
运行
using MediatR;
using YourProject.Application.Dtos;
using YourProject.Domain.Entities;
using YourProject.Domain.Interfaces;
namespace YourProject.Application.CQRS.Commands;
public record CreateUserCommand(CreateUserInput Input) : IRequest<UserDto>;
public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, UserDto>
{
private readonly IUserRepository _userRepo;
private readonly IMapper _mapper;
public CreateUserCommandHandler(IUserRepository userRepo, IMapper mapper)
{
_userRepo = userRepo;
_mapper = mapper;
}
public async Task<UserDto> Handle(CreateUserCommand request, CancellationToken cancellationToken)
{
var exist = await _userRepo.GetByNameAsync(request.Input.UserName, cancellationToken);
if (exist != null)
throw new BusinessException(4001, "用户名已存在");
var user = new User
{
UserName = request.Input.UserName,
PasswordHash = request.Input.Password, // 生产做加密
Phone = request.Input.Phone,
CreateTime = DateTime.Now
};
await _userRepo.AddAsync(user, cancellationToken);
return _mapper.Map<UserDto>(user);
}
}
2.3 参数校验 Validators/CreateUserInputValidator.cs
csharp
运行
using FluentValidation;
using YourProject.Application.Dtos;
namespace YourProject.Application.Validators;
public class CreateUserInputValidator : AbstractValidator<CreateUserInput>
{
public CreateUserInputValidator()
{
RuleFor(x => x.UserName)
.NotEmpty().WithMessage("用户名不能为空")
.Length(2, 20).WithMessage("用户名长度2-20位");
RuleFor(x => x.Password)
.NotEmpty().WithMessage("密码不能为空")
.MinimumLength(6).WithMessage("密码至少6位");
}
}
2.4 AutoMapper 映射 Mappings/MapperProfile.cs
csharp
运行
using AutoMapper;
using YourProject.Application.Dtos;
using YourProject.Domain.Entities;
namespace YourProject.Application.Mappings;
public class MapperProfile : Profile
{
public MapperProfile()
{
CreateMap<User, UserDto>();
}
}
2.5 应用层 DI 注册 DependencyInjection.cs
csharp
运行
using AutoMapper;
using FluentValidation;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
using YourProject.Application.Mappings;
namespace YourProject.Application;
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
// MediatR
services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly));
// FluentValidation 自动扫描全部校验器
services.AddValidatorsFromAssembly(typeof(DependencyInjection).Assembly);
// AutoMapper
services.AddAutoMapper(typeof(MapperProfile));
return services;
}
}
3. 基础设施层 YourProject.Infrastructure
项目引用
- 项目引用:
YourProject.Domain - NuGet:Microsoft.EntityFrameworkCore、Npgsql/Microsoft.Data.SqlClient、StackExchange.Redis
目录结构
plaintext
Infrastructure
├─ Persistence # EF Core数据库上下文、仓储实现
├─ Cache # Redis缓存封装
├─ DependencyInjection.cs
3.1 EF 上下文 Persistence/AppDbContext.cs
csharp
运行
using Microsoft.EntityFrameworkCore;
using YourProject.Domain.Entities;
namespace YourProject.Infrastructure.Persistence;
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
public DbSet<User> Users => Set<User>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// 批量加载实体配置
modelBuilder.ApplyConfigurationsFromAssembly(typeof(AppDbContext).Assembly);
}
}
3.2 仓储实现 Persistence/Repositories/UserRepository.cs
csharp
运行
using Microsoft.EntityFrameworkCore;
using YourProject.Domain.Entities;
using YourProject.Domain.Interfaces;
namespace YourProject.Infrastructure.Persistence.Repositories;
public class UserRepository : IUserRepository
{
private readonly AppDbContext _db;
public UserRepository(AppDbContext db) => _db = db;
public async Task<User?> GetByIdAsync(long id, CancellationToken ct = default)
{
return await _db.Users.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted, ct);
}
public async Task AddAsync(User user, CancellationToken ct = default)
{
await _db.Users.AddAsync(user, ct);
await _db.SaveChangesAsync(ct);
}
public async Task UpdateAsync(User user, CancellationToken ct = default)
{
_db.Users.Update(user);
await _db.SaveChangesAsync(ct);
}
public async Task DeleteAsync(User user, CancellationToken ct = default)
{
user.IsDeleted = true;
await UpdateAsync(user, ct);
}
public async Task<User?> GetByNameAsync(string userName, CancellationToken ct = default)
{
return await _db.Users.AsNoTracking().FirstOrDefaultAsync(x => x.UserName == userName && !x.IsDeleted, ct);
}
}
3.3 基础设施 DI 注册 DependencyInjection.cs
csharp
运行
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using YourProject.Domain.Interfaces;
using YourProject.Infrastructure.Persistence;
using YourProject.Infrastructure.Persistence.Repositories;
namespace YourProject.Infrastructure;
public static class DependencyInjection
{
public static IServiceCollection AddInfrastructure(this IServiceCollection services, IConfiguration cfg)
{
// EF Core
services.AddDbContext<AppDbContext>(opt =>
opt.UseNpgsql(cfg.GetConnectionString("DefaultConnection")));
// 仓储实现注入
services.AddScoped<IUserRepository, UserRepository>();
return services;
}
}
4. 表示层 WebApi YourProject.Api(启动入口)
NuGet 包
Microsoft.AspNetCore.Mvc.NewtonsoftJson、Swashbuckle.AspNetCore、Serilog.AspNetCore、Microsoft.AspNetCore.Authentication.JwtBearer
目录结构
plaintext
Api
├─ Controllers # API控制器
├─ Middlewares # 全局异常、请求日志中间件
├─ Filters # 接口鉴权过滤器
├─ Models # 统一返回模型
├─ Extensions # Jwt、Swagger、跨域扩展
├─ appsettings.json
└─ Program.cs
4.1 统一返回模型 Models/ApiResult.cs
csharp
运行
namespace YourProject.Api.Models;
public class ApiResult<T>
{
public int Code { get; set; }
public string Msg { get; set; } = string.Empty;
public T? Data { get; set; }
public static ApiResult<T> Success(T? data, string msg = "操作成功")
{
return new ApiResult<T> { Code = 200, Msg = msg, Data = data };
}
public static ApiResult<T> Fail(int code, string msg)
{
return new ApiResult<T> { Code = code, Msg = msg };
}
}
4.2 全局异常中间件 Middlewares/GlobalExceptionMiddleware.cs
csharp
运行
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System.Text.Json;
using YourProject.Api.Models;
using YourProject.Domain.Exceptions;
namespace YourProject.Api.Middlewares;
public class GlobalExceptionMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<GlobalExceptionMiddleware> _logger;
public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
{
_next = next;
_logger = logger;
}
public async Task InvokeAsync(HttpContext context)
{
try
{
await _next(context);
}
catch (Exception ex)
{
await HandleExceptionAsync(context, ex);
}
}
private async Task HandleExceptionAsync(HttpContext ctx, Exception ex)
{
ctx.Response.ContentType = "application/json";
ApiResult<object> res;
if (ex is BusinessException busEx)
{
res = ApiResult<object>.Fail(busEx.Code, busEx.Message);
_logger.LogWarning("业务异常:{Msg}", busEx.Message);
}
else
{
res = ApiResult<object>.Fail(500, "服务器内部错误");
// 生产环境不返回堆栈给前端
_logger.LogError(ex, "系统未知异常");
}
var json = JsonSerializer.Serialize(res);
await ctx.Response.WriteAsync(json);
}
}
4.3 用户控制器 Controllers/UserController.cs
csharp
运行
using MediatR;
using Microsoft.AspNetCore.Mvc;
using YourProject.Api.Models;
using YourProject.Application.CQRS.Commands;
using YourProject.Application.Dtos;
namespace YourProject.Api.Controllers;
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly IMediator _mediator;
public UserController(IMediator mediator) => _mediator = mediator;
[HttpPost("create")]
public async Task<ActionResult<ApiResult<UserDto>>> Create(CreateUserInput input)
{
var cmd = new CreateUserCommand(input);
var data = await _mediator.Send(cmd);
return Ok(ApiResult<UserDto>.Success(data));
}
}
4.4 核心 Program.cs(完整启动配置,产品级)
csharp
运行
using Serilog;
using YourProject.Application;
using YourProject.Infrastructure;
using YourProject.Api.Middlewares;
var builder = WebApplication.CreateBuilder(args);
// 1. Serilog日志
Log.Logger = new LoggerConfiguration()
.ReadFrom.Configuration(builder.Configuration)
.Enrich.FromLogContext()
.CreateLogger();
builder.Host.UseSerilog();
// 2. 分层DI注入
builder.Services.AddControllers()
.AddNewtonsoftJson();
builder.Services.AddEndpointsApiExplorer();
// 三层依赖注入
builder.Services.AddApplication();
builder.Services.AddInfrastructure(builder.Configuration);
// 3. Swagger、跨域、Jwt等扩展可自行封装
builder.Services.AddCors(opt =>
{
opt.AddPolicy("AllowAll", p =>
{
p.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
var app = builder.Build();
// 生产/开发区分管道
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseCors("AllowAll");
app.UseAuthentication();
app.UseAuthorization();
// 全局异常中间件放最前面
app.UseMiddleware<GlobalExceptionMiddleware>();
app.MapControllers();
try
{
Log.Information("API服务启动");
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "服务启动失败");
}
finally
{
Log.CloseAndFlush();
}
三、配套 appsettings.json 基础配置
json
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"ConnectionStrings": {
"DefaultConnection": "Host=127.0.0.1;Database=YourDb;Username=postgres;Password=123456"
},
"Serilog": {
"WriteTo": [
{ "Name": "Console" },
{
"Name": "File",
"Args": { "path": "logs/log-.txt", "rollingInterval": "Day" }
}
]
},
"AllowedHosts": "*"
}
四、大厂强制编码使用规则
- 所有 IO 操作全部
async/await,禁止.Result/.Wait() - Controller 只接收参数 + 调用 MediatR,无任何业务逻辑
- 数据库实体不能直接返回前端,必须经过 DTO 隔离脱敏
- 所有入参统一 FluentValidation 校验,不写 if 判断校验
- 业务异常统一抛
BusinessException,全局中间件捕获格式化返回 - 领域层完全解耦,不依赖 EF、Redis、API 任何外部组件
- 仓储只做数据存取,复杂业务逻辑全部放到 Application CQRS Handler
五、快速新建项目命令(.NET CLI)
bash
运行
# 创建四层类库+API
dotnet new classlib -n YourProject.Domain
dotnet new classlib -n YourProject.Application
dotnet new classlib -n YourProject.Infrastructure
dotnet new webapi -n YourProject.Api
# 添加项目引用
dotnet add YourProject.Application reference YourProject.Domain
dotnet add YourProject.Infrastructure reference YourProject.Domain
dotnet add YourProject.Api reference YourProject.Application
dotnet add YourProject.Api reference YourProject.Infrastructure
阅读剩余
THE END