O slideshow foi denunciado.
Utilizamos seu perfil e dados de atividades no LinkedIn para personalizar e exibir anúncios mais relevantes. Altere suas preferências de anúncios quando desejar.

Quick Left: Confidently Build Complex Domains in Rails

3.709 visualizações

Publicada em

Modeling large, complex domains "the Rails way” can cause some serious pain. Ruby and Rails are supposed to make developers happy. Let's not allow “the Rails way” and complex domains to take the “happy" out of ruby development. The goal is to allow your Rails application to express the domain so that the domain's business logic is clear-cut, easy to grok, and designed in a way that reduces unnecessary complexities and coupling.

Publicada em: Tecnologia
  • Seja o primeiro a comentar

Quick Left: Confidently Build Complex Domains in Rails

  1. 1. CONFIDENTLY BUILD COMPLEX DOMAINS! IN RAILS Mike AbiEzzi
  2. 2. WHAT’S A DOMAIN?
  3. 3. WHY? software gets complex fast … and nobody wants a train wreck of models
  4. 4. ▾ app/! ▸ assets/! ▸ controllers/! ▸ helpers/! ▸ mailers/! ▸ models/! ▸ views/
  5. 5. Developer 1 n App Store Customer App Install nn n n Purchase Comment 1 n n 1 Version 1 n 1..6 Screenshot 1
  6. 6. 1. Process of defining the domain! 2. Communicating the domain! 3. Relationships between models! 4. Aggregates! 5. Value Objects! 6. Domain Services! 7. Data access
  7. 7. 1 n App Store Developer 1 n Customer App Install nn n n Purchase Comment 1 n n 1 Review! comment! rating Version 1 n 1..6 Screenshot 1 Release! version_number Developer/ CompanySeller
  8. 8. COMMUNICATION Domain! Expert Software! Expert brainstorm → draw diagrams → ! speak out assumptions → let them correct you → refine
  9. 9. COMMUNICATION “Domain experts should object to terms or structures that are awkward or inadequate to convey domain understanding” “Developers should watch for ambiguity or inconsistency that will trip up design.” -- Eric Evans
  10. 10. UBIQUITOUS LANGUAGE “a common, rigorous language between developers and users […] Domain! Expert Developer Product! Owner User Designer Code the need for it to be rigorous, since software doesn't cope well with ambiguity” — Martin Fowler Ensure one consistent language
  11. 11. app.submit_release(“1.1.0”, ... , screenshots: ["shot1.png", "shot2.png"]) release = Release.new( major: 1, minor: 1, build: 0, ...) release.screenshots << [ Screenshot.new(file: "shot1.png"), Screenshot.new(file: "shot2.png")] release.status = :submitted ! app.releases << release A BETTER WAY?Describe a domain behavior with a methodEasy to understandSimplifies InteractionManages complexities Submit a new release of your app
  12. 12. App 1 Comment 1 n Review! comment! rating n 1..6 Screenshot 1 AGGREGATE Release! version_number
  13. 13. class App < ActiveRecord::Base ... ! def create_release ... def submit_release ... def approve_release ... def flag_for_abuse ... ! def mark_as_staff_favorite ... ! end Tells a story of how the domain works
  14. 14. ENTITY VALUE a thing describes a thing can change state doesn’t reference anything unique independent! of attributes avoids design complexities immutablehas a lifecycle class Customer class Name
  15. 15. App 1 Comment 1 n Review! comment! rating n 1..6 Screenshot 1 Value Value Entity Release! version_number Entity
  16. 16. class Screenshot attr_reader :file, :position def initialize(file, position) @file, @position = file, position end def ==(other) file == other.file && position == other.position end end VALUE OBJECT immutable / equality
  17. 17. class Screenshot include Comparable attr_reader :file, :position ... ! def <=>(other) position <=> other.position end end comparableVALUE OBJECT
  18. 18. class VersionNumber attr_reader :major, :minor, :build ... def next_major_version new VersionNumber(major + 1, minor, build) end ! def next_minor_version ... def next_build_version ... end VALUE OBJECT factories
  19. 19. one-to-many or one-to-one (1-n or 1-1) C. In its own table B. Serialized on the Entity’s table 3 ways to persist value objects A. Inline on the Entity’s table one-to-one (1-1)
  20. 20. class Release < ActiveRecord::Base def version_number= vn version_number_major = vn.major version_number_minor = vn.minor version_number_build = vn.build end ! def version_number new VersionNumber( version_number_major, version_number_minor, version_number_build) end end (1-1)A. Inline on the Entity’s table
  21. 21. class Release < ActiveRecord::Base composed_of :version_number, mapping [ %w(version_number_major major), %w(version_number_minor minor), %w(version_number_build build) ] ... end Release attributes VersionNumber attributes Inline on the Entity’s table (1-1)
  22. 22. Gift an App SERVICE 1. Charge the gifter that’s purchasing the app.! 2. Assign access rights to the giftee receiving the app. Facilitates a transaction between two aggregates
  23. 23. class GiftApp def self.execute(app, gifter, giftee) Purchase.create( app: app, customer: gifter, ...) AccessRight.create( app: app, customer: giftee, purchase: purchase, ...) end end AccessRight 1 n Purchase 1 n Customer
  24. 24. ▾ app/! ▸ models/! ▾ services/! create_app.rb! gift_app.rb! refund_purchase.rb! ...
  25. 25. Data Access App.where( "create_at > ? and purchase_count > ?", 1.week.ago, 10000).all class App < ActiveRecord::Base scope :new_and_noteworthy, -> { where("create_at > ? and purchases > ?", 1.week.ago, 10000) } end Use scopes
  26. 26. class App < ActiveRecord::Base scope :new_and_noteworthy, ... scope :staff_picks, ... scope :most_popular, ... ... def create_release(…) def submit_release(…) ... end One expressive point of entry * you only need to retrieve aggregate roots *
  27. 27. ▾ app/! ▾ models/! ▾ apps/! app.rb! release.rb! review.rb! screenshot.rb! version_number.rb! ▸ customers/! ▸ sellers/! ▸ services/
  28. 28. IN SUMMARY
  29. 29. Continuously refine the domain with a domain expert 1
  30. 30. Insist on having a ubiquitous language for seamless communication 2
  31. 31. Constrain relationships to only what the domain needs 3
  32. 32. Create aggregates that express domain concepts and manage complexity 4
  33. 33. Create value objects to eliminate unnecessary complexity 5
  34. 34. Create domain services to express transactions ! between aggregates 6
  35. 35. Express the domain’s intent through! well defined data access 7
  36. 36. ▾ app/! ▸ assets/! ▸ controllers/! ▸ helpers/! ▸ mailers/! ▸ models/! ▸ views/
  37. 37. ▾ app/! ▾ models/! ▸ apps/! ▸ customers/! ▸ sellers/! ▸ ...! ▸ services/! create_app.rb! gift_app.rb! refund_purchase.rb! ...
  38. 38. Some Pro Tips 1. Don’t let your database diverge to far from your domain model! 2. Don’t try to build and maintain a huge diagram of your domain! 3. Separate you domain logic from view logic! 4. You can use the gem fig_leaf to privatize ActiveRecord methods (e.g. create, where)
  39. 39. Thanks for Listening! @mjezzi www.virtual-genius.com/ www.mikeabiezzi.com

×