Web编程规范之三层架构设计规范
本篇是我对Web开发规范中关于三层架构设计规范的一些浅见。虽然三层架构是比较普通,也比较简单的架构设计模式。但是随着业务的增长,涉及到的对象越来越多,处理的逻辑越来越复杂。这其中难免会出现设计不当,从而导致业务报错或逻辑代码混乱等问题的出现。下面我就来简单的谈一谈我是如何设计的?(注:本篇的见解是在基于当前比较流行的MVC设计模式,不知道什么是MVC设计模式的小伙伴,请自行查找相关资料。)
逻辑划分
在三层的架构设计中通常会采用如下设计方案对逻辑进行划分:
Controller (表现层)
在MVC设计模式中使用Controller代表,它的主要作用为:
- 针对用户的输入进行验证
- 构建对象并调用 Service
- 返回给用户VO或VM
Service(业务层,也叫服务层)
- 接收Controller传过来的对象
- 编写业务逻辑
在编写业务的时候有几个技巧:
1)对于所有更新操作,都把BO对象当成参数来接收,把更新后的对象当返回值
public OrderBO cancel(OrderBO orderBO) { return orderBO; } /** 取消订单. */
public OrderBO finish(OrderBO orderBO) { return orderBO; } /** 完成订单. */
public OrderBO paid(OrderBO orderBO) { return orderBO; } /** 支付订单. */
2)对于查询而言,如果对象不存在,直接返回null或抛出异常均可。当返回为null时,上层调用需要进行判null处理
3) 如果是更新类操作,被操作的对象不存在时,这个时候一定要抛出异常
4)在需要抛出异常的地方使用日志详细的记录
5)如果在Service的方法中涉及到多个更新操作,需要在Service中开启事务。即使该方法的上层调用Service开启了事务,该方法也应该开启。以创建订单为例
@Transactional
public OrderBO save(OrderBO orderBO) {
// .....
OrderMaster orderMaster = new OrderMaster();
BeanUtils.copyProperties(orderBO, orderMaster);
// 存储订单
orderMasterRepository.save(orderMaster);
productInfoService.decrStock(
orderBO.getOrderDetailList().stream()
.map(e-> new StockBO(e.getProductId(),e.getProductQuantity()))
.collect(Collectors.toList())
);
return orderBO;
}
@Transactional //即使上层调用有这个标记,这里最好也加,因为可能会单独调用
public void decrStock(List<StockBO> stockBOList) {
stockBOList.stream().forEach(e -> {
ProductInfo productInfo = this.findOne(e.getProductId());
if (productInfo == null) {
throw new WxbpException(ErrorCodeEnum.PRODUCT_NOT_EXIST);
}
Integer remainStock = productInfo.getProductStock() - e.getProductStock();
if (remainStock < 0) {
throw new WxbpException(ErrorCodeEnum.PRODUCT_STOCK_ERROR);
}
productInfo.setProductStock(remainStock);
repository.save(productInfo);
});
}
- 调用其他Service或Repository
- 返回BO对象或POJO对象
Repository(数据访问层)
- 由业务层调用和数据库进行交互来实现对数据的CRUD操作。为了提高效率,这一般会使用ORM框架,例如Mybatis或Hiberinate。
- 返回POJO对象
模型对象定义
除基本的架构外,还会存在很多其他的实体对象,这些对象包括:
- POJO: 普通的Java对象,映射数据表,内部属性均对应于数据表的列名。用于接收数据库的返回结果。通常在Service和Repository之间进行数据传递。
- BO : 业务对象,是针对整个逻辑单元构成的对象,例如订单业务对象不仅包含订单的信息,还包含订单明细。该对象主要在Controller和Service 之间进行数据传递,可以由不同的POJO组成。如果POJO的属性与BO相同时,可以不用创建BO对象而直接使用POJO对象来充当BO,同理其他模型对象也如此。
- VO : 视图对象,这类对象仅用于返回用户可以看到的信息。例如用户请求产品信息时,返回了一个产品业务对象,我们不能直接把这个对象返回给用户,因为对象的内容包含了很多敏感信息(库存,采购价等),需要进一步对其处理,过滤掉这些敏感信息,从而构成了视图对象。
- VM:视图模型,和VO很像,也是用户返回用户可以看到的信息。这个概念是MVC设计模式中的,对应于MVC中的M,这个对象通常对应于整个页面的信息,由一个或多个VO。例如个人中心的页面,不仅包含用户信息,还有订单信息,积分的信息等。
- PO:参数对象,这个对象映射到Controller方法中的参数。当Controller需要的参数过多时,我们通常使用对象进行参数的接收,这个对象就是请求参数对象。

