Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Jak (nie) używać Service Object

Jak (nie) używać Service Object

Krzysztof Wawer

November 19, 2014
Tweet

More Decks by Krzysztof Wawer

Other Decks in Technology

Transcript

  1. 7 Patterns to Refactor Fat ActiveRecord Models Value Objects Service

    Objects Form Objects Query Objects http:/ /blog.codeclimate.com/blog/2012/10/17 /7-ways-to-decompose-fat-activerecord-models/ View Model Objects Policy Objects Decorators
  2. Nazwy klass, modułów w Ruby on Rails Kontrolery: HomeController, UsersController,

    … Helpery: HomeHelper, UsersHelper, … Modele: User, ...
  3. Nazwy klass w Service Object Form Object: UserForm Service Object:

    UserAuthenticator => UserAuthenticatorService UserAuthenticator => User::Authenticator UserAuthenticator => User::AuthenticatorService
  4. Dlaczego stosować rozróżnienia w nazwach klas ? Klasy: • Product

    • ProductFirmware • ProductModel • ProductState • ProductType • ProductCreator • ProductFinder Klasy: • Quality • QualityComment • QualityHasSerialNumber • QualityType Które klasy są modelami, a które są klasami Service Object ?
  5. Dlaczego stosować rozróżnienia w nazwach klas ? Klasy: • Product

    • ProductFirmware • ProductModel • ProductState • ProductType • ProductCreator • ProductFinder Klasy: • Quality • QualityComment • QualityHasSerialNumber • QualityType Które klasy są modelami, a które są klasami Service Object ?
  6. Form Object - kiedy używać ? Użytkownik: • rejestracja (wymaga

    hasła) • aktualizacja profilu (nie wymaga hasła) • zmiana hasła (wymaga hasła)
  7. Form Object - kiedy używać ? Nie używać modelu ORM

    jako formularz Zalety: izolacja pozostałych funkcjonalności szybsze testowanie obiektu Wady: duplikacja walidacja, dwustopniowa walidacja więcej kodu
  8. Service Object class SessionsController < ApplicationController def create user =

    User.where(email: params[:email]).first if UserAuthenticator.new(user).authenticate(params[:password]) self.current_user = user redirect_to dashboard_path else flash[:alert] = "Login failed." render "new" end end end
  9. Service Object class SessionsController < ApplicationController def create user =

    User.where(email: params[:email]).first if UserAuthenticator.new(user).authenticate(params[:password]) self.current_user = user redirect_to dashboard_path else flash[:alert] = "Login failed." render "new" end end end
  10. Interactor class SessionsController < ApplicationController def create UserAuthenticatorInteractor.new(self).run(params[:password]) end def

    create_succeeded(user, message) self.current_user = user redirect_to dashboard_path end def create_failed(message) flash[:alert] = message render "new" end end October CincyRb - Jim Weirich on Decoupling from Rails
  11. class SessionsController < ApplicationController def create UserAuthenticatorInteractor.new(self).run(params[:password], success: ->(user, message)

    { self.current_user = user redirect_to dashboard_path }, failure: ->(message) { flash[:alert] = message render "new" }) end end Interactor October CincyRb - Jim Weirich on Decoupling from Rails
  12. View Model Object… grr Problem z nazwą typu obiektów: Presenter,

    FormObject, View, ViewModel • Presenter - Jay Fields (http:/ /blog.jayfields.com/2007 /03/ rails-presenter-pattern.html) „The Presenter pattern addresses bloated controllers and views containing logic in concert by creating a class representation of the state of the view. An architecture that uses the Presenter pattern provides view specific data as attributes of an instance of the Presenter. The Presenter's state is an aggregation of model and user entered data.”
  13. View Model Object… grr class Statistic def initialize(params) ... end

    def users ... end def products ... end ... end
  14. Policy Object CanCan (ostatni commit 6 września 2013) accessible_by -

    problematyczne działanie konstrukcja reguł w ability.rb uzależniona od używanego ORMa (ActiveRecord, DataMapper, Mongoid) Helpery Policy Object niezależny od ORMa używany tylko do pojedynczych obiektów a nie do kolekcji