.NET Core优秀的应用逻辑分层框架设计
目前公司系统多个应用分层结构各不相同,给运维和未来的开发带来了巨大的成本,分层架构看似很简单,但保证整个研发中心都使用统一的分层架构就不容易了。
那么如何保证整个研发中心都使用统一的分层架构,以达到提高编写代码效率、保证工程统一性的目的?
这里给出个人的规划设计,希望对你有所启发。
1.分层目标
- 简单易用:少即是多,哪怕应届生进来也能很快上手
- 结构统一:不管是新系统还是旧系统结构的是一样的。
- 提高效率:提高开发和运维效率,减少维护和学习成本
2.分层架构介绍
先简单介绍当前两种比较流行的分层架构体系:领域分层架构和传统三层架构。
2.1领域分层架构
领域架构:包括仓储层、领域层、应用服务层、表现层和基础公共层,如下图所示:
2.2传统三层架构
另一种是相对传统地分为三层:包括数据层、业务逻辑层和表现层,如下图所示:
2.3二者区别?
在早期做三层架构的时候,大都以表来驱动,在做领域架构的时候,大都以业务逻辑来驱动,两者的区别确实比较明显,但到了现在,如果都以业务逻辑为中心,那么两者并没有本质区别。
携程公司采用了第二种分层法,他们希望把分层做得极简,也就是说,哪怕刚毕业进入公司的员工,在分层时基本上也不会乱。
相对于第一种分层法,第二种分层法简单得多。每一个应用的代码量都不应该很大,一旦工程变得过大,就会把它适当拆分,而不是全部放在一单体应用里。
总之,分层越简单,整个软件结构就越清晰,代码就越容易统一。
把工程做得极简,才有利于复制,有利于业务的快速构建,有利于规模化,使系统稳定可靠。
3.统一分层规范
以上两种逻辑分层如何做选型?我们要回到分层的目的上来评估,我们的目标是简单、统一、高效。所以传统的三层架构很好的满足了我们的需求。
而领域驱动开发,对DDD有一定的学习成本,同时对旧系统的历史包袱,比如数据库,我们无法做到面向领域编程,我们更多的要面向数据库编程。
所以,当前敏捷框架比较适合想从老系统迁移的,但是有数据库历史包袱的团队。
3.1减少私人定制:
减少私人通用帮助类CommonLayer的编写,如果每一个应用中有大量相同的帮助类,则在架构层面上是有问题的。线上应用越多,则代码重复越多。比如,每个应用都有分页帮助类、数据库帮助类、缓存帮助类、MQ帮助类、日志帮助类、AOP帮助类等。
每一个应用都是特别的,都需要私人定制极少有通用的代码,如果有,那么应该由框架或组件专门解决。这里框架统一放在Com.Util里。
3.2内聚大于解耦:
内聚是指部门内有共同的目标,然后大家紧密合作。解耦是指部门间各自职责明确,然后减少不必要的连接。一个应用如同一个部门,应该有一个共同的目标和职责,然后大家紧密合作。
换句话说,应用内部应减少不必要的契约接口,减少不必要的依赖注入实现,减少不必要且代价过大的解耦。一切以简单实用为主,应用的价值输出为导向。
总之,无论采取何种分层架构,分层架构最核心的一点就是要保证各层之间职责足够清晰,边界足够明显,让人看到架构图后就能看懂整个架构。
4.分层规范实践
4.0命名规范遵循:
简单、可读、优雅
4.1表现层规范
(1)项目命名规则:
如果是API服务,则命名规则为:{公司}.{业务名}.API 比如:Com.Channel.API
如果是MVC站点,则命名规则为:{公司}.{业务名}.MVCSite 比如:Com.Channel.MVCSite
4.2逻辑层规范
(1)项目命名规则:{公司}.{业务名}.Business
比如:Com.Channel.Business
(2)类名以Logic结尾
4.3数据层规范
(1)项目命名规则:{公司}.{业务名}.{数据库}DB
比如:Com.Channel.MSSQLDB
(2)约定在应用中使用SQL语句,不使用存储过程。旧的存储过程可以继续使用和修改。
(3)使用数据库的最新特性进行分页
4.4实体层规范
DTO规范
(1)项目命名规则:{公司}.{业务名}.DTO
比如:Com.Channel.DTO
(2)请求参数DTO实体类放在Request文件夹下,且命名规则为以Request结尾,如下图的 SearchColorRequest.cs
(3)响应DTO实体类放在Response文件夹下,且命名规则为以Response结尾,如下图的 SearchColorResponse.cs
(4)如果请求或响应的DTO实体类的属性中有对象或枚举,那么这些对象所属的类、枚举放在DTO项目的Common文件夹下。
(5)如果请求或响应的DTO实体类有基类要继承,那么约定为基类取名为RequestBase.cs、 ResponseBase.cs,且这些基类直接放在DTO项目的Common文件夹下。
VO规范
(1)项目命名规则:{公司}.{业务名}.ViewModel 比如:Com.Channel.ViewModel
(2)VO实体类约定用Controller作为文件夹名称
(3)VO实体类名的命名约定:
请求VO以Input/Form/Query结尾,如下图的Colorlnput.cs 响应VO以Output/List/Result结尾,如下图的ColorOutput.cs
4.5公共层规范
(1)项目命名规则:{公司}.{业务名}.Util
比如:Com.Channel.Util
4.6测试层规范
(1)项目命名规则:{公司}.{业务名}.UnitTest
比如:Com.Channel.UnitTest
(3)测试方法命名约定
测试方法名分成三段:主题+期望结果+参数
4.6参数校验层规范
我们知道参数校验对整个系统的安全性和性能来说是很重要的一个环节。
那么我们的参数校验应该怎么做才能让自己满意呢? 也就是说怎样才能让到处都存在的参数校验变得优雅呢?
因为参数校验属于非业务性代码,所以我的想法是使用AOP把他切割出来,不能让校验代码和业务逻辑耦合,而且散落在各处,为此我把校验类独立出来,如下图所示:
在Controller层的做法也很简单,就是绑定一下即可,如下图所示:
5.其他规范
5.1DB配置规范
(1)配置文件分开发环境和生产环境
(2)数据库连接的配置读写分离,链接字符串加密处理
(3)数据库连接配置名的命名规则:{业务}_DB_CONN_读写类型(如上图所示)
5.2配置文件规范
(1)统一使用json文件的配置方式
(2)json文件的读取
- 映射对象
- 统一走AppSetting封装类,通过冒号(:)进行读取
- 数据库做读写分离
5.3静态资源文件规范
(1)公共的静态资源文件(CSS、JS、Image等)放在另外的静态站点中,统一由前端进行开发和维护。一般CSS文件放在css文件夹下,JS文件放在js文件夹下, Image图片文件放在img文件夹下,如下图的左半部分所示。(截图说明,如下图所示:)
(2)静态资源文件必须使用版本号管理,以免更新后由于客户端浏览器缓存而导致站点使用的依然是旧版本的静态资源文件:
<script src="~/js/order.js?v=(Appsetting.StaticFileVersion"></script>
(3)采用前后端完全分离的方式,让Java或NET开发资源撤出表现层,以专注于业务逻辑需求的迭代。
运行,您下载后,所要做的工作就是运行,然后就看到Swagger了(如下图所示)
框架后续
版本系列:
- 单体敏捷框架
- AF3(Agile Framework3),基于三层逻辑概念划分;
- AFD(Agile Framework DDD),基于DDD驱动设计原理划分;
- 微服务敏捷框架
- AMFS(Agile Microservice Framework Single Repository),基于单体代码仓库划分;
- AMFM(Agile Microservice Framework Muti-Repository),基于代码多仓库划分;