Кейс: разметка приложение с нуля на конкретном примере

Привет! На связи Сергей Матросов и команда X5 Tech, ответственная за аналитику в «Пятёрочке». Хотим поделиться с вами тем, как мы внедрили трекер AppMetrica от Яндекса и сделали с помощью него разметку для приложения «Пятёрочки». Почему мы остановились именно на этом фреймворке, какую мы выбрали архитектуру разметки, как писали правила и словари, а также разберём процесс разметки на живом примере разметки экрана приложения. Очень надеемся, что эта статья поможет вам сэкономить много времени, если перед вами будет стоять аналогичная задача.

Выбор AppMetrica как целевого решения

Прежде, чем мы перейдём к процессу разметки, расскажем, почему мы выбрали именно AppMetrica.

Сейчас на рынке есть достаточное количество трекеров, среди которых мы выделили следующие:

  • GA4 –, но из-за угрозы санкций внедрить его и одним днём потерять — это очень больно.

  • Matomo — можно развернуть внутри компании на своих серверах, но, к сожалению, из-за MySQL он не потянул бы нагрузки, при этом слабо развит для наших нужд SDK. К тому же, у него неудобный формат разметки.

  • UXRocket — есть ограничения по количеству параметров для событий. 30 параметров — это немало, но не так уж и много, риск упереться в них есть. Также нас не устроил формат событий. При этом цена в год сопоставима с ценой на AppMetrica.

Ещё для нас было важно, чтобы AppMetrica закрывала следующие функции:

  • Поддержка отправки событий, которые пользователь совершил офлайн: речь тут не о покупке непосредственно в магазине, а ситуации, когда при использовании приложения у пользователя пропадает интернет. Возможность сохранять продолжительное время события, которые юзер делает без интернета, важно для консистентности данных о взаимодействии с приложением.

  • Обогащение Яндекс.Директ событиями: логика тут такая же, как и с Google Ads — свои родные события Яндекс любит больше, чем из прочих трекеров по мобильной атрибуции.

  • Стоит отметить и выделенный канал техподдержки и сопровождения по согласованию, которые мы получили от Яндекса на этапе внедрения и развёртывания AppMetrica: все наши вопросы были оперативно решены.

Конечно, стоит также сказать, что мы рассматривали вариант написать свой трекер. Однако, хоть это и звучит круто, но это дорого, долго, с вероятностью постоянных проблем по трекингу и с сомнительной рентабельностью. Ключевое тут всё же — долго. Результаты нам нужны были быстро.

Что такое разметка

Теперь давайте непосредственно перейдём к разметке.

Итак, разметка приложения — это описание возможных действий пользователя в приложении/веб-сайте, которые те могут отслеживать с последующей отправкой этих данных в трекер, который группирует эти события по-юзерно (иногда и нет), по-сессионно. 

Основная цель разметки — отслеживать, как взаимодействуют пользователи с приложением для последующего анализа этих взаимодействий с выводом метрик, поиска узких мест, возможности тестировать изменения на продукте и пр.

Если вы делаете разметку с нуля, тогда на ваши плечи ложится выбор алфавита разметки, фреймворка разметки (то есть способа размечать), составление правил и словарей для событий, их параметров и пр. Об этом и будем говорить, но сначала отметим вот что: на наш взгляд, хороша та разметка, которая в рамках сырых данных, логов — то есть на уровне строк — даёт вам понимание, что именно за событие произошло, где, на что было совершено воздействие и т. д.

Алфавит разметки

Кажется очевидным, что это латиница, но та же AppMetrica переваривает и кириллицу, а значит выбор как будто есть. Основное обоснование латиницы — это не только минимизация проблем с обработкой полей в таком формате, но и возможность перенести разметку на другой трекер, который, исключая AppMetrica, с большой долей вероятности будет иностранной разработки, а значит будет рассчитан именно на латиницу. 

Итак, определились — латиница.

