How I architected my big Rails app for success! (ConFoo 2014)

How I architected my big Rails app for success! (ConFoo 2014)

6d48d3849102b57bbc1462c0da0b3866?s=128

Benjamin Smith

February 28, 2014
Tweet

Transcript

  1. How I architected my big Rails app for success! Friday,

    February 28, 14
  2. How I architected my big Rails app for success! Benjamin

    Smith Friday, February 28, 14
  3. How I architected my big Rails app for success! Benjamin

    Smith Friday, February 28, 14
  4. How I architected my big Rails app for success! Benjamin

    Smith Friday, February 28, 14
  5. How I architected my big Rails app for success! Benjamin

    Smith @benjamin_smith Friday, February 28, 14
  6. MVC @benjamin_smith Friday, February 28, 14

  7. once upon a time @benjamin_smith Friday, February 28, 14

  8. success Friday, February 28, 14

  9. long project Friday, February 28, 14

  10. big dev team Friday, February 28, 14

  11. lots of codez Friday, February 28, 14

  12. scalable Friday, February 28, 14

  13. big bang release Friday, February 28, 14

  14. success • long project • big dev team • lots

    of code • scalable • big 1st release @benjamin_smith Friday, February 28, 14
  15. a grain of salt @benjamin_smith Friday, February 28, 14

  16. success • long project • big dev team • lots

    of code • scalable • big 1st release @benjamin_smith Friday, February 28, 14
  17. engines Friday, February 28, 14

  18. engines-ish Friday, February 28, 14

  19. Friday, February 28, 14

  20. @benjamin_smith Friday, February 28, 14

  21. @benjamin_smith Friday, February 28, 14

  22. a little background @benjamin_smith Friday, February 28, 14

  23. the product • Multimedia content publishing • A social network

    • iOS app for consumers (using JSON API) • CMS (web interface) for admins @benjamin_smith Friday, February 28, 14
  24. started simple @benjamin_smith Friday, February 28, 14

  25. Friday, February 28, 14

  26. Friday, February 28, 14

  27. Friday, February 28, 14

  28. Friday, February 28, 14

  29. Friday, February 28, 14

  30. Friday, February 28, 14

  31. Friday, February 28, 14

  32. Friday, February 28, 14

  33. Friday, February 28, 14

  34. not too bad Friday, February 28, 14

  35. Friday, February 28, 14

  36. TRIED TO DO ^ Friday, February 28, 14

  37. Friday, February 28, 14

  38. Friday, February 28, 14

  39. Friday, February 28, 14

  40. Friday, February 28, 14

  41. Friday, February 28, 14

  42. Friday, February 28, 14

  43. Friday, February 28, 14

  44. Friday, February 28, 14

  45. but why stop there? @benjamin_smith Friday, February 28, 14

  46. Friday, February 28, 14

  47. Friday, February 28, 14

  48. Friday, February 28, 14

  49. Friday, February 28, 14

  50. Friday, February 28, 14

  51. Friday, February 28, 14

  52. Friday, February 28, 14

  53. Friday, February 28, 14

  54. Dependencies Friday, February 28, 14

  55. Web Engines Dependencies Friday, February 28, 14

  56. Web Engines Domain Engines Dependencies Friday, February 28, 14

  57. Web Engines Domain Engines Dependencies Friday, February 28, 14

  58. Dependencies Friday, February 28, 14

  59. Friday, February 28, 14

  60. @benjamin_smith Friday, February 28, 14

  61. how to get started Friday, February 28, 14

  62. rails plugin new engine_name --mountable @benjamin_smith Friday, February 28, 14

  63. gem "engine_name", path: "path/to/engine_name" @benjamin_smith Friday, February 28, 14

  64. mount EngineName::Engine => "/engine_name", :as => "engine_name" @benjamin_smith Friday, February

    28, 14
  65. bundle gem gem_name @benjamin_smith Friday, February 28, 14

  66. gem "gem_name", path: "path/to/gem_name" @benjamin_smith Friday, February 28, 14

  67. rails plugin new engine_name --mountable @benjamin_smith Friday, February 28, 14

  68. rails plugin new engine_template --mountable @benjamin_smith Friday, February 28, 14

  69. s/engine_template/new_engine_name/g s/EngineTemplate/NewEngineName/g for file in $(find . -name "*engine_template*") do

    mv $file `echo $file | sed s/engine_template/$1/` done @benjamin_smith Friday, February 28, 14
  70. s/gem_template/new_gem_name/g s/GemTemplate/NewGemName/g for file in $(find . -name "*gem_template*") do

    mv $file `echo $file | sed s/gem_template/$1/` done @benjamin_smith Friday, February 28, 14
  71. got it? got it? Friday, February 28, 14

  72. engines the right way Friday, February 28, 14

  73. self contained engines • all tables namespaced • migrations •

    tests @benjamin_smith Friday, February 28, 14
  74. table namespacing @benjamin_smith Friday, February 28, 14

  75. Friday, February 28, 14

  76. Friday, February 28, 14

  77. Friday, February 28, 14

  78. Friday, February 28, 14

  79. Friday, February 28, 14

  80. migrations inside engines @benjamin_smith Friday, February 28, 14

  81. rake your_engine_name:install:migrations @benjamin_smith Friday, February 28, 14

  82. module EngineWithMigrations class Engine < ::Rails::Engine isolate_namespace EngineWithMigrations initializer :append_migrations

    do |app| unless app.root.to_s.match root.to_s app.config.paths["db/migrate"] += config.paths["db/migrate"].expanded end end end end http://pivotallabs.com/leave-your-migrations-in-your-rails- engines/ Friday, February 28, 14
  83. one table per migration http://pivotallabs.com/moving-db-tables-between-rails- engines/ Friday, February 28, 14

  84. testing the engines in isolation @benjamin_smith Friday, February 28, 14

  85. Friday, February 28, 14

  86. engines the right way Friday, February 28, 14

  87. self contained engines • all tables namespaced • migrations •

    tests @benjamin_smith Friday, February 28, 14
  88. no circular dependencies @benjamin_smith Friday, February 28, 14

  89. Friday, February 28, 14

  90. Friday, February 28, 14

  91. Friday, February 28, 14

  92. class User < ActiveRecord::Base has_many :posts end class Post <

    ActiveRecord::Base belongs_to :user end Friday, February 28, 14
  93. Friday, February 28, 14

  94. domain api Friday, February 28, 14

  95. domain api • simple classes to wrap ActiveRecord calls •

    take as input: params or ids • output PORO @benjamin_smith Friday, February 28, 14
  96. class UserManager def find_by_id(id) User.new(UserRecord.find(id)) end private class UserRecord <

    ActiveRecord::Base validates :email, presence: true end end Friday, February 28, 14
  97. class UserManager def find_by_id(id) User.new(UserRecord.find(id)) end private class UserRecord <

    ActiveRecord::Base validates :email, presence: true end end Friday, February 28, 14
  98. class UserManager def find_by_id(id) User.new(UserRecord.find(id)) end private class UserRecord <

    ActiveRecord::Base validates :email, presence: true end end Friday, February 28, 14
  99. class UserManager def find_by_id(id) User.new(UserRecord.find(id)) end private class UserRecord <

    ActiveRecord::Base validates :email, presence: true end end class User def initialize(user_record) self.email = user_record.email end end Friday, February 28, 14
  100. that’s a lie @benjamin_smith Friday, February 28, 14

  101. it’s called the Data Mapper pattern @benjamin_smith Friday, February 28,

    14
  102. Friday, February 28, 14

  103. @user.posts @post.user Friday, February 28, 14

  104. class PostManager def find_all_by_user_id(user_id) PostRecord.where(user_id: user_id) # ... convert to

    PORO ... end end Friday, February 28, 14
  105. class PostManager def find_all_by_user_id(user_id) PostRecord.where(user_id: user_id) # ... convert to

    PORO ... end end post_manager = PostManager.new @posts = post_manager.find_all_by_user_id(@user.id) Friday, February 28, 14
  106. Friday, February 28, 14

  107. Friday, February 28, 14

  108. class UserManager def find_by_id(id) User.new(UserRecord.find(id)) end end Friday, February 28,

    14
  109. class UserManager def find_by_id(id) User.new(UserRecord.find(id)) end end user_manager = UserManager.new

    author = user_manager.find_by_id(@post.user_id) Friday, February 28, 14
  110. Friday, February 28, 14

  111. Friday, February 28, 14

  112. engines the right way Friday, February 28, 14

  113. no circular dependencies @benjamin_smith Friday, February 28, 14

  114. started simple Friday, February 28, 14

  115. Friday, February 28, 14

  116. Friday, February 28, 14

  117. Friday, February 28, 14

  118. Friday, February 28, 14

  119. I live my life one green build at a time

    Friday, February 28, 14
  120. Friday, February 28, 14

  121. Friday, February 28, 14

  122. Friday, February 28, 14

  123. git whatchanged origin/master.. @benjamin_smith Friday, February 28, 14

  124. Friday, February 28, 14

  125. how did it work out? Friday, February 28, 14

  126. Friday, February 28, 14

  127. engines are a pain... • LOW velocity for first 4-6

    weeks • client worries • opposite of Rails • one pair to start is a MUST • some technical hurtles • must be diligent about refactoring • monkey patching Rails @benjamin_smith Friday, February 28, 14
  128. Friday, February 28, 14

  129. ... but they get better • no slow down in

    development time • they age well • easier parallel development • potential for scaling • smart and fast build scripts @benjamin_smith Friday, February 28, 14
  130. thumbs up @benjamin_smith Friday, February 28, 14

  131. Friday, February 28, 14

  132. class UserManager def find_by_id(id) User.new(UserRecord.find(id)) end private class UserRecord <

    ActiveRecord::Base validates :email, presence: true end end Friday, February 28, 14
  133. thanks Friday, February 28, 14

  134. more infoz: @benjamin_smith http://pivotallabs.com/tag/rails-application-suites/ https://speakerdeck.com/benjaminleesmith/ Friday, February 28, 14