
Вторая часть статьи "Contemporary Front-end Architectures", в которой рассмотрены архитектуры фронт-энда с точки зрения распределения потоков данных. Начало здесь
Реализация
Алгоритмы, порожденные DOM (DOM-infused algorithms)
Методика, внедренная и освоенная библиотекой jQuery, действительно стала началом написания крупномасштабных клиентских приложений, хотя на самом деле jQuery не решала архитектурные проблемы. Она была разработана для упрощения манипулирования деревом DOM когда в поведении браузеров находилось слишком много несоответствий. Это обеспечило независимый от браузера API.
Я не думаю, что это было намеренно, но jQuery упростил API DOM до такой степени, что его было трудно отличить от API обычных языков программирования. Это, в свою очередь, позволило разработчикам буквально смешать уровень DOM API (View) с бизнес-логикой (Model).
Еще раз отметим, что мы все еще находимся в контексте того же серверного MVC. Это просто продолжение. Там нет реального инверсии контроля. Общий контроль над представлениями / страницами все еще определяется кодом на стороне сервера.


В приведенном выше фрагменте кода Модель, Представление и Презентатор/Контроллер в некотором роде объединены в одну монолитную структуру кода. Это тот случай, когда Модель состоит только из одного свойства. Представьте себе попытку создания веб-приложения без цикла просмотра сервера (например, SPA). Было бы невозможно справиться со всем этим каким-либо удобным образом. Код для взаимодействия с DOM пронизан остальной логикой приложения, и поэтому он известен как DOM-infused алгоритм (я не уверен, что официально есть такой термин)
Backbone.js — MV*
Как мы видели, в jQuery при разработке приложений путь структурирования и организации нашего кода явно отсутствует. Именно здесь Backbone.js появился в качестве следующего эволюционного решения. Это была одна из первых библиотек, которая принесла преимущества стиля MVC на стороне клиента.

Если мы посмотрим на диаграмму потока данных Backbone, то ясно увидим модель и представление, но нет объекта, эквивалентного контроллеру. Шаблоны развиваются, и MVC на стороне клиента — это просто эволюция предыдущих архитектур MVC. В ходе этой эволюции большая часть сообщества JavaScript согласилась с определением модели и представления, но не было единого мнения о контроллере. Учитывая окружение клиента, идея Controller не очень подходит. Контроллер оставлен открытым для интерпретации.
Что касается Backbone, то в нем контроллер отсутствует. Итак, что это? Это MVC, MVP или MVVM? Заимствуя из определения серверного MVC, контроллер имеет две обязанности, а именно: отвечать на действия пользователя в форме входящих HTTP-запросов и управляйте моделью для создания представления (HTML-страница). В случае Backbone эти обязанности распределяются между View и Router. Но независимое понятие Контролера или Презентера отсутствует.
Некоторые разработчики считают, что Backbone — это MVP, где View это аналог Presenter, Template — это View, а Model и Collection составляют Model.
Как пишет в своем блоге Адди Османи, в конечном итоге лучше не заставлять Backbone соответствовать каким-то конкретным шаблонам дизайна. Шаблоны проектирования следует рассматривать как гибкие руководства по структурированию приложений, и в этом отношении Backbone не подходит ни под MVC, ни под MVP. Вместо этого он заимствует некоторые из лучших концепций из нескольких архитектурных шаблонов и создает гибкую структуру, которая просто хорошо работает.
Вот так рождается MV* или Model-View-Whatever(«что угодно»). Для подробного обсуждения настоятельно рекомендуется ознакомиться с блогом Адди Османи
По сравнению с предшествующим jQuery, Backbone помог создавать более структурированный код.




Ранее в этой статье я назвал Backbone следующим эволюционным решением. Причина в том, что он просто расширил MVC на стороне сервера, дополнив его. Например, если наш сервер является RESTful и подразумевается, что код фронт-энда — это просто средство для представления модели на стороне сервера, то Backbone предварительно настроен для синхронизации с API:

