controller 和 service 怎么区分呢?
发布于 5 年前 作者 yangcecode 12243 次浏览 来自 问答

controller 里面写逻辑 service 里面操作数据库

19 回复

Controller控制路由,去调用对应的service来执行业务逻辑

我是Controller只管输入和输出。逻辑在service里写

Controller = 逻辑, Service = 复用

@xjh22222228 我认为 Controller = 控制, Service = 逻辑

少就是多,分层是有必要,但是这种模糊的分层非常没有必要,以我的经验来看,相当多的数据库操作都是每个接口特别定制的,几乎无法复用的,那为什么不和router写在一起呢? 我使用java/php/node/golang开发过web服务程序,现在主要是做node然后偶尔玩一下golang,各种框架都有自己的一套规范和约定,每个公司使用的框架都不一样,这带来了严重的学习成本和使用限制,当你换工作后你马上就会发现上一个项目学到的东西在这个项目上完全用不上,求不更学不动啊,总结发现最容易维护的恰恰是那些只有最少约定的代码 一个典型的例子就是路由的挂载方式,有的人喜欢把路径拆分,比如/user/reg、/user/login,因为/user可以抽出来,像这样app.use(’/user’ usersRouter);,这带来了一个严重的问题,当你要定位一个api接口时,你不得不从头去看一遍代码,但是如果不做拆分,直接就可以通过全局搜索快速定位到代码 另一个典型的例子是某项目是由之前写java的人转过来写的node,然后照搬java那一套,原本一个接口十几二十行代码就可以完成的功能非要拆成几个文件,甚至各文件之间的引入是运行时动态读文件夹下的文件自动引入的,完全无法用IDE静态跟踪代码 或许真如quickjs作者选择c语言一样,返璞归真,简单的就是最好的,也许你觉得这样写很不时尚很low,但你放下这段代码一年后再回来,你会发现这段很low的代码居然如此清晰易懂

@zengming00 基本赞同,分层是必要的,但不能为了分层而分层,考虑项目但可维护性,至于做到一个什么程度,代码是否解偶,这个要具体去定

service 是负责和db 交互的 你这样理解就ok了

具体情况具体分析:

  • Controller 一般的职责是:校验用户输入(query,body) + 处理业务逻辑 + 渲染模板并响应
  • 其中,处理业务逻辑 这一步,一些共用的逻辑,是可以抽象为 Service 的。
  • 但不是一定要抽取,如果只需要直接调用 BaaS 服务,如调用 oss 之类的,那完全可以直接在 Controller 里面做完,因为 BaaS 就是一个简单的远程 Service 了。
  • 抽离 Service 的一个好处是,方便单测,因为 Controller 的测试是跟 HTTP 绑定的,你需要用 supertest 之类的测试。
  • 如果你的一段业务逻辑是多发的,可以是 HTTP 触发,也可以是定时任务触发的,这时候你放在 Controller 里面自然不好。

代码也不是一成不变的,只要把单测都写了,跟着业务变化,按需重构就好了。

我是一般在 Controller 层控制一些规则,比如 url,method,输入输出参数校验规则,接口权限校验规则,类似如下:

const adminDeleteType = {          //输入输出校验,参数不合法,直接非200返回,告知接口缺少什么参数,或者某个位置的参数类型不合法等
    description: '管理员删除表单',  //接口描述
    input: c.Obj({                //输入参数校验函数
        surveyId: t.Num
    }),
    output: t.Str                 //输出参数校验函数
};

const adminDeletePerm = {         //权限校验,表示对某种资源有某种权限(预览,编辑,删除,管理员等权限),没有权限直接非200返回,告知权限不足
    resourceName: 'DOMAIN',
    resourceId: 'surveyId',
    permType: 'SURVEY_ADMIN'
};

const deleteByAdmin = api.delete('/survey/deleteByAdmin', adminDeleteType, adminDeletePerm, function* (){
    return yield surveyService.deleteByAdmin(this.input);  //输入绑定在 this.input 对象上,直接调用 service 层函数
});

Controller 有点承上启下的作用,只定义规则,具体的规则处理在 router 中间件处理,具体的业务逻辑处理在 service 层处理

有些情况下 service 层中的函数会出现相互调用,但是依赖关系是单向的,一般不建议两个 service 循环依赖,这个时候可以考虑给 service 层抽象一些公共的 util 文件出来。

我们纠结于这些大多是受到了设计模式的影响,分层的目的每个人的理解也不尽相同,比如可以做易变层->稳定层,逻辑层->复用层。如果你的代码本身就很简单,分那么多层又有何意义?

习惯了Service写逻辑,以前写Java的时候Service层才有事务管控

感觉就是谁都有谁的想法和实践,还是得结合现实情况啊。

这个我是严格准守的,可以用现在比较流行的管道思想来解释 controller 负责管道的调用,不负责具体logic service 负责比较完整的原子性事务 比如一个注册功能 1.发送邮件 2.存储数据 这两个步骤分别可以写入service,然后controller来调用。 如果形成良好的习惯,到项目后期会有大量可复用的service

之前写过一点 rails,同事让我不要把太多逻辑放在 controller,要写在 model 里。 我想,传统 mvc 的话,应该就不需要 service 了吧

@TimLiu1 基本赞同,我的controller是带res和req的,不过有时候需要多个service的组合的太多了比如10个service的流程控制,但是它又会被controller调用也会被消息队列比如,或者其他协议调用,假设需要这样一个复用的时候一般怎么放呀。我一直没好的方案所以一直会选择重复调用这一流程,毕竟这种情况不多小项目,不过不知道如果多了怎么复用某一些流程。(新开一个层?controller又可以调用新的层也可以调用原来的service?,新的层可以调用service)

@koroshi 我理解你想表达的点是:多个controller其实调用的是固定顺序的几个service,然后可不可以封装的问题,对于这个问题,其实我们需要找到一个平衡点,平衡点该怎么找呢,其实你确实可以尝试在加一层,把你总结的那些常用的固定组合的service在封装一层,如何重用性确实非常不错,其实可以抽象出一个基础服务来,这样其实也就是在向架构慢慢转变的一个过程

在我们的实践中,会把跟后端服务相关的那块,抽出一个概念,可以叫 proxy 或 rpc 。 不过我们抽出的主要原因是,这部分代码是自动生成的,直接分析后端发布的 jar 包,然后生成对应的 Proxy 类。

@jamieYou 经历类似,观点一样~

回到顶部