toB 应用设计系列 - 构架分层篇

toB 应用设计系列 - 导航

小结

层级结构


顶层
-web
-domainService
-query
-infrastructure
-framework

web层
-pc
--login
---LoginController
---LoginService
---dto
----LoginDTO
--module
---dictType
----DictTypeController
----DictTypeService
----dto
-----AddDictTypeDTO
-----UpdDictTypeDTO
-----converter
------AddDictTypeConverter
------UpdDictTypeConverter
----vo
--component
--dictType
---DictTypeController
---DictTypeService
---dto
----GetDictTypeDTO
----ListDictTypeDTO
---vo
----GetDictTypeVO
----ListDictTypeVO
-mobile
--(和 pc 一致)
-api

domainService 层
-dictType
--IDictTypeDMService
--cmd
---AddDictTypeCMD
---UpdDictTypeCMD
--impl
---DictTypeDMService
---DictTypePO
---DictTypeDao
---DictPO
---DictDao
---AddDictTypeCMDConverter
---UpdDictTypeCMDConverter

framework 层
-exception
-log
-shiro

infrastructure 层
-file
--excel
---ExcleHelper
---ExcelUtil
-thirdApi
--element
--alibaba
--meituan

各层调用关系

层级 下层 同级调用 说明
controller service 禁止 只调用一个service方法
service domainService / query 禁止 可调用多个 domainService / query
domainService dao 允许 可调用多个dao
dao 允许

dto 爆炸的问题

构架分层设计原则

  • 最小知识原则
  • 解耦的思想
  • 业务操作和查询分开

没有优先考虑效率, 业务为主导

参考 领域驱动设计 / CQRS

业务操作和查询分开

业务操作和查询是两条支线, 这两种模式完全不一样
业务操作可以非常复杂, 会更新数据, 但基本不需要返回(新增一般返回主键), 具有高度的通用性

查询则不更新数据, 但是企业应用的查询条件千奇百怪, 而且返回结果各种跨表, 基本没有通用性

业务操作分层

  • controller
  • service
  • domainService
  • dao

controller

接收 http 请求, 返回数据

service

业务层, 通过组装 domainService 领域服务层 以及调用基础设施层完成业务操作

service 层不涉及具体的业务操作, 具体的业务操作由 domainService 层处理

service 专一性很强, 不具备通用性

domain

聚合根的业务操作

domainService 层是最重的一层, 涉及到的核心业务逻辑都在这里, domainService 层不做具体的数据库操作, 具体数据库操作的交由 dao 层完成; domainService 层严格遵循最小知识原则, 业务过程对 service 层透明; 同样的, dao 层要严格遵循最小知识原则, 具体的数据库操作对 domainService 层透明

一个 domainService 对应一个聚合根, 而非对应一张表

eg: 数据字典表和数据字典类型表, 聚合根是数据字典类型表, 故 domainService 是
数据字典类型 domainService, 而且没有数据字典 domainService, 若外部想要操作数据字典, 只能通过 数据字典类型 domainService 来操作

domainService 通用性很强

dao

业务数据库原子操作

``

和数据库的原子操作 CRUD 区别在于, 业务需要的原子操作不一定是 CRUD, 对应的是业务的原子操作, 如新增订单, 修改订单, 审核订单, 删除订单等

eg: 日志dao 只有 insert 操作, 没有 update / delete 操作

数据库的修改订单的原子操作, 不一定会对应业务上的修改订单的操作, 而是可能会拆成若干个方法:

  1. update() : 修改普通属性, 对应的数据库操作为 修改 订单名称,订单明细的订单数量, 订单单价, 收货数量, 收货单价等普通的属性
  2. next() : 修改订单的流程状态, 进入下一个流程状态, 对应的数据库操作为 修改订单的流程状态

dao 通用性很强

// 若订单状态为创建状态, 则可修改 订单名称, 订单明细的订单数量, 订单单价

查询分层

  • controller
  • service
  • query

controller

和操作的一致

service

service 层不涉及具体的数据库操作, 具体的数据库操作由 query 层实现, service 层主要通过调用 query 层, 以及调用基础设施层, 以及对最后的结果进行组装, 返回接口需要的格式

事务放在service 层

query

查询的sql 语句

query 为定制化的查询语句, 不具备通用性

基础设施层 infrastructure

完成基础的操作(不含数据库操作以及业务操作)

  • 工具类 (发邮件 / 发短信 / 解析邮件 / 解析短信 / 加密 / …)

各层间交互

controller <-> service

controller 层接收 http 请求, 一般由框架转换成 dto 传输对象, 直接将 dto 作为 service 层的参数调用service, service 执行完以后, 返回view objert对象(VO), 由 controller 自行封装成统一的响应格式给 api

禁止在 controller 层写业务相关的代码

禁止在 controller 校验参数合法性, 传输参数的合法性应由 service 层校验

一个 controller 只调用一个 service 方法

service <-> domainService

service 接收 dto 传输对象, 对 dto 对象进行转换, 转换成 domainService 层接收的 cmd 命令对象, 调用 domainService, 成功则完成操作(可能需要返回主键), 失败则抛异常

注意:

一个service 可调用多个 domainService 方法

service 不允许同级调用: service方法 不调用 自己的 service 其他方法, 也不调用其他 service的方法, 构架设计来说, 是希望 service 专一化, 不具备通用性(通用性都放领域层了, service 提供专用的接口给 api 就可以了)

service <-> query

service 接收 dto 传输条件对象, 直接将部分或全部 dto 属性(或者对dto 进行加工)作为参数传给 query, query 返回查询结果, service 对所有的查询结果进行组装, 组装成 api 需要的格式, 返回

注意: 一个 service 可调用多个 query 方法

domainService <-> dao

domainService 接收 cmd 命令对象, 将 cmd 命令对象加工成 po对象, 调用 dao, 完成业务操作, 无返回或按需要返回主键

注意:

domainService 可调用多个 dao

domainService 可同级调用: 既可调用同一个domainService 类的方法, 也可以调用不同domainService 类的方法

dao

dao 接收 po 对象, 对 po 对象进行数据库的具体操作, 无返回或按需要返回主键

query

query 接收 dto 传输对象, 进行查询操作, 返回查询结果

toB 应用设计系列 - 构架分层篇_第1张图片

基类

为方便程序识别各种 model, 为 dto / cond / vo / po 提供抽象基类 BaseDTO / BaseCond / BaseVO / BasePO

问题

DTO

DTO 爆炸

VO也有这个问题

接口通过 DTO 传输, DTO 没有通用性, 往往一个 service 方法就需要一个 DTO, 一个 VO, 造成 DTO 数量剧增的现象, 不但增加了很多的工作量, 包括定义, 转换成 CMD, 而且命名很麻烦, 又不好管理

DTO 转换成 CMD

每一个DTO 都需要转换成 CMD, 这一工作也非常枯燥无聊

若 DTO 嵌套的层次很深, 这个工作会变得更加的麻烦

DTO 嵌套

VO 也有这个问题

若 DTO 嵌套的层次很深, 定义会很麻烦, 转换成 CMD 也很复杂

造成 DTO 的种种问题的原因, 和 JAVA 的强数据类型特性分不开, 若能避开 强数据类型, 基本能解决

故一种解决方案是 将 查询的一层剥离出去给 node.js 来承接, 业务操作依然交给 JAVA 来做, node 这一层把 controller 完全承接过来, 把查询的service 也承接过来, 操作的service 依然交给 JAVA

你可能感兴趣的:(toB,应用设计)