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

Model of the colossus @ Rupy Brazil 2013

on

  • 890 views

...



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.

Statistics

Views

Total Views
890
Slideshare-icon Views on SlideShare
878
Embed Views
12

Actions

Likes
6
Downloads
6
Comments
0

1 Embed 12

https://twitter.com 12

Accessibility

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    Model of the colossus @ Rupy Brazil 2013 Model of the colossus @ Rupy Brazil 2013 Presentation Transcript

    • Model of the Colossus
    • Mauro quem...
    • RSpec Best Friends
    • maurogeorge.com.br
    • Seu model, um grande colosso
    • 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
    • AR quebra o SRP Alto acoplamento Callback Observer Finders Falta de coesão Persiste dados Envia e-mail Acessa Api externas
    • Anti-pattern Model gerando conteúdo para a view
    • 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
    • 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
    • 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
    • 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?
    • Presenters, decorators, exhibit, View Objects e helpers??? Helpers Procedurais Decorators Para uma única entidade Presenters Para multiplas entidades
    • Anti-pattern Model Callbacks
    • 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
    • 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
    • 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
    • Anti-pattern Model salvando N models
    • 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
    • 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
    • Anti-pattern Scopes para um único problema
    • 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
    • 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)
    • Anti-pattern ActiveSupport::Concerns
    • 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
    • Anti-pattern: ActiveSupport::Concerns app/models/post.rb class Post < ActiveRecord::Base include Likeable # ... end Esconde responsabilidade
    • 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 # ...
    • Bad Smells Meu Model está virando um Colosso?
    • 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
    • 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
    • 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
    • Bad Smell Classe gigante (Provavelmente uma God Class) Prefira N classes pequenas
    • Futuro DCI Funcional
    • Conclusão Crie classes Quebre Model e Classes grandes em classes menores Divida responsabilidades Classes que façam apenas uma coisa bem feita
    • Obrigado
    • maurogeorge.com.br
    • 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/