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

Новые задачи, темп жизни и большое количество информации побудили компании автоматизировать рабочие процессы, а значит, активно внедрять информационные технологии. В результате компания обрастает различными системами: от лендингов до CRM-систем и мобильных решений. При этом в каждом конкретном случае даже аналогичный функционал разработчику приходится писать заново. Почему нельзя сначала создать «самодостаточный контрол» — элемент интерфейса, отвечающий за определенные действия, способный самостоятельно обращаться к нужным базам данных и работающий абстрагировано от внешней среды?

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

При модификации «самодостаточного контрола» изменения произойдут во всех системах, где он используется. Вдобавок: это работает налету и никогда не падает.

Звучит как фантастика? В этой статье разберемся, возможно ли создание таких контролов и рентабельно ли оно.

Этот вопрос встал ребром, когда ИТ-инфраструктура компании разрослась до четырех самостоятельных сервисов, и в каждом из них на всех платформах как раз нужно было реализовать один и тот же процесс регистрации, который определен брендбуком и обращается к одним и тем же REST-точкам.

Клиент — ритейлер электроники, у которого уже функционируют CRM-, HR-, ERP-системы, интернет-магазин, В2С-приложение, а также возникла необходимость в решении для консультантов.

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

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

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

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

— необходимо использовать кроссплатформенное программное обеспечение;

— выбранная платформа должна быть широкоиспользуемой;

— она способна реализовывать сложные архитектурные решения;

— платформа развивающаяся и перспективная: мы же не хотим потратить много денег и создать кроссплатформенные контролы без возможности дальнейшей поддержки и использования.

На данный момент есть лишь несколько платформ, позволяющих повторно использовать код и в Web-, и в мобильных приложениях. Проанализировав их, я остановил выбор на платформе .NET как самой подходящей для этих целей. Максимум, что придется сделать — создать верстку для Web- и Forms-приложений. Microsoft постоянно поддерживают эту открытую среду разработки, сообщество программистов и выпускает профессиональную литературу. По поводу того, можно ли реализовать сложную архитектуру, и сомневаться не стоит: по моему мнению, Microsoft шагнула дальше остальных в плане разворачивания модульной структуры.

Специфика архитектуры

В чем конкретно заключается нестандартность задачи с точки зрения архитектуры? Во-первых, каждый контрол может выступать как отдельный проект, т. е. он сам должен обращаться к нужным методам в слое бизнес-логики, который, в свою очередь, делает запрос к слою данных, и в зависимости от них происходят определенные события.

Возникает два вопроса:

— взаимодействие элементов интерфейса друг с другом;

— их иерархия, имеется в виду ситуация, когда контрол входит в состав другого контрола и так далее — как матрешка.

Во-вторых, один и тот же элемент интерфейса в разных проектах может играть разную роль. Например, авторизация продавца или авторизация клиента: одна и та же логика, один и тот же вид, но обращения к разным сущностям — контролы должны быть настраиваемыми. Куда выносить эти настройки?

Учитывая вышеописанные запросы, я пришел к структуре, представленной на рис. 2.

На верхнем уровне находятся все приложения. Для создания нового необходимо будет сделать следующее:

— Настроить сетку (grid).

— Добавить и настроить необходимый контрол.

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

Ниже находятся сами контролы, взаимодействие которых осуществляется в «роутинге», отвечающем за передачу состояний друг другу, подстановку необходимых моделей и пр. Контролы помимо связи друг с другом взаимодействуют с бизнес-логикой. На этом слое информация из слоя данных преобразуется в модель, подходящую для отображения.

Дальше стандартно: слой данных, который в зависимости от ситуации агрегирует информацию из локального кэша или удаленного REST-сервиса. Важной составляющей всей архитектуры является Core-слой, к которому есть доступ из любого слоя архитектуры. В нём располагаются IoC-контейнер и интерфейсы. Именно благодаря инверсии зависимости удалось достичь независимости контролов.

Каждый контрол реализует интерфейс, через который пробрасываются исключительно Action’ы, что позволяет подписаться на них неограниченному количеству других контролов. Прообраз элемента интерфейса становится контролом, только реализуя универсальный метод собственной настройки.

Примеры

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

Обратим внимание на две особенности:

— ошибки контрол обрабатывает самостоятельно, и приложение на них не реагирует;

— подписка на Action осуществляется единожды, далее можно не волноваться о текущем состоянии авторизации.

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

Усложним пример — добавим обслуживание клиента. Для наглядности возьмем тот же самый контрол «Авторизация», но уже настроим его на авторизацию клиента и расположим в сетке другие контролы:

— контрол «Клиентская панель». Он подписан на AuthorizationSucceed, но уже контрола авторизации клиента. При успешной авторизации в контроле отобразятся бонусы программ лояльности клиента;

— контрол «Корзина». Он подписан на контрол «Каталог товаров». Мы будем обновлять «Корзину», учитывая бонусы авторизованного пользователя и выбранное в каталоге.

Благодаря подписке на различные Action’ы контролов не возникнет проблем с неавторизованным клиентом — просто при подсчете его корзины не будут учитываться бонусы. Эту цепочку и дальше можно продолжать другими «самодостаточными контролами».

Теперь обратим внимание на родственные связи между контролами — рассмотрим «Каталог товаров» как архитектуру. При этом он будет являться родительским контролом (РК), а все входящие в него (например, «Категория товара», «Карточка товара» и «Отзывы») — дочерними.

РК содержит навигационную цепочку — путь по системе от «Категории товара» до активного в данный момент контрола. При этом РК зависит от дочерних контролов и должен быть подписан на их обновления (т. к. каталог не может существовать без категорий), иначе навигационная цепочка будет неверна.

Это допущение обходится посредством реализации интерфейса, в сигнатуре которого указывается обязательная подписка на дочерние контролы. Таким образом, можно выстраивать матрешки из контролов, где более «самодостаточные» будут входить в состав менее «самодостаточных».

Цена вопроса

Разобравшись в сути и архитектуре такого подхода, вернемся к вопросу его рентабельности. Безусловно, он требует больше времени на построение общей архитектуры и учет всех нюансов первого проекта, но существенно упрощает разработку последующих систем.

А именно:

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

— На разработку второго и последующих проектов тратится значительно меньше времени: в них используются уже написанные и протестированные элементы интерфейса.

— Поддержка всех систем происходит одновременно и не требует привлечения высокоспециализированных разработчиков.

— A/B-тестирование* проводится на качественно более высоком уровне.

Резюмируя вышесказанное: создание нового продукта не потребует особых затрат; подход позволяет собрать его «по кусочкам» из контролов, как конструктор Lego.

Я упомянул те бонусы, которые мы получаем при использовании этого подхода. Возможно, на вашем проекте будут другие плюсы или, может, косяки. Однако он не должен пройти незаметно и, скорее всего, с текущими веяниями в ИТ будет применяться во многих компаниях и даже станет конкурентным преимуществом некоторых.

* A/B-тестирование (англ. A/B testing, Split testing) — метод маркетингового исследования, суть которого заключается в том, что контрольная группа элементов сравнивается с набором тестовых групп, в которых один или несколько показателей были изменены, чтобы выяснить, какие из изменений улучшают целевой показатель.

Автор статьи — разработчик Mobile Dimension.