Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Reconsider REST: 一种构建大型Rails应用的方式

D0db70f9573a8a99a7b5378de0cf117b?s=47 Michael Chen
September 23, 2016

Reconsider REST: 一种构建大型Rails应用的方式

D0db70f9573a8a99a7b5378de0cf117b?s=128

Michael Chen

September 23, 2016
Tweet

Transcript

  1. Reconsider REST ⼀一种简洁的组织⼤大型Rails应⽤用的⽅方式 陈⾦金金洲 Founder of Jinshuju, AdMaster michael@jinshuju.net

  2. REST

  3. None
  4. Rails Programming Model GET POST PATCH DELETE show create update

    destroy SELECT INSERT UPDATE DELETE
  5. 某知名Ruby论坛 resources :topics do resources :replies end

  6. resources :topics do member do post :reply post :favorite delete

    :unfavorite post :follow delete :unfollow end resources :replies end
  7. 某知名Ruby论坛 resources :topics do member do post :reply post :favorite

    delete :unfavorite post :follow delete :unfollow post :action end collection do get :no_reply get :popular get :excellent get :favorites get :feed, defaults: { format: 'xml' } post :preview end resources :replies end
  8. 某知名Ruby论坛 class TopicsController < ApplicationController before_action :authenticate_user!, only: [:new, :edit,

    :create, :update, :destroy, :favorite, :unfavorite, :follow, :unfollow, :action, :favorites] load_and_authorize_resource only: [:new, :edit, :create, :update, :destroy, :favorite, :unfavorite, :follow, :unfollow, :action] before_action :set_topic, only: [:ban, :edit, :update, :destroy, :follow, :unfollow, :action] def index @suggest_topics = [] if params[:page].to_i <= 1 @suggest_topics = Topic.without_hide_nodes.suggest.fields_for_list.limit(3) end @topics = Topic.last_actived.without_suggest @topics = if current_user @topics.without_nodes(current_user.blocked_node_ids) .without_users(current_user.blocked_user_ids) else @topics.without_hide_nodes end
  9. 问题 难于理理解,难于上⼿手 难于测试 复杂产⽣生复杂,产⽣生更更多随意的routing 容易易产⽣生Hacky的代码 容易易产⽣生技术债务

  10. None
  11. None
  12. class InboxesController < ApplicationControlle def index end def pendings end

    end
  13. class Inboxes def index end def pendings end end class

    InboxesController < ApplicationControlle def index end end class Inboxes::PendingsController < Applicatio def index end end
  14. 这么简单? 2007 - 2016 It took DHH 10 years.

  15. None
  16. None
  17. Resource. CRUD. Let’s make ruby-china code better.

  18. 某知名Ruby论坛 resources :topics do member do post :reply post :favorite

    delete :unfavorite post :follow delete :unfollow post :action end collection do get :no_reply get :popular get :excellent get :favorites get :feed, defaults: { format: 'xml' } post :preview end resources :replies
  19. r resources :topics do member do post :reply post :favorite

    delete :unfavorite post :follow delete :unfollow post :action end collection do get :no_reply get :popular get :excellent get :favorites get :feed, defaults: { format: 'xml' } post :preview end resources :replies end resources :favorites resources :followers
  20. r resources :topics do member do post :reply post :favorite

    delete :unfavorite post :follow delete :unfollow post :action end collection do get :no_reply get :popular get :excellent get :favorites get :feed, defaults: { format: 'xml' } post :preview end resources :replies end resources :popular_topics resources :excellent_topics resources :favorite_topics
  21. 某知名Ruby论坛 resources :topics do resources :replies resources :favorites, only: [:create,

    :destroy] resources :followers, only: [:create, :destroy] end resources :no_reply_topics, only: :index resources :popular_topics, only: :index resources :favorite_topics, only: :index
  22. 好处 更更容易易理理解 更更容易易测试 ⿎鼓励以资源的⽅方式进⾏行行思考 不不太可能⿎鼓励hack代码 前端开发更更容易易

  23. 2012年年

  24. 2012年年 「在保持功能正常增长的前提下,项目 功能代码不超过2000⾏。」 ——陈⾦洲,2012

  25. None
  26. resources :forms do member do get :data_collection_status, :print, :reports, :field_attribut

    get 'qrcode/:size', to: 'forms#qrcode', as: 'qrcode' post :clear_data, :copy end collection do get :participated, :all_names, :associable_data post :preview end resource :theme, controller: 'themes', only: [:update] resources :rules, controller: 'field_rules', only: [:index] do collection do patch :save_field_rules, :save_redirect_rules end end patch :toggle, to: 'form_settings#toggle' resource :setting, controller: 'form_settings', only: [:show, :u
  27. 规模之痛 参考《架构腐化之谜》

  28. 重构约束 • routes中没有动词 • 最多只使⽤用7个⽅方法:
 new, create, edit, update, destroy,

    index, show • 让 HTTP Verb + Resource 表达业务 语意
  29. None
  30. resources :forms, except: :index do scope module: 'form' do resources

    :roles, except: :index resources :cooperators resources :recommended_cooperators, only: [:index] resource :wording, only: [:show, :update] resource :after_submission, only: [:show, :update] resources :webhooks, only: [:index, :update] resources :trackings, only: [:index, :update] resource :publish, only: :show resources :field_rules, :redirect_rules, only: [:index, :crea resource :payment_setting, only: [:show, :update] resources :payment_rules, only: :create resources :open_results, only: [:index, :update] resources :open_searches, only: :update resource :notification_setting, only: :show resource :symbol, only: [:create, :update] resource :theme, only: :update resources :reports, only: :index
  31. 能够适应所有场景吗?

  32. Case #1: 图⽚片/打印/RSS Feed def show respond_to do |format| format.html.none

    { render_overview } format.html.print { render_print } format.png { render_qrcode } end end 只是呈现(Representation)问题
  33. Case #2: 批量量编辑

  34. Case #2: 批量量编辑 resources :entries do collection do post :bulk_delete

    patch :bulk_update end end resources :batch_updates, only: [:new, :create] resources :batch_deletions, only: [:create] 将「批量量」看做资源
  35. Case #3: 审批 POST /membership_requests 201 Created Location /membership_requests/123 GET

    /membership_requests/123 { status: 'pending' } GET /membership_requests/123 Link: </memberships/100>; rel="membership" { status: 'approved' } 在Header中包含Link信息
  36. Case #4: lock/unlock/pin/unpin 使⽤用Resource resource :lock, only: [:create, :destroy] class

    LocksController < ApplicationController def create @form.lock! end def destroy @form.unlock! end end
  37. Case #4: lock/unlock/pin/unpin ⽣生成⼤大量量 Controller 在某些情况并⽆无意义 resources :topics do resources

    :favorites resources :bans resources :excellences resources :closures end
  38. Case #4: lock/unlock/pin/unpin 使⽤用PATCH resources :forms resource :setting, only: [:update]

    end PATCH /forms/abcdef/setting { status: 'lock' } class SettingsController < ApplicationController def update @form.settings.update_attributes form_settings_params end end
  39. Case #5: 移动 对「移动」单独建模,但不不必存储 resources :moves class Move include ActiveModel::Validations

    attr_accessor :card_id, :from, :to validates :from, :to, presence: true end class MovesController < ApplicationController def create move = Move.new move_params if move.valid? card = Card.find(move.card_id) card.update_attribute column: move.to end
  40. 「简单多了」 ——冯智超, chaojiwudi.com

  41. REST 定义资源,⽽而不不是动作 正确使⽤用标准HTTP Verb 资源多重呈现(Representation)

  42. ⼤大多数Rails Developer 的软件架构能⼒力力都很糟糕 我不不是特指某⼀一两个⼈人

  43. #1: 微信公众号开放平台 # 标签管理理 POST https://api.weixin.qq.com/cgi-bin/tags/create GET https://api.weixin.qq.com/cgi-bin/tags/get POST https://api.weixin.qq.com/cgi-bin/tags/update

    POST https://api.weixin.qq.com/cgi-bin/tags/delete # 获取标签下的粉丝列列表 POST https://api.weixin.qq.com/cgi-bin/user/tag/get # 为⽤用户打标签 POST https://api.weixin.qq.com/cgi-bin/tags/members/batchtagging WTF????
  44. #1: 微信公众号开放平台 resources :tags do resources :users end resources :taggings

    应该的样⼦子 GET /tags POST /tags GET /tags/1234 DELETE /tags/1234 GET /tags/1234/users POST /taggings { users: [1,2,3,4], tag_id: 1234 }
  45. #2: 钉钉开放平台 # 部⻔门管理理 GET https://oapi.dingtalk.com/department/list GET https://oapi.dingtalk.com/department/get?id=2 POST https://oapi.dingtalk.com/department/create

    POST https://oapi.dingtalk.com/department/update GET https://oapi.dingtalk.com/department/delete # WHAAAAAAT???
  46. #2: 钉钉开放平台 # 部⻔门管理理 GET https://oapi.dingtalk.com/department/list GET https://oapi.dingtalk.com/department/get?id=2 POST https://oapi.dingtalk.com/department/create

    POST https://oapi.dingtalk.com/department/update GET https://oapi.dingtalk.com/department/delete # WHAAAAAAT??? WTF????
  47. 架构的意义在于约束 ⽽而不不在于灵活

  48. None
  49. None
  50. Rails Programming Model GET POST PATCH DELETE show create update

    destroy SELECT INSERT UPDATE DELETE
  51. Thinking about REST GET POST PATCH DELETE show create update

    destroy SELECT INSERT UPDATE DELETE
  52. 总结 思考资源的含义,⽽而不不是动作 思考资源的业务意义,⽽而不不是数据表 routes.rb 中不不出现动词 使⽤用标准HTTP Verb + Resource 组装业务逻辑

    每个 Controller 不不超过7个标准⽅方法
  53. 谢谢 陈⾦金金洲 jinshuju.com 欢迎加⼊入⾦金金数据