开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天,点击查看活动详情
在做设计时,经常强调高内聚低耦合,其实就是将相似的功能统一进行管理,不相关的最好分开,用好“统一”原则在设计时能更好的划分边界,减少功能依赖,降低复杂度。
单一职责:一个方法只负责单一的功能实现。
统一职责:相似的功能都交给我。
入口统一
数据通常都有使用(读)、管理(写)两部分属性,使用方通常有很多,管理方也一样。
如果在设计时不加以控制,那么势必会很乱。
此时假如有个需求,要求在写入数据时对某个字段做转换,那么就需要改所有调用的地方。
所以,我们都希望能够有一个统一的入口来去维护数据,对所有的读、写操作都进行收口,降低依赖,就是我们通常认为的单一职责,这个功能只做一件事情,这份数据只有一个操作入口。
对于数据的操作要有统一的入口,这个理念理解很容易。那么在设计中为何会忽略呢?往下看:
通常在服务划分层面我们通常都能考虑,但是在服务内部就很容易缺少规划。
通常我们都是上图这种模式,一个统一的UserDao作为用户数据操作入口,供其他服务调用。但是这样真的好么?
这里提一下DDD的分层思想。DDD将传统的三层转为四层,独立出单独的领域层,便是为了做统一。
传统的三层架构,如果UserService被多个服务引用,要么采用上图的形式,多个服务引入UserDao作为入口来进行操作,要么多个服务引入UserService作为入口进行操作。
引入UserDao,虽然依赖关系简单,但是对用户的一些业务逻辑,势必会落在UserDao或者其他服务自身逻辑中。
引入UserService,虽然保证了UserService业务的纯粹,但是依赖关系就很复杂,同级相互依赖。
因此,DDD将领域层作为独立的功能服务,统一实现所有该业务相关的功能,领域之间要保持独立,禁止相互调用,应用层负责领域编排,专注于业务逻辑。这样一来就非常的清晰。
数据通常都是有业务属性的,只会归属于一个业务,所以正确的做法是,统一入口归属于领域服务,只能由一个service来操作数据,而非归属于DAO所有的业务都能去调用。
职责统一
以打电话场景为例:假如通话与计费为两个服务。
通话包含:拨打电话,挂断电话,通话控制....
计费包含:通话时长、长途漫游...
就以通话时长来说:通话结束后,调用计费服务执行扣减。我们通常都能够想到统一在计费服务提供扣减入口,通话结束调用扣减即可。
但是这里有个疑问,计算费用的操作归属于通话服务还是计费服务?
放在通话服务
对于计费服务来说,功能就是别人告诉我扣多少,我就扣多少,不需要关注上层业务服务是如何计算的。
不仅是话费,甚至短信、彩信、流量、服务等各种扣费,我总不能来一个业务就写一套业务规则吧。
放在计费服务
如果放在上层业务侧,那么就是上层业务说了算,业务说口多少就扣多少,作为底层反而像是一个傀儡。而且,费用计算明显只和费用相关,对于通话服务没什么用处。
如果放在计费服务,对于一些上层调用失败,计算不准确,甚至不调用问题就能很好的避免了,此时费用来源不依赖与上层计算,整个计费服务就趋于完整、独立了。
虽然这样新增扣费业务需要改动计费服务,同时修改扣费业务(修改通话计费规则:5秒以内不扣费)也只需要修改计费服务。而且,计费本来就是要统一所有费用相关功能,包括规则计算。
甚至还可以通过事件解耦。