@
vczyh #37 再仔细想一下你说得这两个需求,是不是都用户界面需要的功能,这俩需求即不是 UserServive ,有也不是 OrderService 的目标。
这里的层和模块大致可以如此划分:
用户界面层:用户(再细分为用户注册登录等身份识别部分、我的订单等个人资料纯查询部分)、下单、。以上模块之间存在沟通,但不存在依赖。例如商品结算的时候,虽然它要调用下单方法,但它并不依赖订单模块,因为它这里只需将用户 ID 、商品 ID 等不变的值,作为方法参数传递出去即可。实际上这里商品模块是不能调用这一层的下单方法的,它直接调用的是业务逻辑层的下单方法,由业务逻辑层的下单方法处理完成之后再转给用户界面层的下单模块。这里完全可以认为,购物车结算的时候,购物车只需要把参数扔出去即可,谁负责接受并不归它管。你要有足够的资源,这里完全可以把方法调用模型,换成发布订阅 /推送模型。
业务逻辑层:UserService (这一层就只有身份识别相关的了,各种“我的资料”不归它管了)、OrderService (含写方向的下单、读方向的查询,以及所有与订单数据相关的业务)、UserOrderViewService (这是用来解决用户订单关联查询的,如果只是我的订单这种查询功能,用不到这个,那个靠订单模块就够了,但如果用户界面层有更复杂的查询就可以考虑加上这个)。这里涉及到一些读写分离的思想,但还没到读写分离设计模式的地步,用起来还是很容易的。
对于上面的划分,有几点需要说明一下,如果没理解的话看完下面的回去再看一遍应该就能理解了。
第一,方法 /函数调用,不等于依赖关系,Java 早期的简单分层模型,让人习惯了 Controller 只调用 Service 、Service 只调用 Dao 的模式,进而误以为调用方法就是依赖被调用方法的,实际情况不是这样。你可以在编写 Dao 前编写 Service ,但在 Dao 接口正式编写出来前,你这个 Service 是绝对用不了的,连打桩单元测试都不行,这是依赖。而方法调用不一定是这样,比如上面的下单这个处理,虽然最终运行的时候,是商品模块的某个方法,调用了下单模块的某个方法,但是商品模块可以自行独立开发然后打桩测试,完全不用管对方是否已经完成(双方都可以这样,甚至都不用提前协商好方法参数声明),这是没有依赖关系的方法调用。 简单来说,没有对方就能自行打桩测试的,是无依赖关系的方法调用。
第二,同层之内允许从上到下的调用链,而如果是同层同模块内部,允许双向依赖——不分场合的禁止双向依赖,是违反内聚原则的。
第三,有些跨多个模块的信息,可以设计成不变值(在 DDD 中有专有名词:值对象)。例如向商品 ID 、名称、价格这些信息,可以组合成“商品信息{ID 、名称、当时的价格、当时的描述信息}”不变值,整体作为订单的一个属性。这样对于订单详情界面来说,它只需要从 Order 实体 /表 当中就能获取全部信息,而不用再弄个 OrderGoodView 。