前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >rails, django, phoenix,你们错了

rails, django, phoenix,你们错了

作者头像
tyrchen
发布2018-03-29 10:00:42
1.8K0
发布2018-03-29 10:00:42
举报
文章被收录于专栏:程序人生程序人生

写这个题目估计会招人骂。

这三个著名的 MVC(或者 MTV) framework,分别对应 Ruby,Python,Elixir 三种语言。说他们是这几门语言的顶梁柱毫不为过。很多人都是慕着 framework 的名而来,进而学了语言。典型的就是曾经大红大紫(现在也算是一线明星)的 rails:很多 rails 工程师最初只知 rails,写了 rails 后发现语言的短板才反过来学的 Ruby。Phoenix 和 Elixir 大抵也是如此。

在 django / phoenix 上能看得出 rails 的很多影子。rails 在 flickr / delicious 时代是工程师追捧的明星框架。其后有了很多其他语言的跟风者或者学习者,django 不算是第一个, phoenix 也不是最后一个。通过这些框架,工程师可以快速地创建一个 web 项目的脚手架,和数据库(一般是 RDBMS)绑定,生成 model,controller 和 view,不消数日,一个可以运行部署的「网站」就攒出来了。

开发者的效率高么?很高。代码的效率高么?rails / django 虽让人诟病,但 phoenix 很高,在 benchmark 中狂胜各大 framework。

架构优秀么?似乎也很优秀 —— 如果让你我从头写一套 web framework,决计赶不上它们的水平。

那它们错在哪里?

它们错在给 web app 开发者带来「人人都能写 web app」的希望的同时,又把诸多程序员的思维禁锢在那一方小小的 MVC 中。

假设我们要做一个 MOOC 软件。用户可以浏览课程,可以注册课程,收藏课程,在上课的过程中可以为课程评分,记笔记,并和别人互动,等等。

我们看通常情况下一个 rails 程序员如何开始构建其后端:

  • 设计数据库结构:User / Content / Bookmark / Review / ...
  • rails new mooc
  • rails generate model
  • 把数据库设计映射到 rails model 中
  • rails generate controller
  • 撰写各个页面的 controller 和 view
  • (如果有时间)撰写 test

顺着 framework 的思路,我们不知不觉地做了一些假设:

  • 所有的状态都是存储在一个或者若干个 database 中的
  • 如果某个 database 没有 framework 的 adapter,那么我们就无法使用
  • 数据是强耦合的,比如 User 和 Content 间有一张 enroll 的表作为用户注册哪个课程的凭证
  • 一个 controller 可以跨越多个 model 获取数据,并提供给某个 view 把数据展现出来

有了这些假设,我们能够很快地搭建出应用程序,却付出了高耦合度的代价。

有同学疑惑了,MVC 设计模式的初衷不就是解耦么?为什么反倒耦合度变高了呢?经典的 MVC 分层设计是一种纵向的解耦,数据有序流动,各层只管自己的工作,「上帝的归上帝,凯撒的归凯撒」,不必关心其他层次如何实现。然而它并不能避免横向的耦合,比如 model 和 model 的耦合,controller 和多个 model 的耦合。而 web framework 却有意无意地在倡导这种耦合。更令人发指的是,它还将这种耦合做进了数据层面,使得日后无论是从代码层面解耦,还是数据层面解耦,都困难重重。

在 rails 出现以前,我们知道写代码还有一个 business logic layer —— 业务层。在 rails 出现之后,在大家的实践当中,业务层被莫名并入 model 层,有些功能还去了 controller,就此消失。然而,业务层被这样揉进了一个 web framework 中,是不是哪里不太对劲?

rails 们代表的 web 层并不是业务的全部。如果哪天我们要向第三方提供 API 呢?如果 web 的逻辑被大刀阔斧地改变怎么办?如果突然哪天公司被收购,用户账号整合到对方系统里,自己并不保留一个所谓的用户表怎么办?

回到我们的 MOOC 软件的例子里。课程的管理,排期,注册等等,都是业务层的事情。一个用户注册一门课程,在业务层,应该表述成为:{:enroll, uid, cid} -> true/false,而非 controller 和 model 里那些繁杂的逻辑。而展示一个用户订阅的所有课程,应该表述为:{:show, uid} -> [a list of courses]。

所有这些,和 model 无关。User model 甚至不该看见 Content model,也看不见作为连接表的 enroll 表。

这是横向的解耦。大家都是一个个黑盒的服务,user service 负责用户的个人信息的维护和展示,auth service 负责验证身份,content service 负责管理课程内容,content enroll service 处理 enroll 相关的事宜,等等。如下图:

我们甚至还可以将这些服务按照属性分成不同的部分,有些是核心服务,有些是社交服务,有些是交流服务。这些服务都有各自明确的接口,比如 auth 服务提供:

  • 用户名密码验证:{user, pass} -> {:ok, access_token, refresh_token} or :error
  • token 验证:access_token -> {:ok, token_info} or :error
  • token 刷新:refresh_token -> {:ok, access_token} or :error
  • 修改密码:{refresh_token, old_pass, new_pass} -> {:ok, new_refresh_token}

auth service 存储的数据只是用户/密码相关的信息,这信息只有 auth 服务自己知道,连 user service 都没有访问的权限。

起初,这种解耦会带来很多工作量,但随着系统的发展,你会发现,这样设计会为系统的扩展和可重用带来很多的好处。添加新的服务并不会影响已有的服务,我们甚至可以撰写一个已有服务的全新升级替代版,把部分流量导入新的服务,测试良好后把旧服务直接删掉。

这样做的另一个好处是重归以业务为中心的正道。说句不太好听的话,rails 等 framework 很容易引导人们走向一个 web 前端为中心的歧路。这里所说的「前端」,是指后端的前端。我们应该根据需求,先把业务模型构建出来,各个服务构建妥当后,再使用 rails 等打造前端。我们可能需要一个面向用户的前端,可能还要面向管理员的前端,每个独立的服务可能也需要它们各自的管理前端,我们还要有统计分析的前端,用户行为分析的前端等等。这些所有的前端基本都没有所谓的 model,因为数据的存储在各个服务中解决了。

如此这般,我们打破了上述的假设,数据变得弱耦合,每个服务有各自独立的数据,它们只是在需要的时候被组装起来。

至于这样一个个服务嘛,你管它叫 micro service 也好,叫 application 也好,只要它们足够独立,能够随需而动就好。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2016-11-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序人生 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档