1、写在前面
应用分层这件事情看起来很简单,但每一个程序员都有本身的一套,哪怕是初学者。如何让一家公司的几百个应用采用统一的分层结构,并获得大部分程序员的认同呢?这可不是件简单的事情,接下来以咱们真实案例与你们一块儿探讨,先问你们两个技术问题:
- 服务的调用代码你以为放到哪一层好呢?A表现层;B业务逻辑层;C数据层;D公共层。
- 如何组织好VO(View Object视图对象)、BO(Business Object业务对象)、DO(Data Object数据对象)、DTO(Data Transfer Object数据传输对象)呢?
不一样的人会有不一样的答案,因此要统一公司应用分层,以减小开发维护学习成本。统一应用分层要可大可小、简单易用、支持多种场景,咱们采用IPO方式:I是Input、O是Output、P是Process,一进一出一处理。应用系统的本质是机器,是处理设备,一进一出一处理。
IPO原理图
2、统一逻辑架构
统一应用分层的逻辑架构图
职责说明:
层英文名 |
中文名 |
说明 |
|
PresentationLayer |
表现层文件夹 |
上层向用户提供服务,负责视图展现。项目类型包括WebSite、WebForm、MVC、WCF、WebService等。 |
|
BusinessLayer |
业务逻辑层文件夹 |
中间逻辑处理,负责应用系统的业务逻辑的处理。 |
|
DataLayer |
数据访问层文件夹 |
下层调用服务,负责数据资源提供方如数据库、SOA、OpenAPI的交互。 |
|
EntityLayer |
实体层文件夹 |
VO:View Object视图对象; DTO:Data Transfer Object数据传输对象; BO:Business Object业务对象; DO:Data Object数据对象; 在实际项目中,为简化设计可进行裁剪,BO和DO为可选,DTO属于服务项目类型,VO属于网站项目类型,也不会同时存在。 |
|
CommonLayer |
公共层文件夹 |
工具类库,负责提供应用系统中经常使用的操做。 |
|
TestLayer |
测试层文件夹 |
单元测试(可选),负责对其它类库的自动化单元测试。 |
|
|
|
|
|
- 文件夹分层法:应用分层采用文件夹方式的优势是可大可小、简单易用、统一规范,能够包括5个项目,也能够包括50个项目,以知足全部业务应用的多种不一样场景;
- 调用规约:在开发过程当中,须要遵循分层架构的约束,禁止跨层次的调用;
- 下层为上层服务:以用户为中心,以目标为导向。上层(业务逻辑层)须要什么,下层(数据访问层)提供什么,而不是下层(数据访问层)有什么,就向上层(业务逻辑层)提供什么;
- 实体层规约:DO是数据表对象,不是数据访问层对象,不是只能给数据访问层使用;DTO是网络传输对象,不是表现层对象,不是只能给表现层使用;BO是内存计算逻辑对象,不是业务逻辑层对象,不是只能给业务逻辑层使用 。若是仅限定在本层访问,则致使单个应用内大量没有价值的对象转换。以用户为中心来设计实体类,能够减小无价值重复对象和无用转换;
- U型访问:下行时表现层是Input,业务逻辑层是Process,数据访问层是Output。上行时数据访问层是Input,业务逻辑层是Process, 表现层就Output。
3、咱们的具体规范
此规范咱们用了四年,牵涉几百个应用,200多个研发人员,是一个成功的实践。接下来就借用本文提供下载的TripOrderService、TripSellerMVCSite这两个Demo来进行具体规范的说明,如下是截图:
3.一、项目命名规则
项目命名规则:{产品线英文名全称}.{子系统英文名全称+应用名}.{项目职责英文名全称},如:Trip.Seller.DTO。
3.二、业务逻辑层的项目规范
规范说明:
一、项目名的命名规则:{产品线英文名全称}.{子系统英文名全称+应用名}.xxxBusiness,如上图的Trip.Order.Business。
二、类名以Logic结尾,如上图的OrderLogic.cs。
3.三、数据操做项目规范
规范说明:
一、各数据操做项目名根据使用什么数据库进行分类,而后以DB为结尾,具体命名规则是:{产品线英文名全称}.{子系统英文名全称+应用名}.{使用什么数据库}DB,如上图的Trip.Seller.MSSQLDB。
二、若是涉及到多个数据库访问的,那么数据操做项目下的类文件须要按数据库名称(以DB为结尾)建立文件夹分开,如上图的TripOrderDB文件夹。
三、建议在应用中使用SQL语句,不使用存储过程。在数据库中不新增存储过程,但旧的存储过程能够继续使用和修改。
四、分页建议使用数据库(如SQLServer)的最新特性进行分页,并将每一个分页SQL直接写到应用中。
3.四、实体类项目规范
规范说明:
一、DTO项目命名规则:{产品线英文名全称}.{子系统英文名全称+应用名}.DTO,如上图的Trip.Order.DTO。
二、请求参数DTO实体类、响应DTO实体类存放规范以及其命名规则:
a、请求参数DTO实体类放在Request文件夹下,且命名规则为:以Request结尾,如上图的SearchOrderRequest.cs。
b、响应DTO实体类放在Response文件夹下,且命名规则为:以Response结尾,如上图的SearchOrderResponse.cs。
c、若是请求参数DTO实体类或响应DTO实体类的属性中有对象或枚举,那么这些对象所属的类、枚举放在DTO项目的Common文件夹下。
三、若是请求参数DTO实体类、响应DTO实体类有基类要继承,那么建议为基类取名为RequestBase.cs、ResponseBase.cs。且这些基类直接放在DTO项目的Common文件夹下。
规范说明:
一、VO项目命名规则:{产品线英文名全称}.{子系统英文名全称+应用名}.ViewModel,如上图的Trip.Seller.ViewModel。
二、各VO实体类,咱们用Controller名做为文件夹名进行分开,如上图的Order文件夹。
三、VO实体类名的命名建议:
a、请求参数VO实体类以Input/Form/Query结尾,如上图的SearchOrderInput.cs。
b、响应VO实体类以Output/List/Result结尾,如上图的SearchOrderOutput.cs。
BO实体类名以Model为结尾:
规范说明:
一、BO项目命名规则:{产品线英文名全称}.{子系统英文名全称+应用名}.BO,如上图的Trip.Order.BO;
二、以Model结尾,如上图的OrderModel.cs;
三、为了简化设计,BO项目为可选,可在DO项目里建文件夹。
规范说明:
一、DO项目命名规则:{产品线英文名全称}.{子系统英文名全称+应用名}.Entity,如上图的Trip.Seller.Entity;
二、若是涉及到多个数据库访问的,那么须要按数据库名称(以DB为结尾)建立文件夹分开,如上图的TripOrderDB文件夹;
三、表名+Entity结尾,如上图的OrderEntity.cs;
四、DO是数据表对象,供单表CURD操做。对于多表查询请求对象和返回对象,可定义新对象或使用现有对象(DTO/BO)来完成。
3.五、数据库链接配置规范
规范说明:
一、数据库链接的配置必须读写分离。
二、数据库链接字符串建议加密处理。
三、数据库链接配置名的命名规则:{以DB为结尾的数据库名称}_读写类型,如:
TripOrderDB_SELECT、TripOrderDB_INSERT。
3.六、配置文件方面的规范
规范说明:
一、全部配置文件(除Web.config文件外)都必须放到Config文件夹下。
二、全部配置文件(除Web.config文件外)按不一样环境区分开,具体命名规则是:{功能模块英文名}.{环境英文简称名}.config,其中本地环境的英文简称名是Dev,测试环境的英文简称名是Test,正式环境的英文简称名是Prod,如上图的AppSetting.Dev.config。
三、保持Web.config配置文件的干净,只留环境设置节点。
3.七、静态资源文件方面的规范
规范说明:
一、公共的静态资源文件(css、js、image等)放在另外的静态站点中,统一由前端进行开发和维护。通常,css文件放在css文件夹下,js文件放在js文件夹下,image图片文件放在img文件夹下。
二、与某项业务有关的js文件能够放到各自业务项目的表现层PresentationLayer下,以方便开发人员调试,js文件可放在项目的js文件夹下。
三、静态资源文件必须使用版本号管理,以防更新后因为客户端浏览器缓存而致使站点使用的依然是旧版本的静态资源文件:
<script src="~/js/order.js?v=@AppSetting.StaticFileVersion"></script>
4、写在最后
4.一、问题回答
问:服务的调用代码应该放到哪一层呢?
A表现层、B业务逻辑层 、C数据层、D公共层。
答:咱们的规范是统一放到数据资源访问层即C。上层提供服务,下层调用服务,中间处理业务逻辑。
问:如何组织好VO(View Object视图对象)、BO(Business Object业务对象)、DO(Data Object数据对象)、DTO(Data Transfer Object数据传输对象)呢?
答:一般有两种作法,限定访问范围和不限定访问范围,实际项目中可根据须要选择、折中或裁剪。咱们使用后者,将EntityLayer做为通用对象放到左侧,具体可参考实体层规约:“DO是数据表对象,不是数据访问层对象,不是只能给数据访问层使用;DTO是网络传输对象,不是表现层对象,不是只能给表现层使用;BO是内存计算逻辑对象,不是业务逻辑层对象,不是只能给业务逻辑层使用 。若是仅限定在本层访问,则致使单个应用内大量没有价值的对象转换。以用户为中心来设计实体类,能够减小无价值重复对象和无用转换。”
问:应用分层范例代码的编写须要注意些什么?
答:应用分层范例的代码要想写好,很是不容易,很容易引发争议,很难让全部人满意。咱们在具体实践时遵循如下几点:
- 应用分层范例的主要价值是明确层的职责和交互,每一个层的职责是什么,哪些要干,哪些不要干,以及层与层之间依赖和交互;
- 私人定制:减小通用帮助类的编写,若是每个应用中有大量相同的帮助类,这在架构层面上是有问题。在咱们的几百个线上应用中,尽管减小通用的代码,包括分页帮助类、数据库帮助类、缓存帮助类、MQ帮助类、日志帮助类、AOP帮助类、线程帮助类。业务应用的重点是为业务服务,每个应用都是特别的,都须要私人定制,极少有通用的代码,若是有,那么应该由框架或组件专门解决;
- 少便是多:应用的场景多,参考人员多,每一个人想法不一样,牵涉的时间长,因此尽可能只作你们都认同的规范、正确的事情,要自底向上、要减小有争议的代码范例,不然一个错误将会放大百倍、一个有争议的规范将会很难推行。
- 追求简单:代码编写可分为三个层次,简单、复杂、简单。第一简单是不知道的简单,第二个复杂是知道后的复杂,第三个简单是知道后有取舍的简单。范例代码要追求简单,既可轻松扩展支持复杂场景,又要简单到初级程序员也能操做。
- 内聚大于解耦:内聚是什么,内聚是部门内有共同的目标,而后你们紧密合做。解耦是什么,解耦是部门间各自职责明确,而后减小没必要要的链接。一个应用如同一个部门,应有一个共同的目标和职责,而后你们紧密合做。换句话说,应用内部应减小没必要要契约接口(如同公司间才签约合同),减小没必要要的依赖注入实现,减小没必要要且代价过大的解耦。一切以简单实用为主,以应用价值输出、应用的目标(接口或界面)为导向。
4.二、Demo下载