Rotterdam.rb Domain Driven Design

2,985 views

Published on

A presentation about Domain Driven Design with examples in Ruby. Inspired by the talk by Pat Maddox on RailsConf 2010.

Published in: Technology
0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,985
On SlideShare
0
From Embeds
0
Number of Embeds
187
Actions
Shares
0
Downloads
23
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide

































































































































  • Rotterdam.rb Domain Driven Design

    1. 1. Domain Driven Design
    2. 2. Iain Hecker • Ruby developer at Finalist IT Group • 4 years of Ruby experience • Contributed to Rails core / i18n • 9 gems released • http://iain.nl • http://github.com/iain • @iain_nl
    3. 3. the problem
    4. 4. symptoms • maintaining code is annoying • adding features is a pain • development speed is low • system is unstable, but you don't exactly know why • inconsistency and side effects fuck everything up • fear of upgrading • "I'm not touching that code!"
    5. 5. complexity
    6. 6. two categories of complexity
    7. 7. accidental complexity vs. essential complexity
    8. 8. "I need to do ..... because the code needs ....." vs. "I need to do ..... because the business needs ....."
    9. 9. lower accidental complexity • use great frameworks (like Rails) • test all the fucking time • refactor all the fucking time • use programming patterns (like MVC) • pair-programming
    10. 10. domain-driven design
    11. 11. it's a book!
    12. 12. "Tackling Complexity in the Heart of Software"
    13. 13. organizing essential complexity
    14. 14. actions to organize complexity • use the right language • finding the core domain • set the proper boundaries • find the right models
    15. 15. the right language
    16. 16. ubiquitous language technical terms domain model terms business terms
    17. 17. refactor when you got the name wrong
    18. 18. organizing complexity
    19. 19. example: local government
    20. 20. AgreementReference AgreementSubscription AgreementType Agreement
    21. 21. AgreementReference RecentView UserSession AgreementSubscription AgreementType User Agreement
    22. 22. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search
    23. 23. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search Party Department Person Group Member
    24. 24. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Group Member
    25. 25. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    26. 26. high complexity
    27. 27. no clear structure
    28. 28. risk of side effects
    29. 29. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    30. 30. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    31. 31. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    32. 32. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    33. 33. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    34. 34. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    35. 35. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    36. 36. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    37. 37. bounded contexts • boundaries lower complexity • different contexts can interfere • leaking between boundaries is called coupling • side effects are bound to one context
    38. 38. prioritize your design
    39. 39. domain types • core domains • support domains • generic support domains
    40. 40. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    41. 41. As an council member (gemeenteraadslid) I can see which agreements have been made So I can supervise the execution of my policy
    42. 42. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    43. 43. As a civil servant (ambtenaar) I can see which agreements have been made So I know what to work on
    44. 44. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    45. 45. adding real value • find what is important for your users • invest time and effort in designing those core domains • slack your efforts in support domains • use existing products for generic support domains (like authentication)
    46. 46. models
    47. 47. models • describe a version of reality to solve a certain problem • do not represent technical terms like 'database tables' • have a meaning in the business domain
    48. 48. AgreementReference RecentView UserSession AgreementSubscription AgreementType SearchSubscription User Agreement Search ProgressNote Party Department Mutation Person Meeting Group Notification Member
    49. 49. AgreementReference AgreementType AgreementSubscription User Agreement Search Mutation Party Meeting
    50. 50. Reference User Subscription Type Search Agreement Involvement Responsible Charger Charged Party Mutation Meeting
    51. 51. guarding your boundaries
    52. 52. Mexico - United States barrier
    53. 53. modular design
    54. 54. modular design != mixins
    55. 55. namespacing
    56. 56. not just about name clashing
    57. 57. organizing complexity
    58. 58. but what about overlap?
    59. 59. Reference user Subscription Type search Agreement Involvement Responsible Charger Charged party mutation meeting
    60. 60. Reference user Subscription Type search Agreement Involvement Responsible Charger Charged one normalized table party mutation meeting
    61. 61. how not to do it
    62. 62. module Agreement   class Involvement     has_one :charged,     :class_name => "Party::Group"     has_one :charger,     :class_name => "Party::Group"     has_one :responsible, :class_name => "Party::Group"   end end module Party   class Group < ActiveRecord::Base   end end
    63. 63. Reference user Subscription Type search Agreement Involvement Responsible Charger Charged party mutation meeting
    64. 64. not just 3 boundary violations
    65. 65. methods are shared too
    66. 66. module Agreement   class Involvement     def charger_busy?       charger.tasks.count > 4     end     def charged_busy?       charger.tasks.count > 0     end   end end
    67. 67. module Party   class Group < ActiveRecord::Base     def busy_charger?       tasks.count > 4     end     def busy_charged?       tasks.count > 0     end   end end
    68. 68. module Party   class Group < ActiveRecord::Base     def busy?(charger = true)       tasks.count > (charger ? 4 : 0)     end   end end
    69. 69. one simple breach of the boundary can cause a lot of pain
    70. 70. a better way
    71. 71. class Group < ActiveRecord::Base end module Agreement   class Involvement     has_one :charged     has_one :charger     has_one :responsible   end   class Charged < Group   end   class Charger < Group   end   class Responsible < Group   end end
    72. 72. } class Group < ActiveRecord::Base end module Agreement   class Involvement     has_one :charged     has_one :charger domain logic     has_one :responsible   end   class Charged < Group   end   class Charger < Group   end   class Responsible < Group   end end
    73. 73. } implementation detail } class Group < ActiveRecord::Base end module Agreement   class Involvement     has_one :charged     has_one :charger domain logic     has_one :responsible   end   class Charged < Group   end   class Charger < Group   end   class Responsible < Group   end end
    74. 74. class Group < ActiveRecord::Base   delegate :count, :to => :tasks, :prefix => true end module Agreement   class Charged < Group     def busy?       tasks_count > 0     end   end   class Charger < Group     def busy?       tasks_count > 4     end   end end
    75. 75. there are other benefits too!
    76. 76. some (unrelated) problems of bleeding boundaries
    77. 77. class Post < ActiveRecord::Base   scope :published, where(:published => true) end class PostsController < ApplicationController   respond_to :html   def index     respond_with(@posts = Post.published)   end   def show     respond_with(@post = Post.published.find(params[:id]))   end end
    78. 78. class Post < ActiveRecord::Base   scope :published, where(:published => true)   scope :visible, where(:visible => true) end class PostsController < ApplicationController   respond_to :html   def index     respond_with(@posts = Post.published.visible.all)   end   def show     respond_with(@post = Post.published.visible.find(params[:id])   end end
    79. 79. class Post < ActiveRecord::Base   scope :published, where(:published => true)   scope :visible, where(:visible => true)   scope :for_frontend, published.visible end class PostsController < ApplicationController   respond_to :html   def index     respond_with(@posts = Post.for_frontend.all)   end   def show     respond_with(@post = Post.for_frontend.find(params[:id])   end end
    80. 80. default scope would be nice
    81. 81. but that introduces a lot of complexity for admins
    82. 82. "the local representative pattern" term coined right now by me
    83. 83. # app/models/frontend/post.rb module Frontend   class Post < ::Post     default_scope published.visible   end end # app/controllers/frontend/posts_controller.rb module Frontend   class PostsController     respond_to :html     def index       respond_with(@posts = Post.all)     end     def show       respond_with(@post = Post.find(params[:id]))     end   end end
    84. 84. module PersonalModel   extend ActiveSupport::Concern   included do     attr_accessor  :current_user module Public     before_update :check_current_user   class Comment < ::Comment     before_destroy :check_current_user     include PersonalModel   end     attr_accessible :body   end   def owned_record? end     current_user == user   end   def check_current_user     raise UnauthorizedUserError.new(self) unless owned_record?   end end
    85. 85. works great for • (default) scopes • validations • attr_protected / attr_accessible • associations • any other piece of code
    86. 86. problems • introduces problems with identity • getting the right objects • Rails conventions
    87. 87. class Comment < ActiveRecord::Base end module Frontend   class Comment < ::Comment   end end Comment.first != Frontend::Comment.first
    88. 88. class Public::CommentsController < ApplicationController   respond_to :html   def new     respond_with(@comment = Comment.new)   end end
    89. 89. class Public::CommentsController < ApplicationController   respond_to :html   def new     respond_with(@comment = Comment.new)   end end module Public   class CommentsController < ApplicationController     respond_to :html     def new       respond_with(@comment = Comment.new)     end   end end
    90. 90. = form_for Comment.new do |f|   = f.text_field :body <form action="/comments" method="post">   <input type="text" name="comment[body]" id="comment_body" /> </form>
    91. 91. = form_for Comment.new do |f|   = f.text_field :body <form action="/comments" method="post">   <input type="text" name="comment[body]" id="comment_body" /> </form> = form_for @comment do |f|   = f.text_field :body <form action="/public/comments" method="post">   <input type="text" name="public_comment[body]" id="public_comment_body" /> </form>
    92. 92. conclusion
    93. 93. divide and conquer
    94. 94. keep accidental complexity to a minimum
    95. 95. essential complexity needs to be clear and organized
    96. 96. fix leaking contexts
    97. 97. prioritize on what you design perfectly
    98. 98. don't lose sight of reality
    99. 99. !ink ab"t it!

    ×