Фреймворк разметки

  1. Часто встречаемый способ

    На практике это способ разметить данные по модели «Объект_Действие» или «объектДействие»

    По-английски: Object_Action.

    Сразу пример в двух стилях:

    • button_click

    • buttonClick

    Определимся с понятиями:

    • Действие (Action) — то, что совершается пользователем. Например, «клик», «просмотр», «скролл». Именно это — базовое содержание события, которое отправляется в систему трекинга.

    • Объект (Object) — каждая сущность в приложении/на веб-сайте — это объект. У объекта есть конкретные параметры. Объектов может быть много, и чтобы ориентироваться среди них, нам надо их группировать по определённому типу, то есть вводить классы. Например, объект «кнопка». «Кнопка» — это класс.

    Скажем, у нас есть кнопка, мы можем её нажать. Итого:

    Всё вместе «кнопка_нажатие»

    А так как мы оперируем латиницей, тогда и получается, что:

Ранее мы сказали, что основа события — это Действие. Потому что без действия не было бы факта события как такового. Недаром говорят об action-based аналитике. Поэтому сначала Действие, а потом Объект. Таким образом, схема «Объект_Действие» меняется на «Действие_Объект».

Более того, Действие и будет event«ом, то есть событием как таковым, а Объект — первым из его параметров.

Параметры

Cобытие (Действие)

Объект

click

button

Может возникнуть вопрос: почему бы сразу всё в событие не записать, например, clickButton? Уверен, найдутся те, кому так привычнее. То есть было бы так:

Параметры

Cобытие (Действие)

Объект

clickButton

Здесь надо подсветить вопрос об ограничении уникальности имён на события для… GA4 (для бесплатной версии на текущий момент это 500 уникальный имён на поток (из приложения), для платной — 2000). Дело в том, что хорошим тоном является учёт переезда разметки на другой трекер, а для GA4, например, на текущий момент есть ограничение на количество уникальных имён событий. 

У нас есть мнение, что основная причина в том, что GA4 из коробки бесплатный, а данные надо где-то хранить, и именно уникальность создаёт проблему в объёме хранимых данных. В теории его сложно достичь, но нельзя недооценивать мощь усердия людей в преодолении пределов! А потому clickButton, clickBanner уже заняли бы два уникальных места, а просто click с параметром object (который принимает значение button или banner) — одно. Другое дело, что и уникальность имён параметров не резиновое (об этом далее).

Впрочем, если вы решили остановиться на AppMetrica, то ограничений ни по названиям, ни по параметрам у неё нет (точнее, их достичь практически невозможно, даже если будете стараться). Более того, она поддерживает и вложенность параметров до 10 штук. 

Итого, самый распространённый тип разметки — это Действие_Объет (например, нажатие кнопки click_button). Но в нашем случае этого было недостаточно, поэтому мы пошли дальше.

  1. Расширенный фреймворк

    Мы решили обогатить предыдущий фреймворк ещё двумя важными обязательными параметрами — Субъект и Пространство.

    «Субъект_Пространство_Действие_Объект»

    или по-английски:

    «Subject_Space_Action_Object»

    Начнём с новых понятий:

    • Субъект — источник действия. Это может быть, скажем, пользователь или система, то есть user, system. Например, пользователь кликает, система возвращает ошибку. Если вы часто работаете с UML-схемами, то можете мыслить вместо Субъекта — Актора. Мы находим, что это синонимы, но нам ближе понятие Субъект, так как оно как-то более персонализировано что ли.

    Пример: пользователь кликнул где-то на кнопку — user_пространство_click_button

    • Пространство — дело в том, что у нас различные части приложения поддерживаются различными командами разработки, поэтому нам нужно ещё одно уточнение. Отсюда пространство — это команда, отвечающая за конкретный участок приложения, который размечается. Мы находим полезным знать, чьи события мы анализируем, к кому идти, если что-то будет не так.

    Дополняя наш пример: »user_delivery_click_button» — то есть юзер нажал на кнопку в рамках той части приложения, за которую отвечает команда Delivery.

    Тут хочется сказать, что будет очень здорово, если названия ваших команд содержательны и несут смысл того, чем они занимаются. 

    Параметры

    Cобытие (Действие)

    Объект

    Субъект

    Пространство

    click

    button

    user

    delivery

    А пример действия системы? Легко: например, показ экрана. Приложение/веб-сайт вам показывает экран после его открытия вами: system_cart_view_screen

    Параметры

    Cобытие (Действие)

    Объект

    Субъект

    Пространство

    view

    screen

    system

    delivery

    Как и в случае с Объектом, Субъект и Пространство будут параметрами события. Подчеркнём: обязательными параметрами. Всё вместе — это тот необходимый минимум для трекинга, который отвечает на четыре базовых вопроса:

    1. Что произошло?

    2. На что было воздействие?

    3. Где (в ведомстве какой команды) произошло?

    4. Кто это сделал?

    Ещё одно дополнение

    В процессе мы выделили ещё два обязательных параметра — Section и Screen. Section обозначает раздел, который содержит в себе некоторое количество экранов — Screen-s. Основная задача этих параметров проста: вместе они позволяют лучше понять, где именно в приложении было событие. Это как с адресом: регион и город (и да, иногда они совпадают, как Санкт-Петербург, Санкт-Петербург). Но при этом важно понимать: Section можно и не внедрять, но Screen обязателен в любом случае, иначе будет потеряна локация события.

    На всякий случай подчеркнём разницу: Space — это команда разработки, Section — раздел, а Screen — один из экранов раздела.

Правила разметки

Тут всё просто: правила нужны для того, чтобы был единый стиль разметки, чтобы в неё не попадали персональные данные. Вот несколько примеров этих правил:

  1. Не передавать персональные данные в события и его параметры.

  2. Во всех наименованиях используется только нижний регистр. Слова разделяются нижним подчёркиванием (snake_case).

  3. Используемый язык символов — латиница, при этом названия событий, параметров и значений пишутся на английском языке.

  4. Для названий событий и параметров используйте готовые (согласованные) внутри команды аналитиков словари.

  5. Если есть сомнения в том, что использовать — ещё раз загляните в словарь. Если нет ничего подходящего, обсудите и только после этого введите по необходимости новое название для события (действия)

    и т. д.

Здесь сложно выделить что-то универсальное, кроме п.1. Правила вам нужно будет написать самим до разметки и посмотреть их внутри вашей команды аналитиков, поэтому поставим точку в этой теме.

Словари разметки

Очень важно одни и те же действия называть одним и тем же именем. А так как вы вряд ли будете размечать в одиночку (если так, то соболезнуем), а командой, то вы должны на что-то оглядываться при названии событий и параметров. Иначе у одного будет click, у другого — tap. Это не только множит зря сущности, но и путает. Унификация решает! Начнём с событий:

Имена событий

Индекс

Название события

Описание

1

view

показ (страницы, виджета, пр.)

2

tap

касание экрана телефона пальцем

3

double_tap

двойное касание по экрану телефону (в короткий промежуток времени)

4

stretch

на приложении: будто раздвигаешь пальцами что-то

5

pitch

на приложении: будто защипываешь пальцами что-то

6

click

клик

7

sign_up

регистрация

8

refresh

обновление страницы

9

update

обновление объекта (например, кнопки, виджета, баннера)

10

login

вход по логину-паролю

11

logout

разлогин

12

success

всплывающее окно-уведомление «Success» или аналогичное ему по смыслу 

13

confirm

подтверждение (например, при нажатии «ок» на плашке cookie про их сбор)

14

reject

отказ (например, при нажатии «cancel» на плашке cookie про их сбор)

15

enter

ввод (например, данных ФИО, в навигационном поиске)

16

send

отправка (сообщений, обращений по форме на сайте)

17

choose

выбор из вариантов

18

activate

активация (например, при переходе на сайт при подтверждении почты)

19

copy

копирование, нажатие на кнопку копирования

20

add_to_cart

добавление в корзину

21

remove_from_cart

удаление из корзины

22

view_cart

просмотр корзины

23

purchase

покупка

24

filter_goods

фильтрация товаров

25

subscribe

подписка (на новости, на блог)

26

unsubscribe

отмена подписки

27

hide

сокрытие (клавиатуры)

28

open

открытие

29

swipe

свайп списка/блока

30

swipe_full

свайп списка/блока до конца

31

remove

удаление (например, карты оплаты)

32

add

добавление (например, карты оплаты)

Плюс словаря ещё и в том, что индекс позволит вам сразу определить количество уникальных имён, держа в уме информацию об ограничениях. А также он поможет преобразовать словарь в структуру, отфильтровать по паттерну в сырых данных то, что от этих имён отклоняется в написании, найдя ошибку в отправке данных.

Имена параметров

Ограничения на количество уникальных имён на примере GA4 есть и у параметров (и не только: количество символов в названии, количество символов в значении (value). Спрашивается: и зачем тогда переезжать на GA4? Но учитывать эту возможность, повторимся, надо)… В отличии от имён, с ними всё гораздо хуже: для бесплатной версии всего 25 штук. Звучит вроде бы как будто много, но, на самом деле, их может не хватить: объясняется это, прежде всего, не столько нашим усердием, сколько желанием понимать сходу при чтении сырых данных, с каким событием мы имеем дело. А потому — чем больше мы обогащаем событие параметрами, тем лучше (как правило).

Итак, по нашему фреймворку у нас уже три обязательных параметра: object, subject, space

Давайте рассмотрим параметр object и то, какие значения он может принимать навскидку:

  • button

  • screen

  • banner

  • widget

  • warning

  • пр.

При этом как мы, например, отличим одну кнопку от другой? Ответ: по названию этой кнопки. Первое, что мы могли бы сделать, это записать название через »_» в параметре object, например: object: button_T. Но это нарушает саму логику использования параметров, потому что расширяет их содержание. Тогда логичнее вынести это содержание в другой параметр, скажем, name.

Итого у нас появляется следующая развилка:

  1. либо object_name;

  2. либо класс_name: button_name, screen_name и т. д.;

  3. либо даже просто name.

Просто name (п. 3) можно использовать, но нет однозначного референса к тому, чьё это name. Хотелось бы в самом названии параметра однозначно указывать принадлежность к определённому классу объектов, тем самым в рамках отсмотра параметров в сырых данных можно оставаться более сфокусированным, понимая, о каком объекте идёт речь.

Чтобы не упереться в лимиты, которых, как мы уже говорили, в AppMetrica нет, но которые есть в других трекерах, логичнее пойти через object_name (п. 1). И, в целом, это верное решение, но тут несколько теряется читаемость события в сырых данных. Поэтому мы решили пойти через логику «класс»_name, понимая, что тем самым мы увеличиваем количество параметров. То есть, гипотетически, при переезде нам надо будет это свернуть в object_name. Как поступить вам — решать вам же в рамках команды. 

Значения параметров

Параметры могут принимать сколько угодно много значений без ограничений (вот тут как раз нет проблем в лимитах), но есть проблема понимания, например, что было объектом действия: один посмотрит и скажет, что button, другой — icon/widget и т. д. Поэтому надо иметь словарь значений с определениями как минимум (да и как максимум, на самом деле) для Объектов и Субъектов, иначе у одного аналитика свои кнопки на экранах будут размечены как button (object: button), а у другого — icon (object: icon).

Словарь объектов

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

Описание

комментарий

bottom_sheet

«выезжающий» снизу компонент экрана

перекрывает часть контента, обычно занимает половину или треть экрана по высоте, закрывается свайпом вниз или нажатием за его пределами

alert

aлерты 

всплывающие уведомления, которые относятся к бизнес-процессу: например, статус доставки

banner

баннер

инфо с рекламой

widget

виджет

обобщённый элемент, который может менять свой визуал (изменяемый объект)

story

история из stories блока

map

карта

актуально только для адреса доставки

keyboard

клавиатура юзера

button

кнопка

nav_button

навигационное меню

оно у нас постоянное и неизменное

search_bar

поиск (tabbar)

поиск

product_search

поиск продуктов (tabbar)

поиск продуктов

warning

предупреждение

например, предупреждение на стадии оплаты в доставке: чтобы списывать баллы, нужно добавить карту лояльности

hypertext

текст, содержащий URL для перехода

на экране выделяется другим цветом

text_field

текстовое поле

screen

экран

Важно: не стоит бросаться в крайности и усердствовать с классами. У нас, например, в разделе Доставки есть поиск товаров. Да, мы могли бы нажатие (tap) на поиск обозначить как:

Параметры

event

object

text_field_name

tap

text_field

product_search

Но поиск по товарам у нас, во-первых, в приложении представлен в единственном числе, а, во-вторых, является важной продуктовой фичей, поэтому можно сразу обозначить как:

Параметры

event

object

tap

product_search

Также у нас в словаре есть ещё и search_bar — поисковая строка в рамках экрана FAQ. Почему в одном случае мы написали search_bar, а в другом — product_search? Для того, чтобы выделить отдельно поиск по товарам и поиск по FAQ, исключив связь на уровне означающих «search».

Словарь субъектов

