Pro Yearly is on sale from $80 to $50! »

Embrace multi-model thinking

Embrace multi-model thinking

F9c1a378a1e3926ea1a58cf724140000?s=128

Ivan Nemytchenko

April 13, 2019
Tweet

Transcript

  1. Embrace multi- model thinking! Ivan Nemytchenko, @inem, 2019

  2. • Freelancing • Exploring apps architecture • Writing book •

    Teaching at Goodprogrammer.ru • Gitlabbing • Travelling Ivan Nemytchenko Omsk → Belgrade @inem http://inem.at
  3. Rubytrip! 40 days, 4 conferences, 8 countries - RubyConfBY Minsk

    - April 6 - RubyDay Verona - April 11 - RubyWine Kishinev - April 13 - RailsConf Minneapolis - April 30 - Saint P RubyConf - June 1-2
  4. Rubytrip! 40 days, 4 conferences, 8 countries - RubyConfBY Minsk

    - April 6 - RubyDay Verona - April 11 - RubyWine Kishinev - April 13 - RailsConf Minneapolis - April 30 - Saint P RubyConf - June 1-2
  5. 1.System thinking 2.State 3.Layers of abstractions 4.Side-effects 5.Entry point pressure

    6.Building blocks
  6. 1.System thinking 2.State 3.Layers of abstractions 4.Side-effects 5.Entry point pressure

    6.Building blocks
  7. System thinking Lifecycle Roles ISO/IEC/ IEEE 42010 Systems and software

    engineering — Architecture description coursera.org/learn/system-thinking
  8. : Roles System thinking

  9. None
  10. None
  11. None
  12. None
  13. None
  14. None
  15. None
  16. None
  17. None
  18. None
  19. None
  20. Architect role

  21. Number of models 25 50 75+

  22. Inception → Growth → Operation → (Decay) System thinking: Lifecycle

  23. Design Stamina Hypothesis by Martin Fowler

  24. Inception → Growth → Operation → (Decay)

  25. Inception → Growth → Operation → (Decay)

  26. Inception → Growth → Operation → (Decay)

  27. Inception → Growth → Operation → (Decay)

  28. State

  29. 1.Invited 2.Registered 6.Deleted 3.Banned 5.Expired 4.Promoted User

  30. 1.Invited 2.Registered 6.Deleted 3.Banned 5.Expired 4.Promoted User Invoice 1.Created 2.Sent

    3.Delivered 4.Paid 5.Expired Task 1.Created 2.Assigned 3.Sheduled 4.Completed 5.Archived
  31. The natural way to implement states

  32. - if current_organisation_subscribed? %p You are currently subscribed, using the

    following payment method: %p== #{@org.card_brand}: **** **** **** #{@org.card_last4} %p== Expires #{@org.card_exp_month} / #{@org.card_exp_year} - if @org.last_charge %p== The last payment of $#{@org.last_charge.amount/100} was taken on #{@org.last_charge.created_at.strftime("%d %b % %p== The next payment of $100 is due to be taken on #{(@org.last_charge.created_at + 1.month).strftime("%d %b %Y")} = link_to "Update credit card", edit_subscription_path, class: "btn btn-primary" %p = link_to "Cancel my subscription", subscription_path, method: "DELETE", class: "btn btn-danger", data: {confirm: 'Ar sure?'} - else - if @org.trial_ended? %h2 Thank you for trialing our product %h4 To continue using our product please subscribe to the following plan: - else %h2== Thank you for trialing our product (#{distance_of_time_in_words(@org.created_at, Time.zone.now)} left) %h4 To continue using our product after trial please subscribe to the following plan: %p $100/month %p Up to 500 devices and 200 users %p 3 admin users %p CSV/XLS upload = link_to "Subscribe", new_subscription_path, class: 'btn btn-primary'
  33. - if current_organisation_subscribed? #... - if @org.last_charge #... - elsif

    @org.last_charge_status != 'success' #... #... - if @org.expires_at #... - else - if @org.trial_ended? #... - else #... #...
  34. if current_user.active && current_user.account && !current_user.account.deleted && current_org.trial_ends_at > Time.current

  35. Every flag doubles the number of states .trial_ended? .expired? .cancelled?

    .subscription_expired? 4 flags = 16 states!
  36. Flags can be hidden dates ranges, associations, status fields -

    if current_organisation_subscribed? #... - if @org.last_charge #... - elsif @org.last_charge_status != 'success' #... #... - if @org.expires_at #... - else - if @org.trial_ended? #... - else #... #...
  37. State Machines

  38. State Machines

  39. State Machines

  40. State Machines

  41. gem ‘aasm’ gem ‘state-machines’

  42. state_machine :state, initial: :trial do state :trial state :trial_ended state

    :subscribed state :cancelled_subscription state :not_subscribed state :payment_error event :subscribe do transition [:trial, :not_subscribed, :trial_ended] => :subscribed end event :disable_trial do transition :trial => :trial_ended end event :usage_period_ended do transition :cancelled_subscription => :not_subscribed end #... end
  43. - if current_org.trial? #... - elsif current_org.trial_ended? #... - elsif

    current_org.subscribed? #... - elsif current_org.cancelled_subscription? #... - elsif current_org.payment_error? #... - elsif current_org.not_subscribed? #... render partial: current_org.state
  44. def something(org) if org.new_record? # ... 16*2 = 32 states!

  45. Control the number of degrees of freedom of your app

  46. Entry point pressure

  47. None
  48. None
  49. None
  50. False assumtions 1 model → 1 controller respond_to

  51. None
  52. None
  53. None
  54. None
  55. Small scale respond_to do |format| format.html { render locals: {query:

    params[:q]} } format.json { @devices = @devices.first(5) @employees = @employees.first(3) } end
  56. larger scale (REDMINE) respond_to do |format| format.html { @issue_count =

    @query.issue_count @issue_pages = Paginator.new @issue_count, per_page_option, params['page'] @issues = @query.issues(:offset => @issue_pages.offset, :limit => @issue_pages.per_page) render :layout => !request.xhr? } format.api { @offset, @limit = api_offset_and_limit @query.column_names = %w(author) @issue_count = @query.issue_count @issues = @query.issues(:offset => @offset, :limit => @limit) Issue.load_visible_relations(@issues) if include_in_api_response?('relations') } format.atom { @issues = @query.issues(:limit => Setting.feeds_limit.to_i) render_feed(@issues, :title => "#{@project || Setting.app_title}: #{l(:label_issue_plural)}") } format.csv { @issues = @query.issues(:limit => Setting.issues_export_limit.to_i) send_data(query_to_csv(@issues, @query, params[:csv]), :type => 'text/csv; header=present', :filename => 'issues.csv') } format.pdf { @issues = @query.issues(:limit => Setting.issues_export_limit.to_i) send_file_headers! :type => 'application/pdf', :filename => 'issues.pdf' } end
  57. HTML, JSON, PDF, RSS they all used in different ways

    different subsets of data different ways to generate results So it makes sense to create separate REST representations
  58. TOP-LEVEL namespaces class Web::ArticlesController < Web::ApplicationController #... end class Api::ArticlesController

    < Api::ApplicationController #... end
  59. Hierarchy based on “BOUNDED CONTEXTS” scope module: :web do namespace

    :moderation do resources :articles, only: [:index, :edit, :update, :show] do member do patch :publish end end end resources :articles do member do patch :moderate end scope module: :articles do resources :comments do scope module: :comments do resources :likes, only: [:create] end end end end root 'welcome#index' end
  60. Reduce entry point pressure

  61. railshurts.com/lifecycle Rails developer Lifecycle

  62. Rails developer Lifecycle railshurts.com/lifecycle

  63. railshurts.com/lifecycle Rails developer Lifecycle

  64. Painless Rails principles 1.Differentiate 'schema' from 'implementation' 2.Reduce entry point

    pressure 3.Control the number of degrees of freedom of the app 4.Don't mix layers of abstractions 5.Don't fight against the framework railshurts.com/rails-principles
  65. Sources SICP (Structure and Interpretation of Computer Programs) TAPL (Types

    and Programming Languages) by Benjamin C. Pierce DDD (Domain-driven design) by Eric J. Evans CC2e (Code Complete 2nd edition) by Steve McConnell railshurts.com/rails-principles ISO/IEC/ IEEE 42010 Systems and software engineering — Architecture description
  66. Email mini-course “Rails Pitfalls”: railshurts.com/rails-pitfalls inem.at, @inem