开发中有感而想,然后查了一下,发现这个观点不错,以后开发尽量业务代码还是写在service层,然后controller层简单点,看起来也清晰。
首先,分三层并不仅仅是java的专利。几乎所有类似这样的服务都大概会为三层。这并不是什么金科玉律,而是长期实践慢慢自发形成的。
对于一个功能当然可以写一段很长的代码实现,包括,检查参数对不对,判断业务规则,对数据进行访问,包装结果后返回。如果逻辑简单,大概几十行代码也就够了。除了java,写PHP的,写Ruby的,写nodejs的,在第一次写一个新的业务接口时大概也都会这么写,可能写的比java还短。
如果逻辑不变,稳定性要求也不变,这段代码当然可以这么保持下去。我举个例子,比如想实现一个判断服务是否存活的接口/health,逻辑就是简单的发一个“select 1”到数据库。如果成功返回就证明从接口到DB的链路是通畅的。这样的代码几行就写完了,而且几乎永远也不需要改。此时简单写就可以了。
但作为业务逻辑,大概率
是会不断变化发展的。有可能
- 接口形式会增加,从支持http api变层支持某种rpc,支持通过websocket发起上行请求。
- 接口的访问控制可能会变。同样的接口,普通用户访问,后台管理员,合作伙伴访问,给的参数和管控都不一样。
- 业务逻辑会变,引入不同的规则和用来判断规则的数据。
- 存储层可能会变。数据存储可能要变(比如更改数据库,又或者关系性数据库改成kv),单库变分库,可能会引入缓存,增加存储异构降级……
- 接口需要引入其他业务的逻辑,以及其他存储的访问。原来访问单表即可,现在要组装2个rpc的接口和3张表的结果。
- 业务逻辑可能会被复用。或者挪到上游或者下游。
- ……
当以上这些事情不发生,随便怎么写都无所谓。但一旦某一件事发生了,不用别人说,一个正常的程序猿自己就会想着“怎么切一刀”,然后把不变的留住,然后改变需要变的部分。而且你会发现很天然的,在维护接口逻辑的代码,维护业务逻辑的代码,维护存储和实体转换的代码天然就是很适合拆分的。
所以通常,如果未来大概就要这么切,那还不如一开始就把层次分好。后面要改哪部分就直接干了。顺便的,还可以把UT针对某个层次来做。比如一开始就是业务逻辑相对更复杂一点,就可以mock掉dao层,不管controller层,只针对service进行测试。
如果这样的分层可以形成比较好的约定俗成,就能做一个代码模版,每次要写这种代码,直接先把骨架生成出来。这也能更容易形成代码上的规范。大家写的代码不会出现说有的人不分层,有的人分两层,有的人分4层,彼此review代码时还要先熟悉对方的代码风格的情况。
当然了,一开始简单时写到一起。到后面需要时再重构为分层,也是可行的。只不过按照目前的行业的运作方式,往往无法落地。因为新加入代码的人1)并不熟悉原作者的思路;2)绩效总是和产出效率挂钩,倾向于最小的改动来实现需求,而非在花功夫重构,花费更长的时间和引入更大的风险——重构不仅仅会动当前的功能,而要把相关的技术债一次性解决。有测试case吗?覆盖率高吗?能做线上diff吗?有灰度方案吗?——这些附属的能力有需要投入大量资源开发和积累。
因此这种重构只有技术实力和文化较强的组织才有可能做到。但这往往就又会推高成本(好的程序猿贵啊),无法被一般企业接受。因此“先简单写,后面再重构”的路线在绝大场景下是不现实的,最终会演变成屎山。
因此如果有个代码架子一开始好歹规划一下,短期看虽然有些无用,浪费开发者的时间在不同层上反复折腾。但长期看对于代码的可维护性是有好处的。
所以,为这个事情郁闷的开发者只不过是觉得自己做了一些冗余劳动而已。但从技术演进角度,这才只是个开始…… 而对于很多技术能力不强的组织,这点规范是为数不多的可以推动和保持的技术点了。老实搞吧。
但是,这绝对不是说这么简单分3层就足够了和一定管用。根据业务需要肯定需要额外的设计。比方说service可以很复杂,可以做更贴切的领域驱动设计。存储层也可以做很多抽象来屏蔽kv,关系性数据库,以及不同私有化部署之间的差异。但这就不是一线开发需要操心的事情了。
作者:大宽宽
链接:https://www.zhihu.com/question/431911268/answer/2266320559
来源:知乎