开发人员在使用Spring应用是非常擅长谈论依赖注入的好处。不幸的是,他们不是那么真的利用它的好处,如单一职责原则,分离关注原则。如果我们一起来看看大部分Spring的Web应用程序,常见的错误的设计如下:

1.领域模型对象用来存储应用的数据(当作DTO使用),领域模型是贫血模型这样的反模式。

2.服务层每个实体有一个服务。

问题是这样很普遍,错误在哪里呢?

Spring的web应用程序之所以这样是因为他们做事物的方式一直都是这样做的,老习惯难改,特别是如果他们是高级开发人员或软件架构师,这些人捍卫这样做的论据之一是:我们的应用程序遵循关注分离的原则,因为它已经被分为若干层,每个层有自己的特定职责。

1. Web层负责处理用户输入,并返回正确的响应返回给用户。 web层与服务层通信。
2.服务层作为一个事务边界。它也负责授权和包含我们的应用程序的业务逻辑。服务层管理的域模型对象,并与其他服务和存储库层进行通信。
3.存储库/数据访问层负责与所使用的数据的存储进行通信。

分离关注(Soc)是分离计算机程序为不同的部分,每个部分有一个关注聚焦,一个典型的Spring Web应用在一定程度上遵循这一原则,但现实是,该应用程序有一个整体的服务层,它有太多的责任。更具体地,服务层有两个主要问题:

1.在服务层发现业务逻辑
业务逻辑被分散在各个服务层。如果我们需要检查一个业务规则是如何实现的,我们必须先找到它。这可能并不容易。此外,如果相同的业务规则需要在多个服务类,问题是,规则需要从一个服务到另一个简单地复制。这将导致维护的噩梦。

2.每个领域模型一个服务
这完全违反了单一职责原则,它被定义为如下:单一职责原则指出,每一个类都应该有一个责任,责任应该由类完全封装。其所有的服务应该狭义与责任相一致。(不应将原属于领域模型的行为方法等划放在服务中实现,对象不但有属性还有行为)

服务类有很多依赖,以及大量的循环依赖。更像网络紧密耦合和单片服务。这使得很难理解,维护和重用。这听起来有点苛刻,但一个Spring的web应用的服务层往往是最容易出问题的部分。幸运的是,所有的希望都不会丢失。

1. 我们必须将我们的应用程序的业务逻辑从服务层迁移到领域模型类中。

举个例子:假设我是一个服务类,你是一个域模型对象。如果我让你从屋顶上跳下来,你会喜欢我这样的决定吗?(跳下来会摔伤,自己没有脑子或被洗脑,变成僵尸,只听从执行,不思考自己的安全,这就是贫血模型的问题)

将业务逻辑从服务层迁移到域模型类有下面三个优势:

(1)我们的代码将以逻辑方式切割,服务层只要关注应用逻辑,而我们的领域模型关注业务逻辑。
(2)业务逻辑只存在一个地方,容易发现修改。
(3)服务层的源代码是清洁的,不包含任何复制粘贴代码

2. 将每个实体服务切割为单一目标的更小的服务。

比如,有一个单一服务类,提供对人员和用户账户的CRUD操作,我们应该将它分为两个独立的服务类:
第一个是对人员的提供CRUD操作
第二个是提供与用户账户相关的操作。

好处:每个服务类中有一个逻辑组职责。每个服务类的依赖较少,这意味着他们不再是紧耦合的源头。他们是较小的和松耦合的组件。服务类更容易理解,维护和重用。

这两个简单的步骤将帮助我们使得我们的应用程序架构更干净,有助于同行开发商提高生产力和幸福。

贫血模型不只是存在Spring应用,EJB更是普遍,只有架构分层是不够的,还需要更详细的逻辑分层,DDD领域驱动设计正是一个详细帮助建立丰富的有行为的领域模型的方法学。

理论大片,缺少些例子。让我看的云里雾里,姑且认为又是一贫血模式批判片。


前我的认知,打个比喻:领域模型就是人物刻画,比如萧峰、段誉、慕容复,这些人物有着各自的武功、特长、技能、秉性。如果他们都呆在各自的小房间与世隔
绝,那也就不会发生什么事情。而服务就是发生业务的舞台,比如“比武”这个服务,将这些领域模型放入进去,就会发挥领域模型——人物,自身的武功进行交
互。

