SpringMVC项目的若干设计问题

在实施SpringMVC项目过程中,会遇到一些常见设计问题,这里总结一下。

Service层的作用

Service对象与Contrller、(特别是)DAO对象的功能似乎重叠,为什么需要独立的service层?理由有下面几个:

  • 划分事务边界:DAO对象里的操作(方法)一般不标注@Transactional,相对来讲是原子操作,没有什么业务逻辑在里面。而Service对象封装的是业务逻辑,简单的情形就等同于DAO里的一个操作,复杂的情形是依次调用多个DAO的操作,这时对Service里的操作标注@Transactional更有意义。
  • 支持多格式输出:我们知道Controller对象的作用是控制web流程,而不是处理业务逻辑。所以当业务逻辑相同,但要输出不同格式(json/xml等等)或跳转到不同页面时,处理这些输出的逻辑应该写在Controller里,而它们都调用相同的Service对象。
  • 支持单元测试:既然所有的业务逻辑都被写在了Service里,那么我们只要针对Service写单元测试,就能够达到测试业务逻辑的目的。
  • 更好的利用AOP:例如我们可以截获postCommit并写入日志,达到记录每项业务执行成功事件的目的。缺少了Service层就没那么容易了。

可以看到Service的主要作用是包装业务逻辑。那么什么是“业务逻辑”呢,简单的update()、create()、delete()或稍复杂的find()都不属于“业务逻辑”;而例如用户登录功能,需要查询数据库并对传入参数进行一些检查,就可以称作“业务逻辑”了。

例如mybatis提供的jpetstore示例里的OrderService,提供了getOrder()方法,方法里会调用orderDao获取订单的基本信息,并调用itemDao获取订单里商品的库存信息。

再举个例子,UserService里有一个名为getUser(int userId)的方法,返回类型为User对象。在这个方法里,除了查询数据库里的user表外,常常还要查询favorite表、comment表等其他表,并返回收藏数、评论数等统计信息,因此除了UserDao意外还要利用到FavoriteDao、CommentDao等。这些就属于业务逻辑。

再举个例子,封装站内消息的MessageService里,每次返回消息列表的同时,要将列表里所有的消息置为“已读”状态,这个操作也属于业务逻辑。

如果我们的系统没有那么多业务逻辑,是否一定要在Service层里重复包装所有的DAO呢?答案见仁见智,如果你追求设计模式的完整性并且需要前面列出的那些便利性,可以这样做,代价就是工程里多出几十个java类;或者,对没有什么业务逻辑的部分在Controller里直接调用DAO,工程会显得简洁一些(参考这个回答)。

参考资料:

Don't repeat the DAO!

simple spring app - why use service layer?

jpetstore-6 demo from mybatis

POJO对象的Derived属性处理

注:POJO是Plain Ordinary Java Object的缩写,derived属性表示POJO里那些值由其他属性决定的属性。

问题:数据库里有一个Invite表,记录邀请码对象,表结构里有expire_time列,对应的Invite类(POJO)里有expireTime属性。客户端显示邀请码列表时,要对已过期的邀请码做变灰处理,那么如何传递邀请码是否已过期状态给客户端?

方案1:客户端判断expireTime是否小于当前时间,如果true则按已过期处理。这个方案有两个问题:a)相当于将判断邀请码是否过期的业务规则交给了客户端,这个规则未来可能有变化,例如邀请码如果已被使用也是过期/失效;b)客户端本地时间可能不准确,导致判断结果不准确。

方案2:在Invite类里增加一个名为expired的属性,服务器端从数据库里取出记录时,补充这个属性值(可以在service层完成)。

方案3:在Invite类里增加一个isExpired()方法,将判断邀请码是否过期的业务规则写在这个方法里。使用jackson将这个对象序列化json时,jackson会自动发现这个方法并转换为一个名为“expired”的json属性。

本人推荐第三种方案。

独立的用户系统

很多项目都有用户系统,能否设计一套完全独立的用户系统,提供通用的接口,以便在各个项目之间复用?

这个系统通过json格式的api提供以下几个主要功能:

  • 用户管理:用户注册、登录,管理员可管理
  • 收藏功能:例如收藏一个商品
  • 投票功能:例如对商品投票/打分,1~5
  • 评论功能:例如对商品进行评论,支持对评论的回复和点赞
  • 跟随功能:即follow,跟随后即成为对方的粉丝
  • 时间轴:类似微博时间轴
  • 邀请码功能:注册时需要提供邀请码,例如内测
  • 统计功能:以上各类对象的统计,至少支持用户、目标对象两个维度,例如统计指定用户的投票数量,或统计一个商品的投票数量。

为实现这样一个系统需要解决好下面几个问题:

(待续)