校园一体化管理系统2.1、基于整洁四层架构 + DDD+CQRS 项目目录解读

校园SaaS系统基于整洁四层架构 + DDD+CQRS 项目目录完整解读

最近两天在编写controller层代码时,在实际编写代码过程中从最初的三层架构到DDD领域模型、再到DDD+CQRS尝试,在代码结构与后期业务发展间反复权衡(本人科班出身也有过两年开发经验,现在转职高中信息技术老师闲暇时编写代码处理一些业务问题,对现在流行的框架结构也不甚了解),最终决定尝试四层架构来完成这个完整的项目,如果有什么建议或意见欢迎留言交流

前言

本文基于.NET 8 搭建校园管理SaaS系统 SchoolSaas,采用整洁四层架构(Clean Architecture) 作为工程分层基底,核心业务模块落地DDD领域驱动设计 + CQRS读写职责分离模式,拆分5个独立类库项目,严格遵循依赖倒置原则 :外层依赖内层,领域核心层不感知任何外部技术(数据库、缓存、API、AOP)。

整套架构适配教务、分班、学籍、社团多业务模块,兼顾单人快速开发与长期业务迭代维护,解决传统三层Service臃肿、业务逻辑散落、读写代码耦合等痛点。

目录结构

复制代码
解决方案 'SchoolSaas' (5 个项目)
├─ SchoolSaas.Api                     # Presentation 表现层(API入口)
│  ├─ Connected Services
│  ├─ Properties
│  ├─ 依赖项
│  ├─ Controllers                     # 接口控制器
│  ├─ Extensions                      # Web服务注册扩展
│  ├─ Filters                         # 全局过滤器(鉴权、异常)
│  ├─ Mappings                        # DTO对象映射
│  ├─ Middleware                      # 自定义中间件
│  ├─ appsettings.json
│  ├─ Dockerfile
│  ├─ Program.cs
│  └─ SchoolSaas.Api.http
│
├─ SchoolSaas.Application             # Application 应用层(CQRS核心)
│  ├─ 依赖项
│  ├─ Commands                        # CQRS 写命令 + CommandHandler
│  ├─ Dtos                            # 应用层输入输出DTO
│  └─ Queries                         # CQRS 查询 + QueryHandler
│
├─ SchoolSaas.Domain                  # Domain 领域层(DDD核心)
│  ├─ 依赖项
│  ├─ Aggregates                      # DDD聚合根
│  ├─ DomainExceptions                # 领域业务异常
│  ├─ DomainServices                  # 领域服务(跨聚合业务)
│  ├─ Events                          # 领域事件 IDomainEvent
│  ├─ IRepositories                   # 仓储抽象接口
│  ├─ Specifications                  # 规约模式
│  └─ ValueObjects                    # 值对象
│
├─ SchoolSaas.Infrastructure          # Infrastructure 基础设施层(技术实现)
│  ├─ 依赖项
│  ├─ Aspects                         # AOP拦截器(AspectCore)
│  ├─ Cache                           # Redis缓存实现
│  ├─ CommonTools                     # 底层通用工具
│  ├─ Data                            # EF Core DbContext、实体映射
│  ├─ Events                          # 领域事件发布实现
│  └─ Repositories                    # 仓储接口实现类
│
└─ SchoolSaas.Shared                  # 公共共享层(全局通用基础类型)
   ├─ 依赖项
   ├─ Constants                       # 全局常量
   ├─ Dtos                            # 通用基础DTO(分页、统一返回体)
   ├─ Enums                           # 全局枚举
   ├─ Exceptions                      # 异常基类
   ├─ Extensions                      # 通用扩展方法
   └─ Interfaces                      # 通用基础抽象接口

一、整体解决方案分层与依赖规则

解决方案共5个项目,由外至内对应整洁四层架构+公共共享层:

项目名称 四层架构对应层 依赖规则
SchoolSaas.Api Presentation 表现层(最外层) 仅依赖 ApplicationShared,禁止直接引用Domain、Infrastructure
SchoolSaas.Application Application 应用层(流程编排层) 依赖 Infrastructure Shared,不直接操作数据库、缓存
SchoolSaas.Domain Domain 领域核心层(业务心脏) 仅依赖 Shared,无任何外部技术依赖,纯业务模型
SchoolSaas.Infrastructure Infrastructure 基础设施层(技术实现层) 依赖 DomainShared,实现Domain定义的所有抽象接口
SchoolSaas.Shared 公共共享层(通用基础类型) 零业务层依赖,存放全局枚举、常量、通用DTO、基础异常

标准依赖流向(不可反向)

Api → Application → Infrastructure → Domain

二、分层目录详细职责解读

1. SchoolSaas.Api(Presentation 表现层)

核心定位 :HTTP接入入口,仅做请求转发、轻量参数校验、DTO转换,零业务逻辑