И, кроме того, есть много других небольших соглашений, встроенных в Backbone, которые просто ощущаются как расширение. В заключение скажу, что Backbone, возможно, и не был единственным решением в то время, но это была действительно новаторская работа с точки зрения структуры кода и организации. Как и jQuery, он был использован многими продуктами.
Knockout.js — привязка данных (data binding) для фронт-энда
Knockout.js — это наш последний пример использования базовых шаблонов. Он имеет целью реализацию MVVM — Model-View-ViewModel для JavaScript. И это так. В то время как Backbone занимался решением проблемы организации и структуры кода, Knockout — это эффективная реализация уровня View с помощью декларативных привязок данных (Declarative Data Bindings). Преимущества декларативных привязок такие же, как и у любых конструкций декларативного программирования:
- Легко читается: декларативный код помогает в программировании
- Сокращение стандартного шаблона: привязки позволяют нам автоматически обновлять DOM при каждом изменении ViewModel, а также обновлять ViewModel при каждом изменении View через пользовательский ввод.
- Наблюдаемые (Observable): Knockout обеспечивает более высокий уровень абстракции для событий. Это позволяет Knockout автоматически отслеживать зависимости между ViewModel свойствами. При необходимости мы можем подписаться на Observable свойства.


В то время как Knockout предоставляет четко определенные конструкции для View и ViewModel, в нем ничего не говорится о том, какой должна быть модель приложения. Это делает Knockout остро сфокусированным и универсальным, поскольку его можно использовать в качестве библиотеки вместо фреймворка. По своему опыту я видел, что он использовался для создания мини-приложений SPA, где веб-приложение состоит из нескольких страниц, и каждая страница представляет собой небольшое приложение Knockout. Этот ответ на StackOverflow четко определяет область применения MVVM в Knockout.
Часто предполагается, что с моделью у Knockout находится на стороне сервера. ViewModel просто запрашивает модель на стороне сервера, используя Ajax или его эквивалент.
Knockout заменяет jQuery и шаблонные решения типа Handlebars для обновлений DOM, но по-прежнему использует jQuery для анимации, Ajax и других утилит. В сочетании с Backbone он может служить полной реализацией шаблона MVVM. Теоретически это могло бы произойти, но до того, как это могло произойти, эти концепции уже развивались в инструментах следующего поколения.
Здесь начинается революционное движение по Angular 1, Aurelia, Ember.js и т.д.
Благодаря тесной связи с миром .NET, Knockout широко использовался в приложении ASP.NET MVC. Как и Backbone, это было еще одно эволюционное решение немного иной проблемы. И опять же, предположение о том, что код на стороне клиента является просто расширением для шаблона MVC на стороне сервера, не изменилось. Серверная сторона была все еще доминирующей архитектурой.
И Knockout, и Backbone являются библиотеками JavaScript. Так или иначе, Backbone рассматривался как фреймворк. Почему? Нет определенного ответа, но это, вероятно, было в перспективе. Backbone всегда обрабатывался с абстракцией более высокого уровня из-за его акцента на структуру кода. Более того, Backbone никогда не предназначался для замены вездесущего jQuery (даже в 2019 году 70% топ-10000000 веб-сайтов используют jQuery), тогда как Knockout перекрывался с ядром jQuery, то есть, DOM-манипуляциями, что, естественно, усложняло Knockout. Таким образом, адаптация Knockout была ограничена по сравнению с Backbone. Но это все еще была одна из первых реализаций декларативных привязок данных для интерфейсного сообщества, и она заслуживает отдельного упоминания.
Angular 1 — дай мне контроль
Что jQuery сделал с DOM, Angular 1 сделал с экосистемой фронт-энда в целом. Это навсегда изменило представление о создании масштабных клиентских приложений. В качестве основы он представил множество концепций — модульная система, внедрение зависимостей, инверсия управления, более более простое связывание данных и т.д.
Было тогда и остается сейчас трудной задачей выбор правильных библиотек JavaScript и создание идеального технологического стека для фронтэнда. Angular 1 просто предоставил более простую, но сплоченную альтернативу. То же самое можно сказать и о Ember.js и других подобных системах, но применимость Angular 1 была на ином качественно уровне, чем у его альтернатив того времени.
Angular 1 — это революционное решение в том смысле, что оно явно ознаменовало отход от идеи простого расширения MVC на стороне сервера с клиентским кодом, разбросанным по страницам. Angular 1 сделала SPA первоклассным, практически де-факто решением для создания взаимодействия с пользователем следующего поколения.
Фреймворк или библиотека?
Предыдущие решения были больше библиотеками, чем фреймворком. Angular 1, без сомнения, является четко определенной структурой. Необходимый отличительный фактор между платформой и библиотекой — IOC — Inversion of Control (инверсия управления). Далее для квалификации Angular 1 в качестве фреймворка, можно отметить:
- Четко определенные MVVM: Angular 1 имеет четкие объекты Model, View и ViewModel.
- Внедрение зависимостей (DI): Angular 1 имеет первоклассную поддержку DI, которая обеспечивает управляемый жизненный цикл для объектов Model. В Angular 1 Модель приложения реализована с использованием Сервиса (Service). Сервис по умолчанию является одноэлементной сущностью, что часто желательно для объектов модели.
- Привязка данных (data binding) и обнаружение изменений: привязки данных являются декларативными, а обнаружение изменений происходит автоматически. Данные связывают с необходимым компонентом, чтобы квалифицировать его как MVVM. Привязки были двунаправленными. Дальнейшее обнаружение изменений было почти автоматическим (Angular 1 был действительно волшебным). Это уменьшило много шаблонного кода для разработчиков. Использование событий и общий механизм подписки были сокращены. Это очень распространено в системах на основе MVC. События также снижают читабельность кода, поскольку поток данных для определенного рабочего процесса распространяется по всему коду.
- Модульная система: Angular 1 представляет модульную систему, специфичную для фреймфорка. Модули являются основой организации кода практически для каждого языка. У JavaScript не было модульной системы до 2015 года (браузеры не поддерживали его до 2018 года). Angular значительно опередил свое время с точки зрения организации.
В то же время Angular 1 также подвергся критике за сложность, которую он представил. Наиболее важной критикой является то, что он был смоделирован на основе серверных к��нструкций. Это не характерно для разработчиков фронтэнда. Некоторые вещи были откровенно плохи:
- Коллизия пространства имен: хотя DI был великолепен, но он был реализован с использованием шаблона Service Locator, который использовал глобальное пространство имен. Это сделало почти обязательным префикс услуг.
- Двунаправленные привязки данных. Возможно, в то время это была отличная идея, но с течением времени стало ясно, что она не очень хорошо масштабируется. Появление React сделало двустороннее связывание данных необязательным. Двунаправленные привязки могут создать много спагетти-кода, если не соблюдать осторожность.
- Использование запутанной терминологии. Некоторые из терминологий, используемой Angular 1, явно сбивают с толку. Например, Angular 1 имеет $scope, который действует как ViewModel, но также имеет Controller, который увеличивает объект $scope. Вероятно, правильное имя могло быть VMFactory или эквивалентным. Кроме того, в Angular 1 есть три типа способов создания Сервиса, один из которых называется Сервис.
Было много других мелких проблем. Angular 2 или просто Angular, был полным прорывом в такой степени, что выглядел как совершенно новый фреймворк. Я не нахожу между ними ничего общего, кроме названия и нескольких концептов.

За прошедшие годы были небольшие релизы Angular 1, в которых исправлено множество небольших сложностей его использования. Наиболее значительным из них было добавление Component Model, в которой сошлась большая часть направлений развития фронт-энда.
Angular 1 долго жил и продолжает жить среди фронт-энд сообщества. Со всеми своими плюсами и минусами он помог сообществу понять важность архитектуры программного обеспечения и обеспечил основу для написания масштабируемых приложений. Его минусы и недостатки стали основой решения проблем для будущих архитектур.