我举个例子吧。以网上书店为案例,原帖:http://www.jdon.com/44851

业务需求:用户浏览选择书籍,放入购物车,生成订单,付款,等待书籍送上门。

原来服务设计的问题:
一个bookstoreService中又要处理订单,又处理购物车管理,还和Account等实体依赖,也就是说,Account一旦修改,bookstoreService也要修改,这些都违反单一职责和分离关注原则:

如果反映在代码,那么bookstoreService接口代码如下:

public interface BookstoreService{
//将书籍加入购物车
void addShoppingcart(Book book);
//从购物车去除
void removeShoppingcart(Book book);

void addOrder(Book book);

void removeOrder(Book book);

}

好了,如果看得懂这个代码和图,下面我们就要进行分离。

一般很容易将BookstoreService拆成OrderService和CartService。

其实这种区分可能是一种直觉,没有反映内在规律,也就是没有考虑Order和Cart两个实体之间的关系。那么如何考虑这两个实体之间关系呢?一般思路到这里就堵住了。

如果我们从DDD
事件角度考虑,Order和Cart为什么存在?是因为什么上下文存在?显然是在购书这个活动场景中才有这两个实体存在,而且这两个实体是一种先后关系,
先有购物车,后有订单,而且购物车好像是订单的预先准备,草稿一样,这决定了这两个实体应该在同一个上下文,也就是模块中,同时两者存在逻辑关系。

我们设计一个DDD聚合,聚合根分别是Order和Cart两个,可以认为主副之分。

那么订单Cart的类是如洗:

public class Cart{

    private int  id;
public void add(Book book){
..//将书籍加入购物车
} public void remove(Book book){
..//将书籍从购物车移走
} }

订单比Cart复杂一些:

public class order{

    private int  id;

   private boolean checkRule(){
../将书籍加入订单之前需要检查的规则
} public void add(Book book){
if (checkRule()){
..//将书籍加入订单
}
} public void remove(Book book){
..//将书籍从订单移走
} public float count(){
//统计订单总金额
} public Cart getCartForUser(User user){
//为某个用户创建一个购物车
} public Order confirm(Car cart){
//购物车确认,生成订单
} }

而BookStoreService就变成OrderService

public interface OrderService{

       Cart getCart(User user);//派给一个用户购物车,内部委托Order.getCartForUser实现。

       Order createOrder(Cart cart);//创建一个订单,内部委托Order.confirm方法实现

       //其他大粒度服务 如 订单跟踪 多组织销售订单等等复杂服务
}

Order和Cart从之前的贫血模型,也就是只有属性的setter/getter,没有职责行为,到现在富模型,有自己的行为,原来的行为跑到bookstoreService服务里面去了。举个例子:
服务代表一个张三, 李四代表贫血模型,李四就是一堆数据集合,张三是服务,张三获得李四实例以后,想对李四干什么就干什么,让它从屋顶跳下来它就得跳下来。

通过以上行为归类以后,服务里面的方法就变得更加面向业务,比如我们通常讲加油服务,其实加油这个服务内部涉及很多流程,需要实体加油箱,需要加油员拿起枪,需要我们付费,不是简单的一个过程。如果这些复杂过程都写在加油服务里面,无疑变成面向过程,一堆乱麻了。

有的服务后面的流程需要涉及多个部门系统,比如请假单,需要生产部门 人事部门和总经理办公室都要查阅同意,你不可能将这三个部门的处理细节都写在一个请假服务中,况且这三个部门使用的软件厂商可能不同,有微软有Java的。