目录功能说明
  1. Controllers
    所有API控制器,按业务模块二级分包(分班/教务/学籍/社团),接收前端请求,转换DTO为Command/Query,通过Mediator发送至应用层,只做入参、出参包装。
  2. Filters
    全局请求过滤器:身份鉴权过滤器、全局异常捕获过滤器、请求参数基础校验过滤器,属于Web层面通用拦截。
  3. Middleware
    自定义中间件:跨域、请求日志、流量监控、请求ID生成等Web管道增强逻辑。
  4. Extensions
    Web服务注册扩展方法,Program.cs中统一注册控制器、Swagger、跨域等Web组件。
  5. Mappings
    前端DTO与CQRS输入对象的映射配置,分离转换逻辑,简化Controller代码。
  6. appsettings.json / Dockerfile
    项目配置文件、容器部署脚本,仅Web入口层持有环境配置。

2. SchoolSaas.Application(Application 应用层,CQRS核心载体)

核心定位:业务流程编排层,使用CQRS分离读写,不承载任何业务规则,仅协调资源、事务、事件分发。

目录功能说明
  1. Commands
    写操作载体(CQRS命令):新增/修改/删除类业务,如AllocateClassCommandSubmitHomeworkCommand
    配套同级CommandHandlers处理器,负责加载聚合、调用领域服务、提交事务、发布领域事件。
  2. Queries
    读操作载体(CQRS查询):报表、列表、多维度统计,如QueryClassHomeworkStat年级作业通报查询;
    配套QueryHandlers查询处理器,专门处理多表联查、数据导出、大屏统计,完全隔离写逻辑。
  3. Dtos
    应用层专用输入输出DTO,区分Command入参、Query返回报表DTO,按业务上下文分包,隔离前端展示结构。

3. SchoolSaas.Domain(Domain 领域层,DDD核心)

核心定位:纯业务建模层,无任何第三方包、无数据库、无缓存依赖,收拢全部业务约束、状态流转规则,是整个系统的业务标准。

目录功能说明
  1. Aggregates
    DDD聚合根:每个业务边界唯一对外操作入口,如ClassAggregate班级聚合、StudentAggregate学生聚合;
    聚合内部封装所有业务行为(新增学生、调整班级、提交作业),内置业务校验,私有属性禁止外部直接赋值,杜绝贫血模型。
  2. ValueObjects
    DDD值对象:无唯一标识、不可变数据载体,手机号PhoneVo、成绩ScoreVo、班级容量CapacityVo,封装格式校验逻辑。
  3. DomainServices
    领域服务:处理跨聚合复杂业务(批量分班、年级成绩核算),仅依赖聚合根,不操作仓储、数据库。
  4. IRepositories
    仓储抽象接口:仅定义数据读写契约(IClassRepositoryIStudentRepository),数据库实现交给Infrastructure,领域层不感知存储介质。
  5. Events
    领域事件IDomainEvent:聚合状态变更时产生,如StudentAllocatedEvent学生分班事件,用于解耦异步后置动作(通知、数据同步、报表更新)。
  6. DomainExceptions
    业务专属异常:ClassFullException班级名额已满、InvalidScoreException分数非法,领域校验失败直接抛出。
  7. Specifications
    规约模式:封装复杂业务查询条件,复用领域筛选规则,解耦复杂判断逻辑。

4. SchoolSaas.Infrastructure(Infrastructure 基础设施层)

核心定位:所有外部技术实现层,实现Domain定义的抽象接口,隔离数据库、缓存、AOP、消息队列等第三方技术,更换存储/中间件仅改动本层,不影响业务。

目录功能说明
  1. Aspects
    AspectCore AOP全局拦截器:Redis缓存拦截器、全局日志拦截器、幂等拦截器、异常环绕拦截;作用于Application层Handler,横切通用能力,不侵入领域代码。
  2. Data
    EF Core数据库核心:SchoolDbContext上下文、实体与表映射配置、数据迁移脚本,MySQL适配。
  3. Repositories
    仓储实现类:实现Domain层IXXXRepository,封装DbContext、自定义统计SQL,Query侧宽表查询全部写在此处。
  4. Cache
    Redis缓存实现,封装分布式缓存读写逻辑,实现Domain缓存抽象接口。
  5. Events
    领域事件发布实现:进程内同步投递、RabbitMQ异步消息投递,统一处理领域事件分发。
  6. CommonTools
    基础设施通用工具:文件存储、加密、第三方HTTP接口调用等底层工具封装。

5. SchoolSaas.Shared(公共共享层)

核心定位:全局通用基础类型,供其余四层全部引用,无业务逻辑,消除重复代码。

目录功能说明
  1. Enums:全局业务枚举(班级类型、学籍状态、作业状态)
  2. Constants:系统常量、缓存Key前缀、数据库固定参数
  3. Dtos:通用基础DTO,统一分页返回体、全局接口响应包装类
  4. Exceptions:基础全局异常基类,所有业务异常继承于此
  5. Extensions:通用扩展方法(字符串、日期、集合拓展)
  6. Interfaces:通用基础抽象(缓存通用接口、消息基础接口)

