• Like
Model of the colossus @ Rupy Brazil 2013
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

Model of the colossus @ Rupy Brazil 2013

  • 728 views
Published



Saiba como não deixar seu model tornar-se um ameaçador colosso em sua app Rails. Dicas sobre como não deixar seu model cheio de responsabilidades, seguindo o SRP e refactories usando PORO dentre outras técnicas. Vamos ver alguns anti-patterns em models e soluções para resolvê-los. Também será apresentado alguns bad smells que podem estar dizendo que nosso model pode estar se tornando um colosso.

Published in Technology , Lifestyle , Business
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
728
On SlideShare
0
From Embeds
0
Number of Embeds
1

Actions

Shares
Downloads
7
Comments
0
Likes
6

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Model of the Colossus
  • 2. Mauro quem...
  • 3. RSpec Best Friends
  • 4. maurogeorge.com.br
  • 5. Seu model, um grande colosso
  • 6. Seu model, um grande colosso Rails 15 minutes blog MVC Rails way Aplicações grandes 37 Signals stack ERB MySQL MiniTest Fat Models, Skinny Controllers Prime stack Haml PostgreSQL Rspec/Cucumber Skinny models, controllers, and a service layer
  • 7. AR quebra o SRP Alto acoplamento Callback Observer Finders Falta de coesão Persiste dados Envia e-mail Acessa Api externas
  • 8. Anti-pattern Model gerando conteúdo para a view
  • 9. Anti-pattern: Model gerando conteudo para a view app/models/user.rb class User < ActiveRecord::Base Alto acoplamento # ... def info %Q{ <ul> <li>#{name}</li> <li>#{email}</li> </ul> } end end Falta de coesão
  • 10. Solução: Decorator app/decorators/user_decorator.rb require 'delegate' class UserDecorator < SimpleDelegator def info %Q{ <ul> <li>#{name}</li> <li>#{email}</li> </ul> } end end # Exemplo user = User.find(1) user_decorator = UserDecorator.new(user) user_decorator.info user_decorator.name Baixo acoplamento Alta coesão
  • 11. Anti-pattern: Model gerando conteudo para a view app/models/user.rb class User < ActiveRecord::Base # ... def wrote_post?(post) if post.user_id == id "<p>O post #{post.title} foi escrito por #{name}</p>" end end end Alto acoplamento Falta de coesão Método de Post ou User
  • 12. Solução: Presenter app/presenters/writter_post_presenter.rb class WritterPostPresenter def initialize(user, post) @user, @post = user, post end Baixo acoplamento Alta coesão def post_is_wrote_by_writter? if wrote_post? "<p>O post #{post.title} foi escrito por #{user.name}</p>" end end private attr_reader :user, :post # Exemplo def wrote_post? user == post.user user = User.find(1) post = Post.find(1) end writter_post_presenter = WritterPostPresenter.new(user, post) end writter_post_presenter.post_is_wrote_by_writter?
  • 13. Presenters, decorators, exhibit, View Objects e helpers??? Helpers Procedurais Decorators Para uma única entidade Presenters Para multiplas entidades
  • 14. Anti-pattern Model Callbacks
  • 15. Anti-pattern: Model Callbacks app/models/post.rb class Post < ActiveRecord::Base Alto acoplamento # ... after_save :notify_users # ... private def notify_users NotifyMailer.delay.notify(self) end end Falta de coesão Testes lentos
  • 16. Solução: PORO model app/models/post_creator.rb class PostCreator def initialize(post) @post = post end def save post.save && notify_users end private attr_reader :post def notify_users NotifyMailer.delay.notify(post) end end Baixo acoplamento Alta coesão Testes rápidos
  • 17. Solução: PORO model app/controllers/posts_controller.rb class PostsController < ApplicationController # .. def create @post = current_user.posts.new(post_params) if @post.save PostCreator.new(@post).save redirect_to posts_path, notice: "Post criado com sucesso!" else render 'new' end end end
  • 18. Anti-pattern Model salvando N models
  • 19. Anti-pattern: Model salvando N models app/models/user.rb class User < ActiveRecord::Base Alto acoplamento # ... accepts_nested_attributes_for :posts end Falta de coesão
  • 20. Solução: Form Object app/models/app/models/user_with_post.rb class UserWithPost include ActiveModel::Model attr_accessor :user_name, :user_email, :post_title, :post_content validates :user_name, :user_email, :post_title, :post_content, presence: true def save return false unless valid? user = User.create(name: user_name, email: user_email) user.posts.create(title: post_title, content: post_content) true end Baixo acoplamento Alta coesão end # Exemplo params = { user_name: "Mauro", user_email: "maurogot@gmail.com", post_title: "Post 1", post_content: "Content"} user_with_post = UserWithPost.new(params) user_with_post.save
  • 21. Anti-pattern Scopes para um único problema
  • 22. Anti-pattern: Scopes para um único problema app/models/post.rb class Post < ActiveRecord::Base # ... scope scope scope scope # ... end :from, ->(user) { where(user_id: user.id) } :recents, -> { order(created_at: :asc) } :top_likeds, -> { order(like_count: :asc) } :top_from, ->(user) { from(user).recents.top_likeds } Falta de coesão
  • 23. Solução: Query object app/queries/top_post_query.rb class TopPostQuery Alta coesão def initialize(relation = Post.all) @relation = relation.extending(Scopes) end def top_from(user) @relation.from(user).recents.top_likeds end module Scopes def from(user) where(user_id: user.id) end def recents order(created_at: :asc) end def top_likeds order(like_count: :asc) end # ... # Exemplo user = User.find(1) top_post_query = TopPostQuery.new top_post_query.top_from(user)
  • 24. Anti-pattern ActiveSupport::Concerns
  • 25. Anti-pattern: ActiveSupport::Concerns app/models/concerns/likeable.rb module Likeable extend ActiveSupport::Concern def liked_by(user) return false if user_already_liked?(user) up_one_like add_user_as_voted(user) end def unliked_by(user) return false unless user_already_liked?(user) down_one_like remove_user_as_voted(user) end private attr_reader :likeable, :user def up_one_like # ... end # ... Alto acoplamento Falta de coesão
  • 26. Anti-pattern: ActiveSupport::Concerns app/models/post.rb class Post < ActiveRecord::Base include Likeable # ... end Esconde responsabilidade
  • 27. Solução: Service app/services/like_manager.rb class LikeManager def initialize(likeable, user) @likeable, @user = likeable, user end def like return false if user_already_liked? up_one_like add_user_as_voted end Baixo acoplamento Alta coesão Única responsabilidade Duck Typing def unlike return false unless user_already_liked? down_one_like remove_user_as_voted end # Exemplo user = User.find(1) private post = Post.find(1) like_manager = LikeManager.new(post, user) attr_reader :likeable, :user like_manager.like like_manager.unlike def up_one_like # ...
  • 28. Bad Smells Meu Model está virando um Colosso?
  • 29. Bad Smell: N métodos com nome de outra entidade app/models/post.rb class Post < ActiveRecord::Base # ... def popular_comments # ... end def most_viewed_comment # ... end def most_replied_comment # ... end end
  • 30. Bad Smell: N métodos recebendo o mesmo paramêtro app/models/post.rb class Post < ActiveRecord::Base # ... def self.most_popular_from(user) self.top_posts_from(user) self.more_social_media_repercussion_from(user) # ... end private def self.top_posts_from(user) # ... end def self.more_social_media_repercussion_from(user) # ... end end
  • 31. Bad Smell: N métodos privados que são usados em apenas um método app/models/post.rb class Post < ActiveRecord::Base # ... def self.most_popular self.most_commented self.more_social_media_repercussion # ... end private def self.most_commented # ... end def self.more_social_media_repercussion # ... end end
  • 32. Bad Smell Classe gigante (Provavelmente uma God Class) Prefira N classes pequenas
  • 33. Futuro DCI Funcional
  • 34. Conclusão Crie classes Quebre Model e Classes grandes em classes menores Divida responsabilidades Classes que façam apenas uma coisa bem feita
  • 35. Obrigado
  • 36. maurogeorge.com.br
  • 37. Referências http://rubyweekly.com/archive/124.html http://rubyweekly.com/archive/126.html http://robots.thoughtbot.com/post/14825364877/evaluating-alternative-decoratorimplementations-in http://mikepackdev.com/blog_posts/31-exhibit-vs-presenter samuelmullen.com/2013/05/the-problem-with-rails-callbacks http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecordmodels/ http://rubysource.com/ddd-for-rails-developers-part-1-layered-architecture/ http://blog.plataformatec.com.br/2012/03/barebone-models-to-use-with-actionpackin-rails-4-0/ http://www.youtube.com/watch?v=DC-pQPq0acs http://objectsonrails.com/