Spring Web 应用的最大败笔的更多相关文章

  1. Spring web应用最大的败笔

    第一篇 介绍下IOC DI Spring主要是业务层框架,现在已经发展成为一个完整JavaEE开发框架,它的主要特点是IoC DI和AOP等概念的融合,强项在面向切面AOP.推出之初因为Ioc/AOP ...

  2. 《SSM框架搭建》三.整合spring web

    感谢学习http://blog.csdn.net/zhshulin/article/details/37956105#,还是修改了spring到最新的版本和接口开发示例 根据前一篇日志,已经有了myb ...

  3. Spring Web应用的最大瑕疵

    众所周知, 现在的Spring框架已经成为构建企业级Java应用事实上的标准了,众多的企业项目都构建在Spring项目及其子项目之上,特别是Java Web项目,很多都使用了Spring并且遵循着We ...

  4. Spring Framework------>version4.3.5.RELAESE----->Reference Documentation学习心得----->Spring Framework中的spring web MVC模块

    spring framework中的spring web MVC模块 1.概述 spring web mvc是spring框架中的一个模块 spring web mvc实现了web的MVC架构模式,可 ...

  5. spring web.xml 难点配置总结

    web.xml web.xml是所有web项目的根源,没有它,任何web项目都启动不了,所以有必要了解相关的配置. ContextLoderListener,ContextLoaderServlet, ...

  6. 菜鸟学习Spring Web MVC之二

    有文章从结构上详细讲解了Spring Web MVC,我个菜鸟就不引据来讲了.说说强悍的XP环境如何配置运行环境~~ 最后我配好的环境Tomcat.Spring Tool Suites.Maven目前 ...

  7. 4.Spring Web MVC处理请求的流程

  8. 1.Spring Web MVC有什么

    Spring Web MVC使用了MVC架构模式的思想,将web层进行职责解耦. 同样也是基于请求驱动的,也就是使用请求-响应模型.它主要包含如下组件: DispatcherServlet :前端控制 ...

  9. Spring Web Flow使用

    就当我写(嘘,抄)着玩的. 使用Spring框架的一个子项目--Spring Web Flow来建立和管理Web应用和UI流程. 第一节:使用Spring Web Flow在一个Spring MVC应 ...

随机推荐

  1. GridView Print and Print Preview

    sing System.Linq; using System.Printing; using System.Windows; using System.Windows.Controls; using ...

  2. JAVA中如何用接口实现多继承和多态 (非常好)

    ---------------------------------------------------------------多态1.JAVA里没有多继承,一个类之能有一个父类.而继承的表现就是多态. ...

  3. android下通过app名字打开程序(activity)链接

    Version:0.9 StartHTML:-1 EndHTML:-1 StartFragment:00000099 EndFragment:00004599 1.手机遥控器模拟快捷键启动app 刚开 ...

  4. 神经网络权值初始化方法-Xavier

    https://blog.csdn.net/u011534057/article/details/51673458 https://blog.csdn.net/qq_34784753/article/ ...

  5. Python中的高级turtle(海龟)作图(续)

    四.填色 color 函数有三个参数.第一个参数指定有多少红色,第二个指定有多少绿色,第三个指定有多少蓝色.比如,要得到车子的亮红色,我们用 color(1,0,0),也就是让海龟用百分之百的红色画笔 ...

  6. Linux中./configure、make、make install详解

     ./configure && make && make install详解 2010-08-03 23:30:05 标签:休闲 ./configure &&a ...

  7. NPOI 2.1.1 系列(2) 使用NPOI读取List或者datatable数据生成 Excel文档 ;Npoi生成 xlsx 2007以上文档

    结合上一篇文章  NPOI 2.1.1 系列(1) 使用NPOI读取 Excel文档 ;NpoiExcelHelper 导入导出 2003格式 2007格式的 Excel; Npoi 导出 xlsx ...

  8. zabbix server端自动发现和zabbix agent端自动注册

    一.zabbix自动发现 利用zabbix的discovery功能可以实现自动批量添加主机的功能. Zabbix自动发现实现自定义主机名: 通过自动发现添加的客户端主机的Host name 是以IP地 ...

  9. 每天一个linux命令:cd命令

    Linux cd 命令可以说是Linux中最基本的命令语句,其他的命令语句要进行操作,都是建立在使用 cd 命令上的. 所以,学习Linux 常用命令,首先就要学好 cd 命令的使用方法技巧. 1. ...

  10. JAVA中的数字运算+号与字符串+号

    (1)当“+”两边是非数值类型,“+”就被看作连接符. (2)当“+”两边都是数值类型,“+”就被看作算术运算中的加号. (3)当“+”一边是非数值,一边是数值类型,“+”就被看作连接符.