64
23/03/2019 Shower Presentation Engine localhost:8080/#54 1/64 Игорь Александров, JetRockets Co-Founder

@^ccVRDMWZbIMh · 2019. 3. 23. · 23/03/2019 Shower Presentation Engine localhost:8080/#54 1/ 64 @^ccVRDMWZbIMh Игорь Александров, JetRockets Co-Founder

  • Upload
    others

  • View
    0

  • Download
    0

Embed Size (px)

Citation preview

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 1/64

    Not the Rails Way

    Игорь Александров, JetRockets Co-Founder

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 2/64

    Игорь Александров

    jetrockets.pro

    github.com/jetrockets

    github.com/igor-alexandrov

    https://jetrockets.pro/https://github.com/jetrocketshttps://github.com/igor-alexandrov

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 3/64

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 4/64

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 5/64

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 6/64

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 7/64

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 8/64

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 9/64

    Знакомо?

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 10/64

    Разве проблема в Ruby?

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 11/64

    Проблема №1 —архитектура

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 12/64

    @document.save

    class DocumentsController < ApplicationController

    def create

    @document = Document.new(params[:document])

    # 90% of developers don't care about 'magic'

    if

    # …

    else

    # …

    end

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 13/64

    619

    729

    # app/models/documents.rb

    # ###################################

    620 # Callbacks

    621

    622 before_validation :on => :create do

    623 self.scorer = applicant

    624 self.closing_status ||= 'likely'

    # …

    728

    # ###################################

    730 # Class Methods

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 14/64

    Приложение —цепочка сервисов

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 15/64

    Запрос => [*] => [*] => Ответ

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 16/64

    Приложение —цепочка функций

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 17/64

    class CreateDocument

    # ...

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 18/64

    class CreateDocument

    def call

    # ...

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 19/64

    class CreateDocument

    def call(document, params)

    # ...

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 20/64

    class CreateDocument

    def call(document, params)

    document.attributes = params

    document.save!

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 21/64

    class CreateDocument

    def call(document, params)

    document.attributes = params

    document.save!

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 22/64

    class CreateDocument

    def call(document, params)

    document.attributes = params

    # before_create

    rename_document(document) if document.valid?

    document.save!

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 23/64

    class CreateDocument

    def call(document, params)

    document.attributes = params

    rename_document(document) if document.valid?

    document.save!

    # after_commit on: :create

    make_async_call_with_document(document)

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 24/64

    Pros

    Единый интерфейс сервисов;

    частично избавились от колбеков;

    тестирование функциональных объектов — проще;

    композиция сервисов.

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 25/64

    Cons

    Валидации остались в моделях:

    композиция сервисов получалась не всегда, получалосьдублирование;

    тестирование не стало проще из-за невозможности управлятьзависимостями;

    не получалось сделать композицию объектов (Domain).

    •document.save(validate: false) document.from_crm = true

    document.save

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 26/64

    Silver Bullet?

    Form Object!http://trailblazer.to/gems/reform

    http://trailblazer.to/gems/reform

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 27/64

    class CreateDocument

    def call(document, params)

    form = CreateDocumentForm.new(document)

    if form.validate(params)

    document = form.sync

    rename_document(document)

    end

    document.save!

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 28/64

    class CreateDocument

    attr_reader :form_class

    attr_reader :rename_document

    def initialize(form_class:, rename_document:)

    @form_class = form_class

    @rename_document = rename_document

    end

    def call(document, params)

    # …

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 29/64

    class CreateDocument

    def call(document, params)

    form = form_class.new(document)

    if form.validate(params)

    document = form.sync

    rename_document(document)

    document.save!

    end

    document

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 30/64

    dry-validationhttps://dry-rb.org/gems/dry-validation

    https://dry-rb.org/gems/dry-validation

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 31/64

    schema

    # app/services/document/create_document.rb

    class Document::CreateDocument

    def call(document, params)

    result = (document).(params)

    if result.success?

    # …

    end

    end

    end

    class CreateDocumentSchema < Dry::Validation::Schema

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 32/64

    # Initialize once

    command = CreateDocument.new(

    rename_document: Document::RenameDocument.new

    )

    # Use several times

    command.call(passport, params[:passport])

    command.call(visa, params[:visa])

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 33/64

    Profit?

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 34/64

    Не совсем…

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 35/64

    class Document::CreateDocument

    attr_reader :rename_document

    attr_reader :upload_document_to_perfect_audit

    attr_reader :update_expiration_dates

    attr_reader :convert_document_to_pdf

    attr_reader :create_affiliations

    attr_reader :update_cpl_records

    attr_reader :update_application_status

    # hundreds of other stuff here

    # …

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 36/64

    Silver Bullet?

    dry-containerhttps://dry-rb.org/gems/dry-container

    https://dry-rb.org/gems/dry-container

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 37/64

    dry-container

    IoC Container

    Stubs!

    Thread safe

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 38/64

    # app/containers/global_container.rb

    module GlobalContainer

    extend Dry::Container::Mixin

    namespace('services') do

    register('create_document') do

    Document::CreateDocument.new(

    rename_document: self['services.rename_document']

    )

    end

    register('rename_document') do

    Document::RenameDocument.new

    end

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 39/64

    # создание документа от заёмщика command = GlobalContainer['services.create_document']

    command.rename_document # Document::RenameDocument

    command.class # Document::CreateDocument

    # создания документа из CRM command = GlobalContainer['crm.services.create_document']

    command.rename_document # Crm::Document::RenameDocument

    command.class # Document::CreateDocument

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 40/64

    Profit?

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 41/64

    2046

    # app/lib/containers/global_container.rb

    1 module GlobalContainer

    2 extend Dry::Container::Mixin

    namespace('services') do

    # …

    2044 end

    2045 end

    end

    2046 LOC???

    IOC головного мозга

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 42/64

    # app/lib/containers/api_v2_container.rb

    1 module ApiV2Container

    2 extend Dry::Container::Mixin

    namespace('auth') do

    # ...

    124 end

    125 end

    126 end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 43/64

    # app/services/document/create_document.rb

    class Document::CreateDocument

    def call(document, params)

    result = schema(document).(params)

    if result.success?

    # …

    else

    return false

    end

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 44/64

    return false

    # app/services/document/create_document.rb

    class Document::CreateDocument

    def call(document, params)

    result = schema(document).(params)

    if result.success?

    # …

    else

    end

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 45/64

    Silver Bullet?

    dry-monadshttps://dry-rb.org/gems/dry-monads

    https://dry-rb.org/gems/dry-monads

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 46/64

    dry-monads

    Монада – это то, что все знают, но не могут объяснить;

    монада – контейнер, в котором есть результат с объектом;

    chaining;

    синтаксический сахар.

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 47/64

    class CreateDocument

    def call(document, params)

    result = schema(document).(params)

    if result.success?

    # …

    Success(document)

    else

    Failure(result.errors)

    end

    end

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 48/64

    result = command.call(document, params[:document])

    case result

    when Dry::Monads::Success

    # …

    when Dry::Monads.Failure(LendingOne::ValidationError)

    # …

    when Dry::Monads::Failure

    # …

    end

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 49/64

    Pros

    Тестирование функциональных объектов;

    простое расширение логики;

    композиция – основа моделирования процессов;

    масштабирование и слабая связанность компонентовприложения.

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 50/64

    Cons

    Имитация функций объектами;

    концептуально множатся сущности (которые не являютсясущностями по факту);

    IoC с доступом по строкам;

    можно написать монадический метод и вернуть немонадическое значение;

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 51/64

    Проблема №2.

    Nobody wants to think hack

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 52/64

    ActiveSupport

    number_to_currency

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 53/64

    number_to_currency

    report = MemoryProfiler.report do

    # ActiveSupport::NumberHelper::NumberToCurrencyConverter

    number_to_currency(BigDecimal(1543679.753))

    end

    report.total_allocated_memsize

    # 10168

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 54/64

    10 килобайт?

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 55/64

    FasterSupporthttps://github.com/jetrockets/faster_support

    https://github.com/jetrockets/faster_support

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 56/64

    FasterSupport

    report = MemoryProfiler.report do

    d = BigDecimal(1543679.753)

    FasterSupport::Numbers.number_to_currency_n_u(d)

    end

    report.total_allocated_memsize

    # 80

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 57/64

    Performance

    Checking for BigDecimal

    Warming up --------------------------------------

    ActiveSupport 874.000 i/100ms

    FasterSupport 30.613k i/100ms

    Calculating -------------------------------------

    ActiveSupport 9.263k (± 5.2%) i/s - 46.322k in 5.015584s

    FasterSupport 386.647k (± 2.9%) i/s - 1.959M in 5.071773s

    Comparison:

    FasterSupport: 386647.3 i/s

    ActiveSupport: 9262.8 i/s - 41.74x slower

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 58/64

    Counter Cache

    d = Document.first

    d.comments_count # => 0

    Document.increment_counter(:comments_count, 1)

    d.comments_count # => 0

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 59/64

    Counter Cache

    d = Document.first

    d.comments_count # => 0

    d.increment(:comments_count)

    d.reload

    d.comments_count # => 0

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 60/64

    PostgreSQL

    RETURNINGstatement

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 61/64

    RETURNING

    RETURNING statement

    UPDATE "documents"

    SET

    "comments_count" = COALESCE("comments_count", 0) + 1

    WHERE

    "documents"."id" = 343195

    "id", "comments_count"

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 62/64

    RETURNING statement

    d = Document.first

    d.comments_count # => 0

    d.increment_counter_with_value(:comments_count)

    d.comments_count # => 1

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 63/64

    PostgreSQL RETURNING statement

    activerecord-update_counters_with_values

    https://github.com/jetrockets/activerecord-update_counters_with_values

  • 23/03/2019 Shower Presentation Engine

    localhost:8080/#54 64/64

    Links

    jetrockets.pro

    github.com/jetrockets

    github.com/igor-alexandrov

    https://jetrockets.pro/https://github.com/jetrocketshttps://github.com/igor-alexandrov