PHP 开发杂谈:对项目组后端开发的思考

2020-04-01  本文已影响0人  林子er

(团队内部技术分享摘要)

当前开发模式概要:

实践中,出现了以下问题:

  1. 业务泄露。本应属于Service的业务逻辑泄露到其他各层中(Controller、Repository、View等),而原本内容丰富的Service反而变成了贫血类。
  2. 全能Service,主要表现是超多的代码(如vshop的商品和订单的Service代码都在1000行以上)和多方面的功能。例如OrderService,几乎只要是跟订单相关的都在此Service中,而没有进行进一步的精细建模。
  3. 重复功能,表现在(特别是在跨模块时)重复的Service(如MemberService)和重复的方法。这些重复Service大部分地方一样,少数地方有区别。
  4. 一些Service中充斥着各种各样的查询功能(列表和单记录查询),让这个Service看起来很怪异。
  5. 贫血模型。基本都出现在Service层。多出现在查询的地方(列表、单记录)。因为我们默认约定Controller必须通过Service进行读写,而不能直接访问Repository,而实际上很多查询操作只需要Repository直接返回的数据即可,Service不需要做任何操作。
  6. 以技术视角划分模块和系统。这将直接导致4.2.2的问题。例如,所有的作业任务都放在一个系统(如message-center),而作业必然会出现各自业务逻辑(如会员合并),因而同一套业务逻辑必然会出现在多处。
  7. “前后台分离”。此处的分离是指按照前后台站点分离出完全独立的两套系统(如member-center何v-member,虽然商城是在一起,但基本也是两个完全独立的模块,而且v-shop这个名字起得很怪异,估计后本来准备分成两个,后来偷懒了搞成两个模块)。
  8. 经典的“framework问题”。这个问题几乎困扰着全公司人,而且还会继续困扰下去。framework问题的实质是敏捷团队/公司和传统架构思维的阻抗,它实际上正印证着康威定律:组织结构决定着软件架构。该主题将在后面详细讨论。
  9. OO无用论。一般在应聘时,我们都会在简历上写上“良好的OO基础”,但实际上我们骨子里是对OO持有怀疑和抵触的,而且实际开发中也是自觉不自觉地在面向对象框架中进行着面向过程的开发。问题是,为何我们那么抵触OO,那么喜欢过程式开发?另一个问题是,为何我们需要拥抱OO,相比于过程式开发能带来哪些好处?
  10. 二维设计。或者说:“当我拿着MVC这把羊角锤时,全世界都是钉子”。当我们用MVC这一单一设计模式去解决一切问题时,就陷入了二维设计。我们无法立体地看待问题,无论是思考(设计)还是编码都是在平面上进行着(如Controller -> Service -> Repository的形式化调用)。这种思维方式往往导致形式化的流程和约束,而形式化的东西又往往束缚了人们的思想,进而不再去思考。
  11. 耦合与内聚的纠缠。用辩证法的术语说,低耦合和高内聚是矛盾(辩证)统一的,高内聚的内部一定是高耦合的,低耦合的两个东西之间一定是低内聚的。当我们无法辨识出哪些元素之间应当是高内聚,哪些之间又应当是低耦合时,整体的高耦合便产生了(同时整体的低内聚也产生了,如分散在多处的同一套业务逻辑)——“牵一发而动全身”与“牵多发以动一处”。

相关设计模式和架构概述:

描述:高层模块不应该依赖于低层模块,二者都应该依赖于抽象。抽象不应当依赖于细节,细节应当依赖于抽象。

注意这里提到高层和低层,说明该原则主要是用来解决跨层的依赖问题(例如我们的Service和Repository)。一般,高层需要用到低层的东西(Service使用Repository),对低层产生依赖,那么当需要替换低层实现时,就需要改动高层代码。IoC要求,高层不应当依赖于低层实现,低层实现的变更也不应该影响高层。那么如何做到呢?接口,或说抽象。高层和低层遵守相同的抽象,并唯一据此抽象(接口)通信。

再从另一个角度理解这个问题,就是将对象的使用和创建分离,使用者(调用者,依赖方)只是使用对象,而不负责创建它,创建工作由外部负责,这样当需要更换被依赖方的实现时,无需修改依赖方代码。比如说,我们的Service需要使用Repository,传统做法是在Service里面new一个Repository,现在要将原Repository替换成其他的(如原来是Db仓储,需换成Redis或nosql仓储),则需要修改所有使用了该Repository的地方。当使用IoC时,由于该Repository是由外部创建的,只需要调整外部。

这种由原来的内部new变成由外部注入的实现方式,称为依赖注入(DI)。Yii框架里面的构造函数注入和服务容器(\Yii::$app->container)是DI的两种实现方式。

IoC是设计原则,DI是该原则的实现方式。

虽然该原则主要用来解决跨层依赖问题,但他同样适用于同层之间的解耦,如果这些依赖之间不是高内聚的话。(但是,每当出现同层的低内聚类之间的依赖时(例如聚合之间的调用),首先需要考察是否需要一个更高层次的协调者,例如一个Service)。

另外需要注意的是,不要滥用IoC。虽然IoC是用来实现松耦合的很好方式,但软件设计中除了“松耦合”原则还有“高内聚”原则,高内聚的类之间是可以强耦合的,否则,很容易出现过度设计的问题。

参见:http://www.tuicool.com/articles/JBRBzqm 《从百草园到三味书屋》,laravel作者著,里面有很好的示例诠释了依赖反转。

其他设计/架构模式:

现状分析:

上一篇 下一篇

猜你喜欢

热点阅读