三、DDD + CQRS 完整代码执行流程

以「教导处执行新生分班」接口为例,完整链路贴合本项目目录结构:

1. 请求入口(Api层)

前端HTTP请求 → ClassController → 封装入参生成AllocateClassCommand,调用IMediator.Send(cmd)

复制代码
[HttpPost("allocate")]
public async Task<IActionResult> Allocate(AllocateDto dto)
{
    var cmd = new AllocateClassCommand(dto.ClassId, dto.StudentIds);
    var result = await _mediator.Send(cmd);
    return Ok(result);
}

2. AOP拦截增强(Infrastructure.Aspects)

Mediator分发Command后,先经过全局AOP拦截器:

记录操作日志、捕获全局异常、校验操作权限,无业务修改逻辑,仅环绕增强。

3. CQRS CommandHandler 流程编排(Application.Commands)

AllocateClassCommandHandler 仅做协调,不写业务规则:

  1. 注入IClassRepositoryIStudentRepository加载班级、学生聚合;
  2. 注入ClassAllocateDomainService领域服务,执行跨聚合分班逻辑;
  3. 调用领域服务修改聚合内部状态,聚合自动执行业务校验;
  4. 通过IUnitOfWork开启事务,仓储批量更新聚合;
  5. 事务提交后,收集聚合产生的领域事件,统一发布。

4. DDD领域模型执行业务规则(Domain)

  1. 领域服务 ClassAllocateDomainService.AllocateStudents():协调班级、学生两个聚合;
  2. 聚合根 ClassAggregate.AddStudent():内置名额容量校验、学生状态校验,状态变更时新增StudentAllocatedEvent领域事件;
  3. 所有业务约束收拢在聚合/领域服务,上层Handler无法绕过校验直接修改属性。

5. 持久化实现(Infrastructure.Repositories)

仓储实现类使用EF Core DbContext,将聚合根实体持久化到MySQL数据库。

6. 领域事件异步解耦(Infrastructure.Events + Application EventHandlers)

发布StudentAllocatedEvent后,事件处理器异步执行后置动作:

  1. 同步更新学生学籍所属班级;
  2. 推送通知给对应班主任;
  3. 刷新班级人数统计缓存;
  4. 预生成分班报表数据。

查询流程(CQRS读写分离,报表场景)

前端请求班级作业统计报表 → Controller生成QueryClassHomeworkStat → QueryHandler → 直接调用仓储多表统计SQL,返回扁平报表DTO,完全不经过领域聚合、无事务、无业务校验逻辑,读写链路彻底隔离,报表查询不占用写库资源。

四、本架构核心优势总结

  1. 分层职责彻底隔离,符合整洁架构依赖倒置
    业务Domain完全独立,更换MySQL、Redis、消息队列仅修改Infrastructure,核心业务代码无需改动。
  2. DDD领域模型保证业务规则统一
    所有校验、状态流转收敛在聚合根,杜绝上层代码绕过业务规则修改数据,多人协作不会出现业务逻辑不一致。
  3. CQRS读写分离解决报表臃肿问题
    写操作走Command+领域模型,复杂统计、导出报表走Query独立链路,查询逻辑不污染核心业务代码,适配校园海量作业、成绩、分班统计场景。
  4. AOP横切通用能力,业务代码干净
    缓存、日志、异常、鉴权全部抽离到基础设施拦截器,Handler、领域模型无冗余横切代码。
  5. 分层可渐进落地,避免过度设计
    简单模块(公告、报修)可仅使用四层基础分层,不强制DDD聚合;分班、成绩、作业核心模块完整落地DDD+CQRS,平衡开发效率与长期可维护性。
  6. 单元测试友好
    Domain层无外部依赖,可Mock仓储接口单独测试领域业务规则,无需启动数据库、Web服务。
相关推荐
卷无止境9 小时前
Python 脚本工程化:从"能跑就行"到"生产级代码"
后端·python
卷无止境9 小时前
Python 函数式编程:从思想到实践
后端·python
Mandy的名字被占用了18 小时前
晨风AI+知识付费系统|学练考全闭环,重构教育变现新模式
人工智能·后端
why技术18 小时前
分享一套我一直在使用的 AICoding 组合拳,小而美的典范。
前端·后端·ai编程
GetcharZp19 小时前
SRS实战:一个开源流媒体服务,支持6大协议,80ms低延迟,轻松搭建自己的直播平台
后端
Csvn19 小时前
📊 SQL 入门 Day 7:子查询 — 查询中的查询
后端·sql
heimeiyingwang21 小时前
【架构实战】异步化改造:线程池与消息队列的取舍
架构
Mr.HeBoYan1 天前
一次持续三天才出现的丢包故障——深入解析 DPDK Memory Ordering、rte_ring 与 CPU Memory Barrier (下)
linux·网络·算法·架构·dpdk