单体架构下 Java Web 各层之间的传参问题

2023-12-14 16:44:36 +08:00
 hk94
现在的 Java 项目大多都是 controller, service, dao 三层, controller 与 service 之间我一般是单独建 2 个包 req/resp, 用来接收入参和返回值. 而 service 与 dao 之间通常是通过 do 或者说 entity 来做参数.

那么条件查询应该如何实现呢, 以前看到的都是 SearchRequest 从 controller 一路传到 dao, 再从 dao 接收 SearchResponse, 感觉这样把层与层之间的关系搞乱了, 大家有什么好的建议吗? 特别是有的时候查询结果并不是一个表, 就得新建一个对象来接收, 那么这种对象应该属于什么类型的对象, DTO or VO 或者是其他的?
2276 次点击
所在节点    Java
19 条回复
xmiraclez
2023-12-14 17:31:10 +08:00
个人看法,如果仅是查询请求,那么数据模型和展示模型就够用了,入参从 controller 一路传到 dao 也没问题,返回值由数据模型转化为展示模型,数据模型比较稳定,展示模型是定制化的。但是如果是写请求,那么应该是三层模型,数据模型、领域模型、展示模型。
darling19961030
2023-12-14 17:40:00 +08:00
我以前也陷入“规范”的怪区,比如你说的 PO 、VO 、DAO 、BO 、POJO 、Resp 、Param ,再比如 Controller 和 ServiceImpl 哪些代码能写,哪些不能写;参数校验在哪层做,Service 做不做,入参校验,返回值要不要校验;一个函数应该多少行,是写 Void 类型函数把返回值当入参传入,还是定义返回值对象类型的函数;再加上微服务 RPC ,DDD 原则,等等等等。
现在看,细究的意义都不大。
你可以了解,可以有自己的理解,但不用强求一个标准。
现实项目,假如你是一个生命力强的产品,一直迭代,那么你一直改改改自然能找到适合你的规范。
假如是一锤子买卖项目或者爆发性的产品项目的,首要也是快速落地。
计划赶不上变化。技术为业务而生,等你业务到了,自然就知道哪个省力合适了。
脱离实际需求,单纯的讨论技术规范,空中楼阁,意义不大,这跟你的项目周期性质团队成员都有关系。
Goooooos
2023-12-14 18:12:47 +08:00
一个 Map 解决所有烦劳
shockerli
2023-12-14 18:17:27 +08:00
@Goooooos 哈哈哈
awalkingman
2023-12-14 18:35:26 +08:00
@Goooooos 开发一时爽,维护火葬场
ZZ74
2023-12-14 19:20:54 +08:00
定义一种或者两种 O 就行了 再多就是自找麻烦
hk94
2023-12-14 19:47:40 +08:00
@darling19961030 事实上我个人对于阿里的那一套各种 O 也是很排斥, 层与层之间各种 convert. 我们公司现有的项目更是乱七八糟. 发这个问题的初衷是做自己项目的时候思考了一下, 主要是看看大家有什么看法, 能不能找出相对折中的方案.
hk94
2023-12-14 19:48:27 +08:00
@ZZ74 赞同, 各种 convert 以及衍生出来的对象转换的工具...
hk94
2023-12-14 19:50:11 +08:00
@Goooooos 见过服务内部全部用 Map 的, RPC 服务全部用 JSONObject 的..
oneisall8955
2023-12-14 19:56:54 +08:00
单体架构,前端入参 xxxParam ,转换到 service 就 xxxEntity ,再到数据库实体 xxxModel ,响应到前端就 xxxVO ,后缀名自己习惯就好,这 4 种大部分情况都足够了。

甚至可以把 xxxEntity 去掉,xxxParam 一路传到底
skydiver
2023-12-14 20:00:38 +08:00
@hk94 Java 当 PHP 写,哈哈哈哈哈哈
812603834
2023-12-15 09:16:27 +08:00
我自己经过几年经验的积累,我理解的是 controller 、service 、mapper 的参数性质不一样。
1.直接用一个 xxxRequest 从 controller 传到 mapper 层是没有问题,但是想要复用就不容易了,如果别人想复用你的 service ,那么就需要组装复杂参数,而且参数命名也可能意思有歧义,虽然能达到效果,但是很奇怪
2.可以开发同事统一约定自己的规范,例如 mapper 中基础的查询可以用 entity 接收,自定义的查询结果可以用 xxxDto,提供 controller 的用 xxxRequest,xxxResponse 。service 的参数简单的可以直接用 controller 的参数,如果你觉得接口有复用的价值,那么可以自定义参数上下文 xxxContext
Dongxiaohao
2023-12-15 09:51:18 +08:00
你说的这种查询,一般我都是建一个 superDTO 里面带上分页参数,模糊搜索关键词之类的,这些常用的搜索条件放在一个对象里面,controller 里面的入参有些不同,就去建 dto 继承这个 superDTO ,在 service 层 把 dto 转换成 po ( beancopy ,mapstruct ),丢到 dao 去查询,vo 我理解的 vo 是 return 出去的东西,dto 是引用的入参,当然,小项目没什么敏感信息,直接把查询到的 po return 出去也没什么大碍,就是可能前端用不上这么多字段,浪费流量,泄露表结构之类的问题吧
hk94
2023-12-15 11:01:53 +08:00
@812603834 你说到我想表达的点子上了,问题就在于他们的性质不一样. 特别是用 maven 聚合工程把层与层分开, 依赖关系就会变得奇怪.
hk94
2023-12-15 11:03:18 +08:00
@Dongxiaohao 一般来说分页查询是会这么做,会有个 PageRequest, SortRequest, 按需继承. 问题是这种 request 从 controller 一路到 mapper 会觉得有些奇怪。。
litchinn
2023-12-15 16:18:59 +08:00
我用的 mybatis-plus ,我的 query object ,会包含一个方法自动生成对应的 QueryWrapper ,在查询类传入 service 时已经变成 QueryWrapper 了,对于返回类一般来说都是和某一实体相关,额外的数据在 service 中处理并返回 VO 对象。
转换工具用的 mapstruct 。

为了防止混乱可以利用 maven 的模块,例如 controller 层依赖 service 层,service 层依赖 dao(或者叫 domain)层,
实体放在 domain 层,QO 放在 controller 层,VO 放在 service 层,这样就无法在 service 层使用 QO ,也无法在 domain 层直接使用 VO ,QO 。

如果你在很多情况下查询结果并不对应实体那么不应该用数据库驱动设计的思路,而应该考虑 DDD 。
hk94
2023-12-15 16:49:59 +08:00
@litchinn 怎么说呢,按我的理解,Mybatis Plus 也好,JPA 也好, 确实能在 service 层就可以搞定条件查询那些问题, 但是 QueryWrapper 并不能搞定所有的查询场景, 有些时候依然需要 xml 来搞定。
visper
2023-12-15 17:56:07 +08:00
List<Map> listTable(String tableName); Map saveData(String tableName, Map data) 结束
boris1993Jr
2023-12-15 21:30:17 +08:00
@Goooooos #3
@hk94 #9
拖出去枪毙十分钟然后上火刑柱

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1000410

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX