330 000 строк кода • ≥ 20 репозиториев • сложная предметная область • технические решения на границе экосистемы Ruby • рядом аналитический стек на Hadoop/Java/Python 2 / 20
раз в год c XX:XX до YY:YY часов цены внезапно повышаются ▶ события сгруппированы в программы ▶ уведомление людей ▶ уведомление устройств • Тарифный план ▶ энергия: 0 — 100 кВч, 100 — 400 кВч,… ▶ время: 8:00 — 12:00, 12:00 — 16:00,… ▶ мощность: 10 кВ, 20кВ,… ▶ пиковые часы: 12:00 — 14:00 послезавтра ▶ время действия: июнь — август • Развитие стандартов: 2.0 и 1.0 — это две большие разницы 3 / 20
независимый от «большой» страницы поток управления • Плохо ложится на ReST и рельсовый MVC • Пример: создание и редактирование события управления спросом 5 / 20
do |root| root << widget(:event) end # ... end class EventWidget < Apotomo::Widget def new(args = {}); render; end def edit(args = {}); render; end def create(event); end # event — внутреннее для Apotomo событие end # Внутри шаблона = render_widget :event, :new 6 / 20
и забыл» • та же легковесность запуска • то же масштабирование — воркеры можно разнести по разным машинам Против Resque: • один сигнал — одно действие 9 / 20
Contract/Operation • Request — контракт: ▶ первичная валидация, что параметры ок ▶ удобство доступа к объектам предметной области ▶ иммутабельный FormObject или ParameterObject class EventsController::Create::Request attr_reader :start_time def initialize(params) @device_uid = params[:device_uid] @start_time = params[:start_time] end def device @device ||= Device.find_by_uid(@device_uid) end end 14 / 20
Command (CQRS). • В простейшем случае отвечает только на идемпотентный вызов #success? • Есть проблема с пространством имён — некрасиво class EventsController def create operation = Create::Operation.new(Create::Request. new(params)) if operation.success? # ... else # ... end end end 15 / 20
сложившимся лексиконом • Smart Energy Profile 2 • OpenADR 2.0b • Разные языки — разные серверы • Антикоррупционный слой через HTTP API ▶ Хозяин и раб ▶ Адаптер на стороне хозяина: переименование и комбинация ▶ Декоратор на стороне раба: сложно ▶ Срезаем углы: ActiveAdmin как JSON API ▶ Углы могут кусать: id vs uid 16 / 20
FormObjects вместо accepts_nested_attributes_for • Иммутабельность FTW • Щепотка ФП: #flat_map, #inject, #take • Если появляется QueryObject, что-то сдохло в консерватории • Yardoc 18 / 20