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
Fa...
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

# ....
Solução: Decorator
app/decorators/user_decorator.rb
require 'delegate'
class UserDecorator < SimpleDelegator
def info
%Q{
...
Anti-pattern: Model gerando conteudo para a view
app/models/user.rb
class User < ActiveRecord::Base
# ...
def wrote_post?(...
Solução: Presenter
app/presenters/writter_post_presenter.rb
class WritterPostPresenter
def initialize(user, post)
@user, @...
Presenters, decorators, exhibit, View Objects e
helpers???
Helpers
Procedurais
Decorators
Para uma única entidade
Presente...
Anti-pattern
Model Callbacks
Anti-pattern: Model Callbacks
app/models/post.rb
class Post < ActiveRecord::Base

Alto acoplamento

# ...
after_save :noti...
Solução: PORO model
app/models/post_creator.rb
class PostCreator
def initialize(post)
@post = post
end
def save
post.save ...
Solução: PORO model
app/controllers/posts_controller.rb
class PostsController < ApplicationController
# ..
def create
@pos...
Anti-pattern
Model salvando N models
Anti-pattern: Model salvando N models
app/models/user.rb
class User < ActiveRecord::Base

Alto acoplamento

# ...
accepts_...
Solução: Form Object
app/models/app/models/user_with_post.rb
class UserWithPost
include ActiveModel::Model
attr_accessor :...
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
sco...
Solução: Query object
app/queries/top_post_query.rb
class TopPostQuery

Alta coesão

def initialize(relation = Post.all)
@...
Anti-pattern
ActiveSupport::Concerns
Anti-pattern: ActiveSupport::Concerns
app/models/concerns/likeable.rb
module Likeable
extend ActiveSupport::Concern
def li...
Anti-pattern: ActiveSupport::Concerns
app/models/post.rb
class Post < ActiveRecord::Base
include Likeable
# ...
end

Escon...
Solução: Service
app/services/like_manager.rb
class LikeManager
def initialize(likeable, user)
@likeable, @user = likeable...
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_comme...
Bad Smell: N métodos recebendo o mesmo
paramêtro
app/models/post.rb
class Post < ActiveRecord::Base
# ...
def self.most_po...
Bad Smell: N métodos privados que são usados em
apenas um método
app/models/post.rb
class Post < ActiveRecord::Base
# ...
...
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 apena...
Obrigado
maurogeorge.com.br
Referências
http://rubyweekly.com/archive/124.html
http://rubyweekly.com/archive/126.html
http://robots.thoughtbot.com/pos...
Model of the colossus @ Rupy Brazil 2013
Upcoming SlideShare
Loading in...5
×

Model of the colossus @ Rupy Brazil 2013

844

Published on



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
0 Comments
6 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
844
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
7
Comments
0
Likes
6
Embeds 0
No embeds

No notes for slide

Model of the colossus @ Rupy Brazil 2013

  1. 1. Model of the Colossus
  2. 2. Mauro quem...
  3. 3. RSpec Best Friends
  4. 4. maurogeorge.com.br
  5. 5. Seu model, um grande colosso
  6. 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. 7. AR quebra o SRP Alto acoplamento Callback Observer Finders Falta de coesão Persiste dados Envia e-mail Acessa Api externas
  8. 8. Anti-pattern Model gerando conteúdo para a view
  9. 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. 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. 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. 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. 13. Presenters, decorators, exhibit, View Objects e helpers??? Helpers Procedurais Decorators Para uma única entidade Presenters Para multiplas entidades
  14. 14. Anti-pattern Model Callbacks
  15. 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. 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. 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. 18. Anti-pattern Model salvando N models
  19. 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. 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. 21. Anti-pattern Scopes para um único problema
  22. 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. 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. 24. Anti-pattern ActiveSupport::Concerns
  25. 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. 26. Anti-pattern: ActiveSupport::Concerns app/models/post.rb class Post < ActiveRecord::Base include Likeable # ... end Esconde responsabilidade
  27. 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. 28. Bad Smells Meu Model está virando um Colosso?
  29. 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. 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. 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. 32. Bad Smell Classe gigante (Provavelmente uma God Class) Prefira N classes pequenas
  33. 33. Futuro DCI Funcional
  34. 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. 35. Obrigado
  36. 36. maurogeorge.com.br
  37. 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/
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×