V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yuhangch
V2EX  ›  程序员

controller/service 的负责界限问题

  •  
  •   yuhangch ·
    yuhangch · 2021-12-06 13:53:19 +08:00 · 3532 次点击
    这是一个创建于 1083 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如这两种怎样更好

    public ActionResult GetSomething()
    {
    	return _service.GetSometing(); //<- 返回包装好的结果
    }
    
    
    
    public ActionResult Getsomething()
    {
    	try
        {
        	return Ok(_service.Getsomething());
        }
        catch (ExceptionA ea)
        {
        	return BadRequset();
        }
        catch (ExceptionB eb)
        {
        	return NotFound();
        }
    }
    
    

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

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

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

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

    如果哪天要复用 service 后一种会更方便点.
    aababc
        12
    aababc  
       2021-12-06 15:48:18 +08:00
    @Inside 我们最开始想希望遵循这个规则,但是数据的写入很难在 controller 中全部处理的,我们有一个下单的业务,数据的提供 用户,需要购买的产品 这些都是在 controller 中查询而后传入到 service 中,但是下单的具体的流程很难抽离到 controller 中。这样带来的问题就是,事物的嵌套不过好在很多框架都有事务的传播机制。
    Leviathann
        13
    Leviathann  
       2021-12-06 16:08:13 +08:00
    @Edsie 感觉这个很难分清,因为很多 crud 的业务逻辑就是数据组装。。
    Edsie
        14
    Edsie  
       2021-12-06 16:19:45 +08:00
    @Leviathann 是的,所以也不要被条条框框限制,尽量遵循原则
    Saxton
        15
    Saxton  
       2021-12-06 16:26:15 +08:00
    validator 不香吗
    wx497657341
        16
    wx497657341  
       2021-12-06 16:26:35 +08:00
    route
    middleware
    controller
    request validator
    service
    logic
    entity
    transformer
    thtznet
        17
    thtznet  
       2021-12-06 16:28:12 +08:00
    选下面的方式,控制器层对 DTO 校验,然后直接传入 DTO 到应用层,控制器层因为要返回 HTTP Status ,所以你需要 try catch 包一下,具体的错误在应用层里进行 throw ,当然如果应用 DDD 的话,应用层也不要写业务,应用层调用领域层,业务写在领域层。
    unco020511
        18
    unco020511  
       2021-12-06 16:29:19 +08:00
    一般场景不特别处理异常,异常统一捕获就行了
    yiheweigui
        19
    yiheweigui  
       2021-12-06 16:41:53 +08:00   ❤️ 6
    自己给自己做的项目:
    无 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
        20
    gowk  
       2021-12-07 11:31:56 +08:00
    @yiheweigui 哈哈,C#,Java 混着写,确实容易疯
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   978 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:54 · PVG 03:54 · LAX 11:54 · JFK 14:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.