简化理解
对于上述三层架构中涉及的业务和对象,我们可以简化这样理解,假设有这样一个情景。
BOSS把总监(Controller)叫到办公室 ,说:公司经营的状况如何了,给我出个总体报告我看看。总监收到指令后把A部门的经理(Service)叫到了办公室,并说:我需要近一年A部门的总结报告。A部门经理需要收到请求后,开始使用报表工具做事(Repository),当遇到了自己处理不了的地方又会叫其他有关联的经理帮助处理(调用其他的Service),最后完成了整件事。把报告交给了总监,总监看过之后发现没什么问题,又把B部门的经理叫到了办公室,并说了需求(有时候需要给B看A部门的报告的部分或所有结果), B部门的经理收到请求后,也是同A一样的处理办法,把报告交给了总监。此时总监这里已经有了BOSS要的所有数据,然后进行汇总,交给BOSS。而在这个过程中,为保密和方便各方之间的沟通,BOSS和总监之间的对话可以理解成 PO -> BO 的过程,总监和经理的对话可以理解成BO->POJO的过程,当然有些经理对业务理解比较透彻就不需要转换了(直接使用BO,此时BO=POJO)。而这个对话过程反过来就是,进行逐层汇报的时候,应该言简意赅,一语中的,至于你怎么做的,就不要向上层反映了。最后达到BOSS那里只要看到满意的结果就好。
Service分层设计
Service分层设计是什么意思呢? 让我们来看一个例子
public interface OrderService{
/** 查询一个订单. */
OrderBO findOne(String orderId);
/** 取消订单. */
void cancel(OrderBO orderBO);
}
在上述的代码中OrderService包含根据orderId获取订单这个操作。那么对于买家而言调用此方法是需要对买家进行安全性验证的,非该用户的订单则不能返回给调用方。为了代码的整洁性我们衍生出来一个BuyerService,这个BuyerService主要处理和买家相关的业务,对买家进行验证后在进行调用相应的Service。
public class BuyerService{
public OrderBO checkOrderOwner(String userId,String orderId){
OrderBO orderBO = orderService.findOne(orderId);
if(!orderBO.getUserID().equals(userId)) { //抛出异常; }
return orderBO;
}
/** 查询用户的订单列表. */
public OrderBO findOrderList(String userId,String orderId){
OrderBo orderBO =checkOrderOwner(userId,orderId);
return orderBO
}
}
这个分层理解起来也和三层架构类似,你当然可以在Controller中直接使用 OrderService 进行操作,就像你也可以直接使用Repository操作一样,只不过是比较麻烦而已。更好的设计有助于提高代码的整洁性,可读性和可维护性。
提升效率小妙招
如上的设计中涉及复杂业务的同时又衍生出了很多不同种类的对象,在开发的过程中会难免会造成代码的可读性,安全性和可维护性降低,下面我们看一些可以提升整体的开发效率的小妙招。
- 对象转换工具:实现不同对象间的转换操作,可以由一个对象拆分为多个对象,也可以由多个对象组合成一个对象。
- 统一返回的对象:这一点对调用方而言很重要,如果我们的返回值结构多种多样,这会让调用方不知如何处理,从而增加使用难度。故需要统一的返回值结构,通常返回值的JSON结构如下:
{ "code":"0","msg":"操作成功","data":"需要返回给用户的数据"}
或
{ "code":"错误的状态码","msg":"错误的信息" }
- 统一返回对象工具:该工具主要是封装一些快捷返回【统一返回的对象】的方法,避免大量冗余的代码。
- 全局的异常处理拦截器:虽然上面有了【统一返回的对象】,但是当抛出异常的时候,返回给用户的信息破坏了这个对象的结构,导致用户处理起来困难。所以需要全局的异常拦截器,拦截异常然后处理成【统一返回的对象】格式。
- 统一异常类型和异常代码:如果在代码中存在各式各样的异常类型和异常信息的硬编码,一经修改就有可能导致遗漏或逻辑报错等问题,让代码难以维护。所以需要统一的异常类型和集中管理异常代码是必要的。
Web编程规范之三层架构设计规范的更多相关文章
- Java Web(八) MVC和三层架构
今天终于认识到自己的渺小,想起了一句话,不努力机会来了你也抓不住,不要一直想一步登天,一直沉浸在白日梦的美好之中,一步一个脚印,有多大能力做多大的事情,走程序员的这条路,那么我就想去好公司,一切都以进 ...
- 三层架构和MVC模式
目录[-] 1.三层架构 2.MVC 2.1 标准的MVC(Model-View-Controller) 2.2 Web MVC 3.三层架构和MVC的区别与联系 1.三层架构 三层架构(3-tier ...
- 浅谈C++三层架构
浅谈C++三层架构 三层架构并不是MVC,MVC是一个很早就有的经典的程序设计模式,M-V-C分为三层,M(Model)-V(View)-C(Control). web开发中的三层架构是指:数据访问层 ...
- Web项目的三层架构和MVC架构异同
http://www.cnblogs.com/zhhh/archive/2011/06/10/2077519.html 又看到有人在问三层架构和MVC的关系,感觉这种问题有点教条化了.因为它们都在逻辑 ...
- 【转】.NET 三层架构 中 DAL+IDAL+Model+BLL+Web
其实三层架构是一个程序最基本的 在.Net开发中通常是多层开发 比如说 BLL 就是business Logic laywer(业务逻辑层) 他只负责向数据提供者也就是DAL调用数据 然后传递给 客户 ...
- MVC三层架构编程(Dao、service、servlet 之间的关系)
木哈哈~先开心一会儿,人生的第一篇博客aaa.我一定好好写.不过之前也没怎么看别人写过,还是有点小激动呢,加油.好好总结,会总结的宝宝才会有提高! 今天想总结一下mvc三层架构模型编程,宝宝学习不怎么 ...
- JavaWeb -学生信息管理实践(JDBC+web+三层架构+DBUtil构造思路)
前言: 1 该程序使用动态web项目 2 该程序使用SQL server需导入对应包( 具体可看前篇----JDBC的使用) 3 三层架构思想: ①表示层 前台:jsp/html等 作为前台与用户交互 ...
- MVC三层架构模式编程思想 JSP-Servlet-JavaBean
MVC(Mdodel-View-Controller)编程模式.把一个Java应用分成三层:模型层.视图层.控制层,各层分别实现各层的功能,整个过程见下图就一目了然了. watermark/2/tex ...
- MVC设计模式与Java Web经典三层架构
MVC设计模式 MVC的概念 首先我们需要知道MVC模式并不是javaweb项目中独有的,MVC是一种软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model).视图(View)和控 ...
随机推荐
- django项目mysite
项目建立 建立项目mysite 各文件和目录解释: 外层的mysite/目录与Django无关,只是你项目的容器,可以任意重命名. manage.py:一个命令行工具,用于与Django进行不同方式的 ...
- 【DeepCTR】
DeepFM: https://www.jianshu.com/p/6f1c2643d31b CCPM,FGCNN: https://cloud.tencent.com/developer/artic ...
- 【Leetcode_easy】893. Groups of Special-Equivalent Strings
problem 893. Groups of Special-Equivalent Strings 题意: 感觉参考代码也是有点问题的... 参考 1. Leetcode_easy_893. Grou ...
- 未处理的异常:system.io.file load exception:无法加载文件或程序集“ 。。。。 找到的程序集的清单定义与程序集引用不匹配。
问题描述: 添加控制器的时候,突然就报了这个错: Unhandled Exception: System.IO.FileLoadException: Could not load file or as ...
- RabbitMQ官方教程四 Routing(GOLANG语言实现)
在上一教程中,我们构建了一个简单的日志记录系统. 我们能够向许多消费者广播日志消息. 在本教程中,我们将向其中添加功能-我们将使仅订阅消息的子集成为可能. 例如,我们将只能将严重错误消息定向到日志文件 ...
- zabbix4.2+grafana搭建骚气的监控运维平台
Zabbix 是一个企业级分布式开源监控解决方案,其监控与告警功能十分强大.Grafana是一款开源的可视化软件,可以搭配数据源实现一个数据的展示和分析:Grafana功能强大,有着丰富的插件.两者结 ...
- 03.linux入门命令
1.linux命令的格式 命令 [选项] [参数] eg: ls ls -l ls -l /home 注: a.选项与参数不一定存在 b.选项用 "-" 来指明 c.命令,选项,参 ...
- PHP替代session的方法
PHP替代session的方法 服务器集群的时候 会发现session的问题 一般采用redis 来代替 用账号作为key 因为redis能主从 所以打算用替代session的方法1 cookie代替 ...
- 在ensp上简单的配置交换机
在ensp中我们经常用到交换机,但是我们还没有配置过,下面我们来学习一下怎样简单的配置交换机的速率,双工模式等. 全双工:同时发送和接收数据 半双工:只能在一个时间做一件事 速率:交换机每秒 ...
- Copy-On-Write技术
通俗的解释,假定多方需要使用同一个资源时,没有必要为每一方都创建该资源的一个完整的副本,反而令多方共享这个资源,当某方需要修改资源的某处时,利用引用计数,把该处复制一个副本,再把跟新的内容写入该副本中 ...