Вы можете справедливо заметить, что system можно уточнить до конкретного микросервиса. Да, верно, но такое дробление на названия сложно поддерживать, хотя в потенциале это и помогло бы определить, в какой сервис идти, чтобы разбираться с проблемными событиями. Мы решили обобщить всё до system. Условно, если мы видим, что system нет там, где она должна быть, то начинаем раскапывать.

Подведём промежуточные итоги

У нас есть:

Он даёт нам таблицу/схему событий вида:

Параметры

Cобытие (Действие)

Объект

Субъект

Пространство

Секция

Экран

view

screen

system

delivery

my_orders

order

  • Правила и словари событий, параметров, объектов, субъектов, пространств (также отметим, что полезным будет в документации привести примеры заполнения).

    А чтобы вам было проще, вот вам наш готовый свод правил разметки событий.

Пришло время размечать!

Разметка (Процесс)

Прежде чем брать самый первый экран в работу, мы рекомендуем сделать несколько важных предварительных шагов.

  1. Узнать ФИО тех, кто отвечает за ту или иную часть приложения/веб-сайта. Как правило, речь идёт о менеджере продукта, продакте, Product Owner. 

Рекомендуем оформить это в виде таблицы в отдельном разделе про разметку в инструменте, в котором вы ведёте документацию:

Команда

Space

Экраны

PO 

(ФИО)

Контакты

(email, tg)

Комментарии

Доставка

Delivery

Catalog, Cart, etc.

Профессионал Топовый

ПД

Что-то важное

  1. Распределить экраны приложения внутри команды аналитиков:

Команда

Space

Экраны

PO 

(ФИО)

Контакты

(email, tg)

Комментарии

Аналитик (ФИО)

Доставка

Delivery

Catalog, Cart, etc.

Профессионал Топовый

ПД

Что-то важное

Герой Масков

После этого мы, наконец, приступаем к разметке:

  1. Размечаем-размечаем

  2. Ревью разметки со стороны менеджера (каждый аналитик идёт к тому Product Owner (далее — PO), чьи экраны он размечал)

  3. Правки + новое ревью

  4. ТЗ на внедрение

  5. Приём проделанной работы

Profit!

Что конкретно делают аналитики

Остановимся на самом интересном — на пункте 3 — «Размечаем-размечаем» (как мы, аналитики, это делаем). Пока open-source инструмент разметки от СберЗдоровья не в релизе, о котором ребята рассказали на конференции Aha 2023, мы используем старые-добрые Google Spreadsheets. Вот наш шаблон.

В нём три вкладки:

  1. «Экраны/Виджеты» — это просто описание всех экранов и виджетов этого спредшита (считай, локальный словарь объектов экрана).

  2. «Экран» — это экран приложения, который подвергается разметке. Вместо экрана для веба можно написать «Страница». Обычно мы меняем название вкладки с «Экран» на «Э. {{Название экрана}}».

  3. «Экран ТочкиВхода» — это названия баннеров/виджетов в рамках приложения, через которые можно попасть в этот экран (если есть).

Основная вкладка — это вкладка № 2, «Экран». Разберёмся в нём подробнее.

У нас есть в хеддере два поля с URL-ами: одна на Figma, другая на Confluence. В Figma — актуальные макеты приложения, Confluence — страница с уже внедрённой разметкой на базе этого спредшита.

Поля «Дата добавления…» и «Статус в Android/iOS»  говорят сами за себя.

Далее идут «Приоритет внедрения», «Индекс», «Субъект (Subject)», «Пространство (Space)». Все, кроме Индекса, должны быть уже понятны. 

C Индексом всё просто: вместо того, чтобы ссылаться на какие-то события в спредшите на строки (так как строки могут меняться), лучше ссылаться на индекс.

5e8d43270e629759b58acf35c227ab6f.png

Следующие три поля — это «Сценарий», «Описание события» и «Событие». Событие — это уже наше известное Действие. Описание события — это формулировка того, что сделалось и с каким объектом. А сценарий — это что происходит с пользователем, с системой, что даёт событие как результат.

d574229aa6772a265342fd917afb9b45.png

Как вы помните, у нас есть обязательные параметры для заполнения: это Object (string), Subject (string), Space (string). Но к этому списку добавляются ещё два:  

  • Screen_name

  • Section_name 

Так как все они обязательны, мы вынесли их отдельно:

5fac2cdee584755cf26dd4941367d873.png

