controller/service 的负责界限问题

2021-12-06 13:53:19 +08:00
 yuhangch

比如这两种怎样更好

public ActionResult GetSomething()
{
	return _service.GetSometing(); //<- 返回包装好的结果
}



public ActionResult Getsomething()
{
	try
    {
    	return Ok(_service.Getsomething());
    }
    catch (ExceptionA ea)
    {
    	return BadRequset();
    }
    catch (ExceptionB eb)
    {
    	return NotFound();
    }
}

同理还有参数校验要放在哪里头整。。

3271 次点击
所在节点    程序员
20 条回复
warcraft1236
2021-12-06 13:55:57 +08:00
validator 可以做校验的事情
justNoBody
2021-12-06 13:56:20 +08:00
选第二种,参数校验分情况。service 里面也需要有一定业务层上的校验,但基本的前端参数校验是建议放 controller 的。
X0ray
2021-12-06 14:01:33 +08:00
Exception 都在 Global Exception Handler 里面处理
所以,我更推荐不带异常处理的 return Ok(_service.Getsomething())
VeryZero
2021-12-06 14:17:58 +08:00
个人不喜欢在 service 里包装返回值,不利于复用。典型情况就是分页查询,有人喜欢 service 返回的是带分页信息的包装类,这样一旦有不分页的查询请求,就得再写一遍 service 。
justNoBody
2021-12-06 14:35:58 +08:00
@VeryZero 确实是,分页这种可以用工具类抽象成模版方法,或者用代理模式来解决,不要直接改 service
johnsona
2021-12-06 15:00:13 +08:00
ctroller 一把锁
wfd0807
2021-12-06 15:32:42 +08:00
看见过好多人纠结这个问题,尤其是 java spring 技术栈的
有些说逻辑复用、抽象、和展示层节藕,都对,但不是 service 层独有的特性
service 层的本质是“事务脚本”(这是 spring 官方某个大佬的观点,忘记出处了)
明白 service 是事务脚本以后,责任边界就很清晰了
wolfie
2021-12-06 15:34:22 +08:00
校验 Validator
业务异常,Service 直接抛出来,ControllerAdvise 或者自定义切面。
Inside
2021-12-06 15:38:54 +08:00
以下是我遵循的一些规范,供参考。
----

# 数据库访问
⼒求在 controller 层向 datasource 层发起访问,不在 service 层发起。

# WHY ?!
在⼊⼝处就知道当前请求到底访问了哪些数据,无需⼀层层抽象来回跟踪,有助于减轻心智负担、事务控制。
有助于 service 层保持无状态,service 层需要什么数据,在方法参数中声明完整。
把 controller 视为组织者,service 视为参与者。
数据的获取、写⼊,由组织者负责。
数据的处理及运算,由参与者负责。
Edsie
2021-12-06 15:44:04 +08:00
Controller 层:数据清洗、数据组装
Service 层:具体的业务逻辑
你的 Controller 层太薄了,Service 层异常直接抛出就行,在 Controller 层做 ExceptionHandler 捕获。
参数校验可以看下 Java 的 Bean Validation JSR303 规范,但是大部分人都是在 Service 层做参数校验...
《凤凰架构》有一篇文章说的不错,(可以提供一些思路,条条框框太多也不是好事) http://icyfenix.cn/architect-perspective/general-architecture/system-security/verification.html
Jooooooooo
2021-12-06 15:47:50 +08:00
后一种好

如果哪天要复用 service 后一种会更方便点.
aababc
2021-12-06 15:48:18 +08:00
@Inside 我们最开始想希望遵循这个规则,但是数据的写入很难在 controller 中全部处理的,我们有一个下单的业务,数据的提供 用户,需要购买的产品 这些都是在 controller 中查询而后传入到 service 中,但是下单的具体的流程很难抽离到 controller 中。这样带来的问题就是,事物的嵌套不过好在很多框架都有事务的传播机制。
Leviathann
2021-12-06 16:08:13 +08:00
@Edsie 感觉这个很难分清,因为很多 crud 的业务逻辑就是数据组装。。
Edsie
2021-12-06 16:19:45 +08:00
@Leviathann 是的,所以也不要被条条框框限制,尽量遵循原则
Saxton
2021-12-06 16:26:15 +08:00
validator 不香吗
wx497657341
2021-12-06 16:26:35 +08:00
route
middleware
controller
request validator
service
logic
entity
transformer
thtznet
2021-12-06 16:28:12 +08:00
选下面的方式,控制器层对 DTO 校验,然后直接传入 DTO 到应用层,控制器层因为要返回 HTTP Status ,所以你需要 try catch 包一下,具体的错误在应用层里进行 throw ,当然如果应用 DDD 的话,应用层也不要写业务,应用层调用领域层,业务写在领域层。
unco020511
2021-12-06 16:29:19 +08:00
一般场景不特别处理异常,异常统一捕获就行了
yiheweigui
2021-12-06 16:41:53 +08:00
自己给自己做的项目:
无 service ,无 dao ,只有 controller 和 util
controller 的 action 访问一切,mybatic 的 mapper 直接放 controller 上或在 controller 手写 sql 。c 井就直接 dapper 放 controller 上访问任意,加入 redis 什么乱七八糟的各种库都在 util 或 controller 的注入上。
如若发现复用的获取数据或其他逻辑,则只把复用的提出来封装 service 或者 bll ,所以这一层顶多是复用的,如果不复用坚决没有这个,就是一个方法在很多地方都在用。
目的:看代码的人打开一个文件,查看一个方法,就能看出到底是在做什么,改动的时候非常方便。
如果一个方法很长,使用代码折叠 region 和详细注释。
如果抽象类确实有多个实现子类,才使用继承,而且一旦使用抽象类,必有多肽,绝对有多个不同子类逻辑会调用。
否则类就是类,基本不用 interface 。
异常统一处理。

给公司做的项目
dao ,service ,controller ,util ,bean ,各种加。
一个方法代码不超过 200 行,各种封装函数。
多使用 interface 和 impl ,管球他基本上只有一个子类。
dao ,service ,bean 什么的,每一层 interface ,impl 起码两个文件。
加的越多越复杂,越提现分层思想,越增加代码行数,文件数量也大,越在 jira ,禅道,tpad 这种东西上可以写工作量。
绝不做统一异常处理,controller 每个都 try catch ,耦合度越大越好。
如若 util 层增加一个接口方法,或者接口方法增加参数,连着 dao ,service ,controller ,bean 等到处修改。
这样在 git 提交的时候,发现改动文件多,在 jira 上工作量就写得漂亮。
关键是,整天开会,开完就这么写,速度也不快,让别人感觉自己非常忙,别人拿代码一看,确实封装方法多,绕来绕去,看一个功能起码打开五六个文件。
以前自己不喜欢这么做,但是公司的规范,企业文化把人逼成这样。

目前被他们搞疯了,已经去精神病院治疗抑郁症。
gowk
2021-12-07 11:31:56 +08:00
@yiheweigui 哈哈,C#,Java 混着写,确实容易疯

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

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

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

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

© 2021 V2EX