Fat Modelの倒し方 / how to deal with fat model

Fat Modelの倒し方 / how to deal with fat model

銀座Rails#21 ( https://ginza-rails.connpass.com/event/173610/ ) の発表資料になります。

Ruby on Rails アプリケーションにおける Fat Model の解決方法を下記の3つに分けて紹介しています。
1. Rails Way
2. Sub-Rails Way
3. Non-Rails Way

本発表はBlogにも文書の形でまとめてありますのでそちらもあわせてご参照ください。
https://blog.toshimaru.net/how-to-deal-with-fat-model/

5919537a0ecfa5d4dea704cf878ae90e?s=128

toshimaru

May 15, 2020
Tweet

Transcript

  1. Fat Modelͷ౗͠ํ 2020/5/15 ۜ࠲Rails#21 1

  2. ࣗݾ঺հ toshimaru - GitHub: toshimaru - Twitter: toshimaru_e Rails Engineer

    @ MedPeer - We're Hiring ❗ OSS Activities: - Syntax Highlighted Go cat (toshimaru/nyan) - VSCode Color Theme (toshimaru/hybrid-next-plus) 2
  3. Q.ංେԽͨ͠RailsΞϓϦέʔγϣϯ ࠷΋ਏ͍ϨΠϠʔ͸Ͳ͜ 1. Fat View 2. Fat Controller 3. Fat

    Model 3
  4. Fat Model 4

  5. Fat Rails Stage Fat Stage Railsशख़౓ Fat Layer 1 ௿

    Fat View 2 த Fat Controller 3 ߴ Fat Model1 1 Buckblog: Skinny Controller, Fat Model 5
  6. How to Deal with Fat Model 6

  7. ࿩͢͜ͱ • Fat Model ʹରॲ͢ΔͨΊͷΞϓϩʔνΛ੔ཧ • Fat Model ରॲʹ͋ͨΓͲͷΞϓϩʔνΛऔΔ΂͖͔ ࿩͞ͳ͍͜ͱ

    • ͦΕͧΕͷΞϓϩʔνͷ࣮૷ৄࡉɾ۩ମతͳ࣮૷ํ๏΍࣮૷ྫ • Fat Controller/Fat Viewͷରॲ๏ 7
  8. Fat Model ରॲͷͨΊͷ ̏ͭͷΞϓϩʔν 1. Rails Way 2. Sub-Rails Way

    3. Non-Rails Way 8
  9. RailsΞϓϦέʔγϣϯͷ Model૚͕ݶքʹ Ϳ͔ͭΔͱ͖ 9

  10. Rails Modelͷݶք఺a a ApplicationModel ͷ͋Δ෩ܠ - Speaker Deck 10

  11. Rails Modelͷݶք఺a a ApplicationModel ͷ͋Δ෩ܠ - Speaker Deck 11

  12. Rails Modelͷݶք఺b b Ruby on Railsͷਖ਼ମͱ޲͖߹͍ํ - Speaker Deck 12

  13. Rails Modelͷݶք఺ • 1ͭͷModel͕ෳ਺ͷҟͳΔϢʔεέʔεʹີ݁߹࣮ͯ͠૷͞Ε Δͱ͖ • → ͋Δ৚݅΍contextʹඥ෇͍ͨValidation/Callbackॲཧ • 1ͭͷϑΥʔϜͰෳ਺ͷαϒϦιʔε͕ߋ৽͞ΕΔͱ͖

    ʢϑΥʔϜͱModel͕1ର1Ͱඥ෇͔ͳ͍ͱ͖ʣ • → 1ͭͷModelΛى఺ͱͨ͠ෳ਺ModelΛލ͙τϥϯβΫ γϣϯॲཧ • Rails Model : DB Table = 1:1 ͷੈք؍ʹىҼ͢Δݶք 13
  14. զʑ͕໨ࢦ࢟͢c c Growing Rails Applications in Practice 14

  15. զʑ͕໨ࢦ࢟͢c c Growing Rails Applications in Practice 15

  16. ΰʔϧ ίʔυϕʔε͕େ͖͘ͳͬͯ ΋ϖΠϯ͕૿େ͠ͳ͍Rails ίʔυϕʔε 16

  17. 1st Approach Rails Way 17

  18. 18

  19. Ϩʔϧʹ৐ͬͨ։ൃख๏ 19

  20. Concerns Model/Controllerͷڞ௨ͷؔ৺ࣄʢConcernʣΛmoduleʹ੾Γग़͢ ʢ୅දྫ: DHH's Recording Class6ʣ 6 https://twitter.com/dhh/status/964244090224128001 20

  21. Concerns • ⚠ ConcernͷެࣜΨΠυ͸ͳ͍ • DHH: Put chubby models on

    a diet with concerns • Modelͷ͍࣋ͬͯΔೳྗʢability = -able suffixʣʹண໨ͯ͠੾ Γग़͍ͯ͘͠ͷ͕Rails WayͬΆ͍ 21
  22. STI Raiilsʹ͓͍ͯςʔϒϧͱModel͸1ର1Ͱ݁ͼ͕ͭ͘ɺSTIΛ࢖͑ ͹1ͭͷςʔϒϧͰෳ਺Modelඥ෇͚Δ͜ͱ͕Ͱ͖Δ5 5 PoEAA: Single Table Inheritance 22

  23. STI companies ςʔϒϧʹඥͮ͘ Firm, Client Ϟσϧͷྫ: 23

  24. Polymorphic Association 1ͭͷϙϦϞʔϑΟοΫؔ࿈෇͚ఆٛͰෳ਺ͷςʔϒϧΛैଐͤ͞ Δ͜ͱ͕Ͱ͖Δ 24

  25. Polymorphic Association ⚠ ʰSQLΞϯνύλʔϯʱ6ষ ϙϦϞʔϑΟοΫؔ࿈ 25

  26. accepts_nested_attributes_for ωετ͞ΕͨΞτϦϏϡʔτͰؔ࿈Ϧιʔεͷ࡞੒ɾߋ৽ɾ࡟আ Λߦ͏ 26

  27. accepts_nested_attributes_for • ⚠ ඇਪ঑ʁ • DHH͕ʮফ͍ͨ͠ʯͱൃݴ͍ͯ͠Δ7 7 https://github.com/rails/rails/pull/26976#discussion_r87855694 27

  28. ͦͷଞࡉ͔ΊͷςΫχοΫ • Serialize Attribute • jsonܕΧϥϜ΁ͷϝλσʔλอଘʹศར • ⚠ ʰSQLΞϯνύλʔϯʱ5ষ EAV

    • Value Object (compose_of) • ෳ਺ΧϥϜΛValueΦϒδΣΫτͱͯ͠ల։͢Δͱ͖ʹศར 28
  29. ͦͷଞࡉ͔ΊͷςΫχοΫ • Ϋϥεͱͯ͠෼཭Մೳ • Validation Class • Callback Class •

    → ෼཭͢Δ͜ͱͰෳ਺ϞσϧͰ࠶ར༻Մೳʹ 29
  30. ʮ1. Rails Wayʯ·ͱΊ • ! Rails Way͚ͩͰ͸Fat ModelΛ౗͢खஈͱͯ͠ख਺͕গͳ͘ ෺଍Γͳ͍ •

    ❌ Concerns, Validation ClassͳͲFat ModelΛDRYʹهड़͢ Δखஈʹ͸ͳΔ͕ɺߏ଄తʹμΠΤοτ͢Δखஈʹ͸ͳͬͯ ͳ͍ʢہॴతͳμΠΤοτࢭ·Γʣ • ❌ STI, PolymorphicͳͲ͸DBઃܭͱີ݁߹ͨ͠ιϦϡʔγϣ ϯͰɺ׬શͳίʔυϨϕϧͷղܾʹ͸ͳ͍ͬͯͳ͍ʢͦΕࣗ ମ͕ٕज़ෛ࠴ʹͳΓ͑Δߏ଄త໰୊ΛሃΜͰ͍Δʣ 30
  31. 2nd Approach Sub-Rails Way 31

  32. 32

  33. ϨʔϧΛิڧɾ֦ுͭͭ͠ Ϩʔϧʹ৐Δ 33

  34. ϨʔϧΛԿΛ࢖ͬͯ ิڧɾ֦ு͢Δ͔ʁ 1. gem 2. SaaS 34

  35. View Model • Modelʹ͓͚ΔViewؔ࿈ϩδοΫΛ View Model ͱͯ͠੾Γग़ ͢ • Development

    of Further PoEAA: Presentation Model • ModelΛDecoratorύλʔϯͬΆ֦͘ு͍ͯ͠ΔͷͰ Decorator ͱ΋ݺ͹ΕΔ8 • Fat View ͷରॲͱͯ͠΋ػೳ͢Δ఺͕˓ 8 ࢀߟ: ʰRubyʹΑΔσβΠϯύλʔϯʱୈ11ষ ΦϒδΣΫτΛվྑ͢ΔɿDecorator 35
  36. View Model • ! gem • draper • active_decorator 36

  37. View Model ! draper ͷ৔߹ʢArticleϞσϧͷDecoratorΫϥεʣ: 37

  38. ݖݶ؅ཧ • ! ؅ཧը໘Ͱಀ͛ΒΕͳ͍࣮૷͕ݖݶ؅ཧɾೝՄ • " ResourceͷCRUDͰϢʔβʔͷΞΫηε੍ޚ͢Δͷ͕ʮRails Β͍͠ʯݖݶ؅ཧ • #

    gem • pundit • banken • cancancan 38
  39. ݖݶ؅ཧ ! pundit ͷ৔߹ʢPostϞσϧͷೝՄΫϥεʣ: 39

  40. Interactor Clean Architecture: Use Case (Interactor) 40

  41. Interactor Rails x Clean Architecture 41

  42. Interactor • ! gem • interactor-rails • (not Rails) hanami's

    Interactor 42
  43. Interactor ! interactor-rails ͷ৔߹ʢϢʔβʔΛೝূ͢ΔΫϥεʣ: 43

  44. ಛఆͷ՝୊ͷղܾ • ࿦ཧ࡟আ • ! gem: discard, paranoia, acts_as_paranoid •

    ⚠ SQLΞϯνύλʔϯ ݬͷୈ26ষʮͱΓ͋͑ͣ࡟আϑϥ άʯ • ཁૉͷιʔτɾฒͼସ͑ • ! gem: acts_as_list, ranked-model 44
  45. ಛఆͷ՝୊ͷղܾ • State Machine • ! gem: aasm, stateful_enum •

    Tagging • ! gem: acts-as-taggable-on • HashΛActiveRecordͬΆ͘ૢ࡞ • ! gem: active_hash 45
  46. ʮͦΕRailsͰͰ͖ΔΑʯ10 • ! enumerize (Emumerized Attributes) • Rails 4.1: ActiveRecord

    enum • ! switch_point (Database R/W Split) • Rails 6: Multi-DB • ! activerecord-import (Bulk Import) • Rails 6: insert_all, upsert_all 10 Ruby/Railsެࣜͷఏڙ͢Δػೳ͸gemΑΓශऑͩͬͨΓ͢ΔͷͰ͔͋͠Βͣ 46
  47. ʮͦΕRailsͰͰ͖ΔΑʯ10 • ! carrierwave, shrine (File Uploader) • Rails 5.2:

    Active Storage • ! config (YAML Config Management) • Rails Custom configuration: • Rails::Application.config_for • config.x 10 Ruby/Railsެࣜͷఏڙ͢Δػೳ͸gemΑΓශऑͩͬͨΓ͢ΔͷͰ͔͋͠Βͣ 47
  48. ʮͦΕRailsͰͰ͖ΔΑʯ10 • ! friendly_id • ActiveRecord: to_param • ID/Passwordೝূ •

    ActiveModel: has_secure_password 10 Ruby/Railsެࣜͷఏڙ͢Δػೳ͸gemΑΓශऑͩͬͨΓ͢ΔͷͰ͔͋͠Βͣ 48
  49. ʮͦΕRubyͰͰ͖ΔΑʯ10 • ! pry • Ruby 2.4: binding.irb • Ruby

    2.7: REPL Syntax Highlighting 10 Ruby/Railsެࣜͷఏڙ͢Δػೳ͸gemΑΓශऑͩͬͨΓ͢ΔͷͰ͔͋͠Βͣ 49
  50. SaaSʹ੾Γग़͢ • ! ϢʔβʔೝূϩδοΫΛ Auth0 ʹҠৡ • ೝূʹͱ΋ͳ͏MFAɺύεϫʔυϦηοτɺηΩϡϦςΟର ࡦͳͲͷ໘౗ͳ࣮૷ΛAuth0͕ݞ୅ΘΓ •

    " ͦͷଞͷࣄྫ • Τϥʔ௨஌Λ Sentry ʹҠৡ • APMΛ NewRelic / Datadog Ͱ΍Δ 50
  51. ʮ2. Sub-Rails Wayʯ·ͱΊ • ! gem Λ࢖͏͜ͱͰ Fat Model ରॲ๏ͷόϦΤʔγϣϯ͕޿

    ͕Δ • ✅ ಠ࣮ࣗ૷ͰModelΛଠΒͤͣɺ࢖͑Δgem͸ੵۃతʹར༻ ͠Α͏ • # ҰํɺgemΛ࢖Θͣͱ΋Railsඪ४ͰղܾͰ͖Δ͜ͱ΋ଟ ͍ͷͰݟۃΊ্ͨͰgemಋೖ͠Α͏ • $ બ୒ࢶ͸͞΄Ͳଟ͘ͳ͍͕ SaaS Λ࢖͏ͷ΋ Fat Model ର߅ खஈͷ̍ͭ 51
  52. 3rd Approach Non-Rails Way 52

  53. 53

  54. ಠࣗ࿏ઢ 54

  55. Form Model • Form Model = include ActiveModel ͨ͠RubyΫϥεʢ͍ΘΏ Δ

    Form Objectʣ • ϑΥʔϜ:Form Model = 1:1 • ! gem • reform • dry-rbγϦʔζ3 3 چɾvirtus 55
  56. Form Model ඥͮ͘ςʔϒϧ਺ Ϣʔεέʔε 0 ໰͍߹ΘͤϑΥʔϜͳͲςʔϒϧ Λ࡞Δ·Ͱ΋ͳ͍ϑΥʔϜͰར༻ 1 - 4

    2Ҏ্ accepts_nested_attributes_for ͷ୅ΘΓͱͯ͠ෳࡶͳϑΥʔϜͷ ૊Έཱͯ࣌ʹར༻ 4 ςʔϒϧͱϑΥʔϜ͕1ର1Ͱඥͮ͘৔߹͸Rails WayͰղܾͤ͞·͠ΐ͏ 56
  57. PORO • PORO (Plain Old Ruby Object) • PoEAA: POJO

    (Plain Old Java Object) • ActiveRecord ͷػೳʹґଘ͠ͳ͍७ਮͳRuby࣮૷ • include ActiveModel • ७ਮͳRuby࣮૷ͳͷͰ͋Δҙຯ Ruby Way ͱݴ͑Δ • Model ͷิॿྠత໾ׂ 57
  58. PORO 58

  59. Service Class • ⚠ αʔϏεͷఆٛ໰୊ → What is your "Service"?

    • PoEAA: Service Layer • DDD: Service Class • Onion Architecture: Application Service, Domain Service • Rails "Service" • ʮͲ͏͍͏จ຺ͷαʔϏε͔ʯΛ໌֬ʹ͠ͳ͍ͱService Class ͷఆٛɾ֓೦͕ϘϯϠϦ͢Δҹ৅ 59
  60. Service Class • αʔϏεΫϥεʹର͢Δݸਓతݟղ • Service Class ͷఆٛɾ࢖͍ํΛ໌֬ʹ্ͨ͠ͰνʔϜʹಋ ೖ͍ͯ͘͠ͷ͕٢ •

    ʮಛఆͷϢʔεέʔεͷղܾʯͱ͍͏ҙຯʹ͓͍ͯ͸ Interactor ͷ΄͏͕ʢগͳ͘ͱ΋Railsʹ͓͍ͯ͸ʣے͕ྑ͞ ͦ͏ • Լखʹ৽͍֓͠೦Λ࣋ͪࠐΉΑΓɺPORO ͱ͍͏֓೦Ͱࡶʹ ·ͱΊͨ΄͏͕޷Έ 60
  61. 1 Table Multiple Models • Ұͭͷςʔϒϧʹෳ਺ͷModelΛඥ෇͚Δ • Rails WayͩͱSTIͰͷΈ࣮ݱՄೳ 61

  62. 1 Table Multiple Models 62

  63. 1 Table Multiple Models • 1 Table 1 ModelͷRailsͷύϥμΠϜʢن໿ʣΛյ͢ͷ͸͍͞͞ ͔ةݥࢥ૝ͱ͍͏ҹ৅

    • ࣮ӡ༻ʹ͓͚Δ੒ޭྫ͋Ε͹ڭ͑ͯԼ͍͞ 63
  64. ʮ3. Non-Rails Wayʯ·ͱΊ • 4ͭͷ Non-Rails 1. Form Model 2.

    PORO 3. Service Class 4. 1 Table Multiple Models • ! ͏·͘ಋೖͰ͖Ε͹ Fat Model Λ౗͢ڧྗͳ෢ثͱͳΔ • " ਖ਼ղ͸ͳ͍ͱࢥ͏ͷͰνʔϜʹ͋ͬͨख๏Λબ୒͢Δͱྑ͍ 64
  65. Non-Rails Way ͓͢͢ΊΞϓϩʔν • ෳࡶͳϑΥʔϜ: Form Model • ϢʔεέʔεʹಛԽͨ͠Ϋϥε: Service

    Class Interactor • ↑Ͱ଍Γͳ͍৔߹: PORO 65
  66. શମͷ·ͱΊ Fat Model Λ౗ͨ͢Ίͷ3ͭͷΞϓϩʔν Λ঺հ͠·ͨ͠ɻ 1. Rails Way: Railsͷن໿ʹԊͬͨ։ൃ 2.

    Sub-Rails Way: Railsͷن໿ΛgemͰิڧɾ֦ு 3. Non-Rails Way: Railsͷن໿͔Β֎ΕΔಠ࣮ࣗ૷ 66
  67. ݁࿦ • ·ͣ͸ Rails Way + Sub-Rails Way ͰFat ModelΛμΠΤοτͰ

    ͖ͳ͍͔ߟ͑·͠ΐ͏ɻͦͷ্ͰඞཁʹԠͯ͡ద੾ͳ Non- Rails Way ΛऔΓೖΕ͍͖ͯ·͠ΐ͏ • Non-Rails Way ͸νʔϜຖʹ࠷దղ͕͋Δͱࢥ͏ͷͰɺνʔϜ Ͱ߹ҙͰ͖Δಠࣗ࿏ઢΛબ୒ɾಋೖ͢Ε͹Α͍ͷͰ͸ͳ͍Ͱ ͠ΐ͏͔ 67
  68. ! Thank You 68

  69. ࢀߟจݙ • Patterns of Enterprise Application Architecture by Martin Fowler

    • Clean Architecture by Robert Cecil Martin • Domain-Driven Design by Eric Evans • Growing Rails Applications in Practice by Henning Koch and Thomas Eisenbarth 69
  70. ࢀߟࢿྉ • Martin Fowler: Development of Further Patterns of Enterprise

    Application Architecture • Clean Coder Blog: The Clean Architecture • The Onion Architecture : part 1 | Programming with Palermo 70
  71. ࢀߟࢿྉ • RailsެࣜυΩϡϝϯτ • Active Record Associations — Ruby on

    Rails Guides • Active Model Basics — Ruby on Rails Guides • accepts_nested_attributes_for • ActiveRecord::Inheritance • ActiveSupport::Concern 71
  72. ࢀߟࢿྉ • தن໛Web։ൃͷͨΊͷMVC෼ׂͱϨΠϠΞʔΩςΫνϟ - Qiita • acceptsnestedattributes_forΛ࢖Θͣɺෳ਺ͷࢠϨίʔυΛอଘ ͢Δ | Money

    Forward Engineers' Blog • Concerns about Concerns - Speaker Deck • Model ͱը໘্ͷ form ͕1ର1ͰҰக͠ͳ͍৔߹ɺͲͷΑ͏ʹ ࣮૷͢Δͷ͕៉ྷͳͷ͔ʁ - clean-rails.org • Architecture: Interactors | Hanami Guides 72
  73. ࢀߟࢿྉ • RailsͰॏཁͳύλʔϯpart 1: Service Objectʢ຋༁ʣʛ TechRachoʢςοΫϥονϣʣʙΤϯδχΞͷʮʁʯΛʮʂʯ ʹʙʛBPSגࣜձࣾ • Decorator

    ͱ Presenter Λ࢖͍෼͚ͯɺ Rails Λ ViewModel Ͱ ͖ͬ͢Γͤ͞Α͏ - KitchHike Tech Blog • ͯΊ͑ΒͷRails͸ΦϒδΣΫτࢦ޲͡ΌͶ͑ʂ·ͣ͸Callback ΫϥεɺValidatorΫϥεΛ׆༻͠Ζʂ - Qiita 73
  74. ࢀߟࢿྉ • Why Service Objects are an Anti-Pattern — INTERSECT

    • Service Object͕ΞϯνύλʔϯͰ͋Δཧ༝ͱΑΓΑ͍୅ସ खஈʢ຋༁ʣʛTechRachoʢςοΫϥονϣʣʙΤϯδχΞ ͷʮʁʯΛʮʂʯʹʙʛBPSגࣜձࣾ • RailsͰෳ਺ϞσϧΛѻ͏ϑΥʔϜΛ͖ͬ͢Γॻ͘ʢFormΦϒ δΣΫτʣ - LiBz Tech Blog • ActiveRecordͷϞσϧ͕1ͭͩͱͭΒ͍ - Qiita 74