Space — это команда (example), Section — раздел (settings), а Screen — один из экранов раздела (notification). Итак, в секции «Настройки» есть ряд экранов (screens). Один из screen — это «Уведомления». За секцию и этот экран отвечает команда «Example».

Теперь к нашей непосредственной задаче добавляется ещё одна — разметить какой-то экран. У вас может быть либо постановка задачи от PO на первые события, либо «размечай всё, а там уж приоритезируем». Во втором случае вы открываете экран и… описываете все взаимодействия с ним. Вот и весь секрет.

Давайте немного посмотрим разметку главного экрана приложения «Пятёрочки»:

fa8b4753548bf6ec75651876421b38d6.png

Первое событие, которое можно описать — это показ «Экрана»:

8ba215813f85d10551183ffcf2671431.png

Событие:

Параметры этого события:

  • Что показалось? — Screen (экран).

  • Какая команда отвечает? — Growth (название команды).

  • Кто сделал это действие? — экран показывает system«a (то есть наше приложение показало экран пользователю).

  • В какой части приложения? — в части главной страницы (main).

  • Какой это был экран? — главная страница (main).

b32becd2bc25978f103a32a707f31b2c.png

Доп. параметры (необязательные):

  • referrer — предыдущий экран

  • load_status — статус загрузки — успешно или ошибка (с указанием какой)

  • load_time — время загрузки экрана

    (был еще ряд параметров, но они не имеет значения для понимания).

Второе событие, которое можно описать — это tap (нажатие) на виджет со штрихкодом. 

Это виджет карты лояльности. За него отвечает другая команда — команда Loyalty.

37e2ec23ae3321a3f17401ea0a08af39.pngda19c5bfa21554d259117f27653760e2.png

Событие:

Параметры этого события:

  • На что было нажатие? — на widget

  • Какая команда отвечает? — Loyalty (название команды)

  • Кто сделал это действие? — нажатие делает user

  • В какой части приложения? — в части главной страницы (main)

  • Какой это был экран? — главная страница (main)

И доп. параметры (необязательные):

f90efe2e7e1c0b16f04b1b5936616af8.png

А почему, кстати, Widget? Может, это banner? Может, вообще вид button«a?

Мы внутри команды и с PO Loyalty договорились, что это именно Widget на базе того, что он может менять свой вид в зависимости от наличия карты лояльности.

Таким образом, размечаем все элементы на главной странице и получаем таблицу с событиями (как в шаблоне), которую несём на согласование продакту — PO (п. 4). Затем правим замечания (п. 5), затем опять несём на согласование — и так по кругу, пока не договоримся.

После того, как мы с продактами пришли к единому мнению, из таблицы нужно будет сформировать ТЗ для разработки на внедрение. Нам нужно было из спредшита сделать вот такое ТЗ:

34a850e63ade9cb1fc12c6c402285ffd.pngaa4a6ddc91aabf5f4ce39ec1c4c5af47.png

Естественно, делать руками это было бы слишком грустно, поэтому мы написали скрипт, который из пис-оф-шита (GoogleSpread Sheet) делает вот такую конфетку-ТЗ (см. скрин выше). Отметим, что в рамках ТЗ у нас на базе event_name + параметров собирается в отдельном поле имя события в CamelCase: это уже уникальный индекс для разработчиков, который помогает им ориентироваться в событиях, которые отправляет приложение/веб-сайт.

Послесловие

На этом наш экскурс в процесс разметки приложения закончен. Мы поговорили о том, почему мы остановились на AppMetrica, какую мы выбрали архитектуру разметки и разобрали процесс разметки на живом примере экрана приложения. 

Надеемся, наш рассказ и материалы вам пригодятся, когда перед вами встанет вопрос о разметке вашего приложения. Данная методика не претендует на единственно верное решение, но у нас она сработала и поэтому имеет право на жизнь. 

Соавторы и непосредственные участники разметки:  

  • Денис Кучкильдин 

  • Дмитрий Чернышев 

  • Никита Сурков 

  • Ранее мы сказали, что основа события — это Действие. Потому что без действия не было бы факта события как такового. Недаром говорят об action-based аналитике. Поэтому сначала Действие, а потом Объект. Таким образом, схема «Объект_Действие» меняется на «Действие_Объект».

    Более того, Действие и будет event«ом, то есть событием как таковым, а Объект — первым из его параметров.

© Habrahabr.ru