$30 off During Our Annual Pro Sale. View Details »

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. Jak (nie) używać
    Service Object
    Krzysztof Wawer

    View Slide

  2. O mnie
    Krzysztof Wawer
    [email protected]
    Github: wafcio
    Twitter: @KrzysztofWawer
    Neubloc Polska

    View Slide

  3. Na początku było …
    The Rails Way

    View Slide

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

    View Slide

  5. Struktura aplikacji

    View Slide

  6. 1. Nazwy klass

    View Slide

  7. Nazwy klass, modułów
    w Ruby on Rails
    Kontrolery:
    HomeController, UsersController, …
    Helpery:
    HomeHelper, UsersHelper, …
    Modele:
    User, ...

    View Slide

  8. Nazwy klass w Service Object
    Form Object:
    UserForm
    Service Object:
    UserAuthenticator => UserAuthenticatorService
    UserAuthenticator => User::Authenticator
    UserAuthenticator => User::AuthenticatorService

    View Slide

  9. 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 ?

    View Slide

  10. 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 ?

    View Slide

  11. 2. Ograniczenie liczby
    wywołań Service Objectów

    View Slide

  12. Sieci Komputerowe - hop

    View Slide

  13. Service Object

    View Slide

  14. 3. Form Object - kiedy używać ?

    View Slide

  15. Form Object - kiedy używać ?
    Użytkownik:
    • rejestracja (wymaga hasła)
    • aktualizacja profilu (nie wymaga hasła)
    • zmiana hasła (wymaga hasła)

    View Slide

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

    View Slide

  17. 4. Service Object ≠ Interactor

    View Slide

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

    View Slide

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

    View Slide

  20. Clean Architecture

    View Slide

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

    View Slide

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

    View Slide

  23. 5. View Models Object -
    brzydka nazwa

    View Slide

  24. 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.”

    View Slide

  25. View Model Object… grr
    class Statistic
    def initialize(params)
    ...
    end
    def users
    ...
    end
    def products
    ...
    end
    ...
    end

    View Slide

  26. View Model Object… grr
    View Model => Presenter

    View Slide

  27. 6. Era CanCana za nami

    View Slide

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

    View Slide

  29. Policy Object
    https:/
    /github.com/elabs/pundit

    View Slide

  30. Policy Object
    Gemy wspomagających integrację z aplikacją:
    Pundit (https:/
    /github.com/elabs/pundit)
    Allowy (https:/
    /github.com/dnagir/allowy)

    View Slide