Говорим о новом

Data Context Interaction approach

Обзор книги Clean Ruby

Автор: Давыденков Михаил

Предпосылки

  • Парадигма MVC не предусматривает где живёт бизнес логика. Из-за этого бизнес логика имеет тенденцию размазываться по приложению.
  • Модели в классическом варианте нарушают принципы SRP и open/closed.
  • Не всегда ясно как одни методы взаимодействуют с другими, т.к. 'взаимодействие' между методами не first-class citizen в коде.
  • Тесты помогают понять, что происходит в приложении, однако они оторваны от кода и могут не отражать действительного функционирования программы

Цели

  • Инкапсулировать бизнес логику и отделить её от кода фреймворка (framework-agnostic code)
  • Сократить время понимания кода и упростить/ускорить процесс тестирования
  • Быстро понимать какие роли(actors) вовлечены в тот или иной контекст

Основные положения Data Context Interaction подхода

  • Разделять стейт и поведение.
      Объект может заниматься ИЛИ хранением стейта (в рельсах это связано с валидациями, выборками из базы)
      ИЛИ управлять поведением (реализация алгоритмов и бизнес логики)
  • Введение в приложение объектов типа объект-контекст
  • Представление бизнес-логики в виде сценариев
  • Использование Convention over Configuration принципов для организации бизнес-логики в коде

Назначение контекст-объектов

  • Контекстные валидации
  • Назначение ролей экторам
  • Хранение алгоритмов, которые вызываются различными триггерами
  • Взаимодействие с другими контекстами (методами экторов)

Структура контекст-объекта


          Class MoneyTransfering
            # Блок инициализации c назначением ролей, 
            # Создание карты ролей(хэш ролей), 
            # Аксессоры, делегирование, валидации
            # Блок триггеров
            # Приватные методы
            # Модули/классы экторов
          end
          

Продвинутое делегирование и гем casting

Гем использует возможность взять метод из модуля и забиндить его к любому объекту. Преимущество над object.extend(Module) в том, что бинд можно отменить с помощью unbind


            module Foo
              #...
            end

            method = Foo.instance_method(:bar)
            p method.bind(Object.new).call
          

Разруливание ролей через гем surrounded

Гем использует возможность взять метод из модуля и забиндить его к любому объекту. Преимущество над object.extend(Module) в том, что бинд можно отменить с помощью unbind


          module Surrounded
            def method_missing(method_name, *args, &block)
              if @context && @context.roles_include?(method_name)
                @context.role(method_name)
              else
                super
              end
            end
          end

          class User
            include Surrounded
          end
          

Немного поработав с этими гемами я пришёл к выводу, что текущая реализация DCI не для продакшна, да и сам подход не без изъянов. Слишком большой оверхед и пляски с ролями. Есть потери производительности. В руби достаточно сложно наметапрограммировать так, чтобы подход было удобно использовать не потеряв при этом производительности в рантайме. Стоит посмотреть для расширения кругозора, но не более.

Пример реализации контекстов в rails приложении

Правда, не пользуйтесь этим в продакшне