Умер ли MVC для фронтенда?

https://medium.freecodecamp.com/is-mvc-dead-for-the-frontend-35b4d1fe39ec
  • Перевод

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

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

За последние 4 года я видел множество веб-проектов и потратил немало времени на разработку архитектуры фронтенда и интегрирование различных фреймворков.

До 2010 года JavaScript (язык программирования, на котором был написан jQuery) использовался, в основном, для манипуляций с DOM и прикручивания дополнительной логики, оживляющей веб-приложения. Разработчиков не особо волновала архитектура, поскольку штуки вроде revealing module pattern вполне справлялись с задачей структурирования кодовой базы.

Дискуссии на тему архитектуры фронтенда в сравнении с бэкендом фактически начались с развитием концепции single page application (в конце 2010 года) и с растущей популярностью фреймворков типа Backbone и Knockout.

Поскольку тогда это было новшеством, разработчики этих фреймворков вынуждены были искать вдохновения на стороне, так что они обратились к уже хорошо устоявшимся практикам, применяемым на серверной стороне. А к тому моменту все популярные серверные фреймворки в том или ином виде реализовывали классический MVC (Model — View — Controller), также известный как MV* из-за различных вариаций.

Когда React.js впервые был представлен как библиотека рендеринга, многие принялись его высмеивать за интуитивно непонятные манипуляции с HTML в JavaScript. Но разработчики упустили из вида важнейший вклад, который принес миру React, — компонентно-ориентированную архитектуру. React не изобрел компонентный подход, но перенес идею на новый уровень.

Этот главный прорыв в архитектуре проглядели даже в Facebook, когда они анонсировали React как «V в MVC».

Примечание: меня до сих пор преследуют кошмары после ревью проектов, в которых одновременно использовались React и Angular 1.x.

2015 год стал поворотным для сознания разработчиков — от привычного MVC мы перешли к однонаправленным архитектурам и потокам данных, унаследованным от Flux и функционального реактивного программирования, с помощью таких инструментов, как Redux или RxJS.

Когда же для MVC все пошло не так?


MVC до сих пор остается, пожалуй, лучшим способом разработки серверной части приложений. Работать с фреймворками вроде Rails или Django — одно удовольствие.

Корень проблемы в том, что принципы и декомпозиции, которые MVC представляет на сервере, не такие же, как на клиенте.

Связь контроллер-представление


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

Когда мы переносим MVC на клиент, начинаются проблемы. Контроллеры напоминают то, что мы называем «code-behind». Контроллер сильно зависит от представления. В большинстве реализаций фреймворков он даже создается представлением (как в случае с, например, ng-controller в Angular).

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

Толстые модели


Вспомните, какие типы данных вы храните в модели на клиентской стороне.

С одной стороны, у вас есть данные типа users и products, представляющие состояние приложения. С другой стороны, вам приходится хранить состояние интерфейса — что-нибудь вроде showTab или selectedValue.

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

Так где в этой модели место компонентам?


Компоненты представляют из себя: представление + обработка событий + состояние интерфейса.

Диаграмма ниже показывает как фактически разделить стандартную MVC модель, чтобы получить компоненты. Выше линии осталось именно то, что пытается решить Flux: управление состоянием приложения и бизнес-логикой.

Одновременно с популярностью React и компонентно-ориентированной архитектуры мы также увидели растущую популярность однонаправленной архитектуры для управления состоянием приложения.

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

Но это больше не история про один React. В Angular 2 можно увидеть точно такие же подходы, хотя и с различными вариантами управления состоянием (например, ngrx/store).

MVC не мог сделать ничего лучше на клиенте. Он с самого начала был обречен на провал. Нам лишь нужно было время, чтобы это увидеть. Через этот пятилетний процесс фронтенд-архитектура эволюционировала в то, что мы видим сегодня. И если подумать, то пять лет не так уж много для того, чтобы выработать лучшие практики.

MVC был необходим в самом начале, потому что наши фронтенд-приложения становились все больше и сложнее, а мы не знали как их структурировать. Я думаю, он справился со своим предназначением и заодно преподал хороший урок о том, как брать хорошую практику из одного контекста (сервера) и применять ее в другом (клиенте).

Что же нам готовит будущее?


Я не думаю, что в ближайшее время мы вернемся к классической MVC архитектуре для клиентских приложений.

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

Будет ли такой тип архитектуры лучшим решением через пять лет? Вполне возможно, что да. Но опять же, нет ничего определенного.

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

От переводчика


Я фулл-стек разработчик в отделе фронтенда Tutu.ru, опыт работы 5 лет. Ранее писала на C#, cейчас пишу на JavaScript/Node.js и PHP.

В Tutu.ru фронтенд проделал немалый путь — от подсветки маршрута электрички при наведении (кто не знает, Электрички — наш самый старый проект) до полноценного SPA (single page application) в новом разделе Автобусы.

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

Это было лучше, чем ничего, но впоследствии мы столкнулись с проблемами, хорошо описанными в статье. В 2014 году мы постепенно начали переходить на однонаправленные архитектуры и теперь MVC у нас живет только на бэкенде (и в легаси-фронтенде, на правах почетного ветерана).
Туту.ру 108,65
Tutu.ru — сервис путешествий №1 в России.
Поделиться публикацией
Комментарии 149
  • +5
    MVC умер, да здравствует MVC!
    • +4
      MVC до сих пор остается, пожалуй, лучшим способом разработки серверной части приложений.


      Не согласен с этим утверждением. Конечно, MVC можно притянуть за уши к чему угодно, но я предпочитаю работать с концепцией middleware pipeline, которая полностью устраняет старые монструозные контроллеры из кода, заменяя их на узкоспециализированные сервисы.
      • +8
        Если в MVC на бэкенде у вас монструозные контроллеры, то у вас неправильный mvc. Что там может быть монструзного? Бизнес-логика в модели, рендеринг во вью. Монструозными они получаются когда в них пихают логику относящуюся не к ним.
        • +2
          А еще некоторые люди сложную бизнес логику выносят в слой Service. В модели остается только бизнес логика связанная непосредственно с данными сущностями.

          Толстые контроллеры, как и толстые модели — это зачастую ошибка проектирования.
          • 0
            Модель — это вся бизнес-логика, «размазанная» по сущностям и сервисам. Модель должна быть как можно более толстой, включать всю бизнес-логику. В идеале спрятанная от контроллера и модели за фасад/адаптер, но этим можно пренебречь на практике.
            • +1
              Классы моделей на 2-3к строк — повод задуматься не слишком ли много на себя берет один класс. И зачастую код вполне себе хорошо делится на несколько классов.

              Излишняя толстота любого из слоев — это плохо. Но бизнес логике место именно в моделях. Это да. Контроллеры должны быть максимально тонкими.
              • +2
                Да ради бога, хоть на 100 классов поделить. Главное, чтобы бизнес-логика оставалась в модели, чтобы не протекала в контролерры и вью, а как делить всю отвественность модели между классами сущностей и сервисов в её составе, дело уже десятое в рамках MVC.
                • 0
                  С т.з. бэкенда View должен быть, по возможности, исключительно и полностью тупым.
                • 0

                  Бизнес логика должна лежать в сервисах, а модели лишь проецируют данные приложения в БД.

                  • 0
                    Не вся. Та, что относится исключительно к данным модели может находится в модели.
                    И да, модели не обязаны быть построены из БД.

                    У меня они могут строиться из конфигурационных файлов и много чего еще.
                    • 0
                      Бизнес-сервисы — часть модели, такая же как сущности. А проецирование на базу, файлы, на учто угодно вообще вне зоны ответственности доменной модели.
                      • 0
                        Раньше был Transaction Script, теперь то о чем вы говорите называется Anemic Model.
                        Да, подход рабочий. Хорош ли он, с этим можно поспорить.
                    • +2
                      Модель это не бизнес логика — не путайте. Модель может делать что-то сама — например изменять собственное состояние. Но зачастую мы хотим совершать какие-то действия над моделью, а еще более зачастую хотим менять состояние одной модели в зависимости от состояния другой и так далее. Вот это уже и есть бизнес логика. То, что делает приложение, как говорится «valuable».
                      • +2
                        По-моему, это вы путаете модель с классами сущностей в этой модели.
                        • 0
                          Немного дополню, в DDD бизнес логика как раз в сущьностях. А модель предметной области — это по сути и есть сам Problem Domain.
                          • 0
                            В сущностях и сервисах, если сложно отнести логику к какой-то отдельной сущности.
                        • 0
                          Тут весь вопрос, что вы имеете ввиду говоря «модель» и в каком контексте.
                          Если мы говорим о DDD, то Модель это Problem Domain.
                          А бизнес логика Модели в Сущьностях (Entity).
                          Ну и конечно не все так просто.
                        • 0
                          Пока, Single Responsibility.
                          • 0
                            Почему пока? Каждый класс в модели (сущность, объект-значение, сервис и т. д.) делает одну вещь и делает её хорошо. :)
                            • 0
                              Если говорить об MVC, то M может быть чем угодно это не DTO.
                        • 0
                          Мне это напоминает, падрон, Agile, «если оно не работает вы его неправильно используете», при этом сотни статей с взаимоисключающими вариантами как его правильно использовать.
                          MVC не серебряная пуля, если какие-то суровые проблемы — можно и посмотреть на другие шаблоны.
                          • +2
                            Не серебряная пуля. И другие подходы есть. Но жирные контроллеры — это явно признак того, что что то не так
                            • 0
                              В имплементации MVC во всех популярных фреймворках, контроллеры — это то самое «что-то не так». Потому что у них нет одной ясной задачи и каждый лепит что умеет.
                              • 0
                                Видимо Вы работали только с очень плохими образцами кода.
                                Если Вам попадался не лучший код — то это вовсе не означает, что патерн плохой.

                                Это примерно, как уроки по GoLang — меня от изучения него очень долго отталкивал стиль написания кода в примерах. Потом сам для себя решил, что если в примере экономят на буквах(в именах переменных, например), то никто не мешает мне на нем писать нормально.
                                • –1
                                  Я работал с разными образцами. Дело в самом паттерне — он допускает слишком много трактовок, что создаёт почву для плохого кода.
                          • +3
                            Есть альтернативное мнение, что модель должна быть исключительно тупой и в идеале вообще генериться автоматом на базе некого формального описания структуры данных.

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

                            Бизнес-логика, как правильно заметили, должна находиться в отдельном слое Services, независимом от контроллера и от реализации модели. Более того, в махровом энтерпрайзе бывает, что бизнес-правила верхнего уровня вообще выдаются неким отдельным сервисом масштаба корпорации на некотором формализованном языке.
                            • 0
                              Есть альтернативное мнение, что модель должна быть исключительно тупой и в идеале вообще генериться автоматом на базе некого формального описания структуры данных.

                              Так это и будут голые данные, зачем тут еще что-то «генерить»? (кроме как чисто какие-либо объекты ORM или вроде того — но это уже детали реализации).

                              Придерживаюсь точки зрения, что модель — это данные плюс поведение (бизнес-логика), а не только данные.
                              • 0
                                Генерить классы под конкретный орм из SQL, WSDL или какой-то другой схемы.
                              • 0
                                Сервисы бизнес-логики неотъемлемая часть модели. А хранение данных не отвественность модели.
                                • 0
                                  С точки зрения программы данные хранятся именно в модели, и нигде больше. То, что они там сбрасываются в файлы/бд/носкул это уже отдельный разговор.
                                  • 0
                                    С точки зрения программы, она создаёт модель в памяти из (как правило) части хранилища, обращается к модели и, если надо, сохраняет её изменения в хранилище. Модель — это не независимый процесс, это объекты создаваемые программой.
                                    • 0
                                      Я не знаю, какие программы вы пишете, но видимо не сильно сложнее helloworld-а.
                                      В реальном мире структура моделей вам приходит извне (от железа, от другого софта, в т.ч. унаследованного, из стандартов, от гос. органов и пр.), а правила их обработки (бизнес-логика) — тоже извне, но из другого.
                                      Примеры.
                                      В бизнесе: модели заданы альбомом бухгалтерских документов, утвержденным минфином, бизнес-логика — приказами налоговой, которая любит каждый год в них чего-нибудь менять.
                                      В инженерке: модели заданы а) софтом типа солидворкса или акада, файлы которого нужно уметь не только читать, но и корректно писать б) промышленными железками с их крайне тупыми форматами. «Бизнес-логика» приходит в виде ТЗ, ТУ, ОСТов и т.д. и т.п., а также стопки увесистых книжек про матан, сопромат и прочую жуть.
                                      В геймдизе: форматы моделей заданы видеокартами и используемой либой. Да, однородные координаты, да, только вот такой формат текстур. «Бизнес-логика» придумывается на ходу геймдизом и меняется семь раз в неделю.
                                      Вы конечно можете запихать в модель «годовой баланс» правила его сведения, или в модель космического корабля — правила расчёта урона его лазеров в зависимости от цели… но это очень плохая идея.
                                      • +4

                                        Почему-то многие считают, что модель — это классы в папочке models.
                                        Модель — это не класс и не объект. M в MVC — это самое абстрактное определение модели из существующих.
                                        Модель — это источники данных и правила их обработки, а работа приложения — моделирование.


                                        Объект, хранящий космический корабль — часть модели. Расчёт лазеров в зависимости от цели — тоже часть модели. Какой-то там сервис для обработки объектов — тоже часть модели.

                                        • 0
                                          В этом вообще проблема паттерна. Он обо всём и ни о чём конкретно, что подразумевает десятки трактовок и сотни имплементаций.
                                        • 0
                                          И что, программа работает с альбомами (бумажными) минфина и приказами налоговой? Нет, она создаёт в памяти некую модель бизнес-данных по этим альбомам, вызывает некое действие по этим приказам и сохраняет результат. «Годовой баланс» лишь часть всей модели учёта организации, а модель учёта лишь часть всей модели деятельности организации. И, конечно, правила сведения годового баланса будет находиться в модели баланса, а не в модели розничной торговли или планирования отпусков.
                              • 0
                                middleware pipeline

                                Можете расказать как устраняются старые монструозные контроллеры?
                                • 0
                                  Они заменяются на цепочку из middleware, например как здесь. Запрос и ответ просто передаётся «из рук в руки», пока кто-то не решает вернуть его или он не доходит до последнего получателя.

                                  В результате то, что можно ещё назвать контроллером по аналогии с ранее используемыми в Symfony 2 или ZF2, выглядит гораздо тоньше и является просто ещё одним шагом в цепочке логики.
                                  • 0

                                    Подождите-подождите, но у вас же мидлварь выполняет только инфраструктурные задачи, нет?

                                    • 0
                                      Не уверен, что понял ваш вопрос. Что значит «инфраструктурные»?
                                      • 0

                                        Аутентификация, авторизация, сериализация/десериализация.

                                        • 0
                                          Нет, не только. Вся логика проекта построена на таких слоях.
                                          • 0

                                            Тогда как вы разделяете домен, UI и отображение?

                                    • 0
                                      Они заменяются на цепочку из middleware, например как здесь

                                      Если вы вынесли код из контроллера в отдельный файл, как мусорку — этот код никуда не делся. Зачем логику разных частей сбрасывать в одном файле? Те мидлвары, которые вы указали — глубокие детали реализации и в роутах им точно не место. А если вынести в контроллер (где им и место), то будет самая обычная композиция.
                                • 0
                                  Примечание: меня до сих пор преследуют кошмары после ревью проектов, в которых одновременно использовались React и Angular 1.x.

                                  А можно поподробнее про этот момент? Что вызывало такие кошмары.
                                  Мы сейчас подумываем дать возможность интегрировать в свои проекты реакт-компоненты. Не скорости ради, как было в Ангулар 1, а скорее для более широкого выбора возможностей при разработке отдельных частей.

                                  • +2
                                    Это перевод статьи. Думаю что можно узнать подробности у самого автора
                                    https://medium.freecodecamp.com/is-mvc-dead-for-the-frontend-35b4d1fe39ec
                                  • +14
                                    А мне пофиг.
                                    Да здравствует jQuery! :)
                                    • +1
                                      Странно что у вас MVC вызывает негатив. Если правильно структурировать, то и на фронтенде все будет красиво. Со своего опыта могу сказать что модульная архитектура очень выручает. Приятно читать код, который у тебя разбит на разделы

                                      • js
                                      • sass
                                      • templates


                                      В свою очередь js разбит на пакеты и модули, к примеру

                                      • menu / header
                                      • dashboard / owner


                                      А каждый модуль состоит из router, handlers, views etc

                                      PS handler — это аналог контроллера

                                      Когда читаю подобные статьи и бывает думаю, а может поменять что-то, сменить структуру. Но когда начинаю анализировать, то не могу придумать что либо лучшее, чем то что есть сейчас.
                                      • +4
                                        MVC не вызывает у нас негатива, но в наших условиях лучше себя показали подходы с однонаправленным потоком, так как в этом случае вносимые в код изменения имеют более предсказуемые последствия. А это, в свою очередь, влияет на скорость разработки.

                                        Модульность, которую вы упомянули, безусловно выручает, причем независимо от архитектуры приложения. Грамотное структурирование кода на модули всегда в почете.
                                        • +1
                                          MVC не вызывает у нас негатива, но в наших условиях лучше себя показали подходы с однонаправленным потоком

                                          Почему вы говорите так, словно это взаимоисключающие вещи? «Колбаса не вызывает у меня негатива, но хлеб значительно важнее».
                                        • +3

                                          Для больших проектов разбиение на js,sass, template это зло, потом тяжело найти части, я разбиваю по модулям/компонентам, каждый со своими скриптами, стилями и ресурсами. Ну а webpack уже все в одно место собирает. Так можно сосредоточиться на одном маленьком и независимом модуле/компоненте и не нужно искать его запчасти в разных местах.

                                          • 0
                                            Templates and sass сожержат такую же структуру — один в один как и модули для JS. В самом начале, когда это проектировал, мне показалось это удобным. И в принципе так оно и есть. По крайней мере ни от кого из девелоперов не слышал, что это плохо.
                                            • 0
                                              Речь о том, чтобы на верхнем уровне выделять модуль, компонент системы, например customer, а туда складывать и js, и sass, и всё что ещё может понадобиться компонент.
                                              • +1
                                                Скорей всего такое разделение пошло от какого нибудь «hello world» проекта, который стал потом референсным шаблоном для всех html проектов. Когда в проекте 10 файлов, это ок, для микро проекта структура проекта, так же как и архитектура (вам не нужен архитектор, чтобы построить шалаш ;)) вообще не важна, можно как угодно раскидать файлы по папкам или вообще обойтись без них и все будет работать. Когда же количество файлов переваливает за сотню, то с одной стороны все сложено по полочкам, аккуратно и красиво, но искать связанные файлы становится очень трудно. Это не делает работу невозможной, но тратиться больше времени, которое уходит на поиск точно такой же папки только в другой ветке, вместо того, чтобы нашел папку компоненты и там все лежит все, я даже юнит тесты туда кидаю, что относится к этой компоненте и ничего, что к ней не относится. Это получается такой микропроект компоненты, внутри обычного, который кстати потом можно относительно легко выделить в отдельный проект, если он вдруг разросся. Попробуйте, это удобно.
                                                • 0
                                                  В начале так и было, но потом возникли небольшие проблемы с сборкой для гранта. И тогда я перенес структуру в нынешнее состояние. Уже и грант почил и сборка на галп перешла, сейчас вот планирую на вебпак перейти, а структура все так же неизменна. Надо покрутить вебпак (уже вторая версия) может внесу изменения.
                                                  • +1

                                                    Абсолютно согласен. Если templates положить отдельно от js, то иногда получаются странные переходы.
                                                    Из гипотетического src/js/app/users/UserView.js нужно делать такой require, чтобы получить соотвествующий шаблон


                                                    require('../../../templates/app/users/UserTemplate.hbs')

                                                    А если потом понадобится переместить js-файл, то нужно обязательно не забыть и про шаблон.

                                                    • 0
                                                      Эта проблема решается довольно просто. У меня используется шаблонизатор от моззилы nunjucksJS.Он умеет делать прекомпиляцию на сервере. После чего все шаблоны модуля вкомпиливаются в модуль. Дальше уже никакие импорты не нужны, как так по умолчанию шаблоны уже с модулем. И конечно, из-за прекомпиляции, скорость работы с шаблонами очень высокая. Рендер шаблона такой же как и у реакта, а иногда и обгоняет.

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

                                                      По поводу копирования — редкий кейс. Так как основные модули собраны в core и запаблишены в локальный NPM репозитарий. Другие проекты, которые разрабатываются просто инсталят себе модули. Таким путем достигается и версионность и поддержка.
                                            • +5
                                              Странно, на бэкенде как раз классический mvc не используется обычно, а предназначен классический как раз для динамических UI, где либо вью подписывается на изменения в модели, либо контроллер сообщает вью необходимость перерендериться. Как по мне, то react/redux как раз ложатся на классический MVC, где реакт — вью, редакс — модель, а реакт-редакс, контроллер, обеспечивающий реакцию вью на изменения модели.
                                              • +2

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

                                                • +2
                                                  Как по мне, то react/redux как раз ложатся на классический MVC, где реакт — вью, редакс — модель, а реакт-редакс, контроллер, обеспечивающий реакцию вью на изменения модели.

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

                                                  My presentational components:
                                                  — Are concerned with how things look.

                                                  My container components:
                                                  — Are concerned with how things work.


                                                  Ничего не напоминает? Поняли теперь как современные разработчики по-модному называют вьюху и контроллер? Presentational and Container!
                                                • +3

                                                  А разве архитектура с однонаправленными потоками — это не MVC? Ведь именно MVC постулировала зависимости в одном направлении.


                                                  На примере Реакта:


                                                  Model -> application state,
                                                  View -> JSX
                                                  Controller -> Reducer + Middleware, разделение на логику перехода по состояниям и логику взаимодействия с "внешним миром".

                                                  • +4
                                                    А к тому моменту все популярные серверные фреймворки в том или ином виде реализовывали классический MVC

                                                    Дальше можно не читать, в принципе. На сервере никогда не было классического MVC, на сервере был Model 2 и его наследники. Для классического MVC нужны события, которые в парадигме запрос-ответ сделать сложновато.

                                                    • 0
                                                      Model 2 это то, что называется JSP model 2 architecture?
                                                    • +1
                                                      про MVC была недавно хорошая обзорная статья
                                                      https://habrahabr.ru/post/321050/

                                                      MVC может быть активным/пассивным, поэтому событий в нем может и не быть.

                                                      А так я согласен с предыдущим комментарием. Однонаправленный поток — этот тот же самый MVC. Ничего нового не придумали.

                                                      Что на самом деле произошло?
                                                      1. работа со стейтом в одном месте помогает уменьшить разные сайд эффекты из-за мутабельности данных
                                                      2. отказались от точечной перерисовки — да какой смысл это делать, когда браузеры стали невероятно быстрыми, а большая часть компонентов очень маленькие. Бери и перерисовывай все целиком и никто ничего и не заметит.
                                                      3. мы снова получили событийную модель, на которую очень ругались в бекбоне. Говорили о том, как это сложно поддерживать, непонятно что происходит и тд. Но теперь она стала всем проще и понятнее. На самом деле мы просто уже привыкли к событиям и натерпелись связанного лапшекода и трудноподдерживаемого состояния. По факту чем меньше ваш компонент, тем проще подписываться на события, которые могут на него повлиять. Все это же самое можно делать сейчас и в бекбоне.
                                                      • –1
                                                        активный/пассивный — Вы точно MVC обсуждаете?
                                                        • 0
                                                          отказались от точечной перерисовки — да какой смысл это делать, когда браузеры стали невероятно быстрыми, а большая часть компонентов очень маленькие. Бери и перерисовывай все целиком и никто ничего и не заметит.

                                                          Перерисовывал весь как раз-таки Бэкбон: вставлял данные в шаблон, получал строку разметки и обновлял innerHTML. В Реакте же манипуляция щадящая — неизменившуюся часть элементов он и не трогает.

                                                        • –1

                                                          == Для классического MVC нужны события,


                                                          откуда вы это взяли?


                                                          == которые в парадигме запрос-ответ сделать сложновато.


                                                          причём здесь "запрос-ответ"?

                                                          • 0
                                                            откуда вы это взяли?

                                                            Из описания. Observer построен на событиях, даже если их нет явно в языке.


                                                            причём здесь "запрос-ответ"?

                                                            При том, что HTTP — это запрос-ответ. Соответственно, веб-сервера (как минимум преимущественно) — запрос-ответ. Соответственно, серверные MVC-фреймворки (в подавляющем большинстве) — запрос-ответ.

                                                            • –1

                                                              Это фантазии мастера выдуманных архитектур, прославившегося циничной эксплуатацией тупости ООП-шников.


                                                              Вебсокеты есть везде и давно. В отличие от "классического MVC"

                                                              • 0
                                                                Это фантазии мастера выдуманных архитектур, прославившегося циничной эксплуатацией тупости ООП-шников.

                                                                Says who, простите?


                                                                Вебсокеты есть везде и давно. В отличие от "классического MVC"

                                                                Ну так покажите мне (веб-)серверный MVC-фреймворк на вебсокетах.

                                                                • –1

                                                                  тот, кто читал эту статью, а так же ряд других работ данного автора.


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

                                                                  • 0
                                                                    тот, кто читал эту статью, а так же ряд других работ данного автора.

                                                                    Я тоже читал, и не говорю такого. Мнение на мнение.


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

                                                                    Эмм, у вас с логикой что-то. Я утверждаю, что в серверных фреймворках не классический MVC, а Model 2. Совершенно не вижу, почему я должен показывать классический MVC на основе событий на основе веб-сокетов.

                                                                    • –1

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


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

                                                                      • +1
                                                                        Если бы автор описал применяемую где-то архитектуру, написал бы хоть строчку кода в продакшн или стандарт, или просто сделал что-то полезное для индустрии, я бы с вами согласился

                                                                        Вы имеете полное право думать об автора что угодно, равно как и не соглашаться со мной. К счастью, я имею такое же право думать о вашем мнении что угодно и поступать с ним так, как я считаю нужным.


                                                                        В любом уважающем себя серверном фреймворке есть вебсокеты.

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


                                                                        Следовательно там есть события.

                                                                        Нет, следовательно, там есть возможность реализовать события.


                                                                        Вот я и прошу вас показать этот "классический MVC"

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


                                                                        Ну и да, примера ради: в ASP.net MVC нет вебсокетов, нет событий (кроме жизненного цикла самой инфраструктуры) и нет классического MVC — там типичный Model 2.

                                                                        • –3

                                                                          Здесь пойнт не в субъективности, а в оценке автора, на которого вы сослались как на авторитет — если он не создал ничего практически ценного, то какие есть основания у кого либо вообще принимать его мнение во внимание?


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

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


                                                                          Нет, следовательно, там есть возможность реализовать события.

                                                                          вебсокет без реализации событий лишён смысла


                                                                          Я и говорю: ошибка в логике

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


                                                                          в ASP.net MVC нет вебсокетов

                                                                          В контексте разговора существенно, что там есть возможность использовать вебсокеты, пусть даже через костыли. Это больше говорит о качестве инструментов .net/C#, чем о предмете разговора — многое, из того что нужно, отсутствует, зато дофига лишнего


                                                                          нет классического MVC

                                                                          Не надо выдавать ваши с Фаулером домыслы о том, что есть классический MVC, за стандартные общепринятые понятия

                                                                          • +1
                                                                            Здесь пойнт не в субъективности, а в оценке автора, на которого вы сослались как на авторитет — если он не создал ничего практически ценного, то какие есть основания у кого либо вообще принимать его мнение во внимание?

                                                                            Ваша оценка "не создал ничего практически ценного" — субъективна. Поэтому вы, конечно, можете не принимать его мнение во внимание. А я могу не принимать во внимание ваше мнение.


                                                                            Для SPA серверный фреймворк, в котором нельзя безболезненно прикрутить вебсокеты следует выкинуть на помойку.

                                                                            Вот только серверное MVC не использовалось для SPA (его можно использовать, понятное дело, но это будет смешанный фреймворка уже).


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

                                                                            Есть, но там все равно останется Model 2.


                                                                            Не надо выдавать ваши с Фаулером домыслы о том, что есть классический MVC, за стандартные общепринятые понятия

                                                                            Осталось понять, кто же определяет понятие "классический MVC".


                                                                            (мне все-таки интересно, с чем именно вы спорите — с используемым мной определением классического MVC, или с тем, что классический MVC в этом определении не использовался в серверных фреймворках?)

                                                                            • –1
                                                                              Ваша оценка "не создал ничего практически ценного" — субъективна.

                                                                              Ваше игнорирование аргументации не прокатывает, я выше написал почему это объективно


                                                                              Вот только серверное MVC не использовалось для SPA (его можно использовать, понятное дело, но это будет смешанный фреймворка уже).

                                                                              Именно. MVC с SPA использовали в силу того, что людям свойственно ошибаться, а мозги отравлены фаулерами. А так MVC на сервере такое же легаси, как и MPA, и дорога ему в ту же помойку


                                                                              Осталось понять, кто же определяет понятие "классический MVC".
                                                                              мне все-таки интересно, с чем именно вы спорите

                                                                              никакого "классического MVC" не существует, концепция MVC слишком размытая и абстрактная. Определения, область применения и пределы распространения не ясны. Если обобщать всё, что про это известно, так это будет скорее RoR, Django и ASP.net mvc со всем комплексом их уродства, нежели чем фантазии ООПшных теоретиков. Веб фрэймворки дают опыт обеих знаков, Фаулер и иже с ним не дают ничего, а потому идут далеко. То у них UML, то Rich Model, то DDD, то какое-то особенно правильное "классическое" понимание MVC то еще какая-то хрень.

                                                                              • +1
                                                                                я выше написал почему это объективно

                                                                                Nope. Нет объективного способа оценить "вклад в индустрию". Вы, видимо, не считаете Refactoring и PoEAA "вкладом", я — считаю, "и вместе им не сойтись".


                                                                                никакого "классического MVC" не существует

                                                                                Ну вот видите. Значит, и на сервере его не существует. С чем спорили-то?


                                                                                Если обобщать всё, что про это известно, так это будет скорее RoR, Django и ASP.net mvc со всем комплексом их уродства

                                                                                И на основании чего вы делаете такой вывод?


                                                                                Определения, область применения и пределы распространения не ясны.

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


                                                                                То у них UML, то Rich Model, то DDD, то какое-то особенно правильное "классическое" понимание MVC

                                                                                … а кто-то где-то говорил, что оно "особенно правильное"?

                                                                                • –1
                                                                                  Нет объективного способа оценить "вклад в индустрию"

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


                                                                                  И на основании чего вы делаете такой вывод?

                                                                                  Из того, что имеет хоть какой-то практический смысл, это наиболее близко к изначальной концепции MVC Реенскауга


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

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

                                                                                  • 0
                                                                                    можно тысячи бесспорных примеров вклада в индустрию привести

                                                                                    Попробуйте. Но в любом случае они не будут подтверждать то, что любой другой вклад в индустрию можно объективно оценить.


                                                                                    Из того, что имеет хоть какой-то практический смысл, это наиболее близко к изначальной концепции MVC Реенскауга

                                                                                    О, то есть вы все-таки сравниваете с Реенскаугом. Остается вопрос (а) как именно вы сравниваете и (б) как вы определяете, что имеет практический смысл, а что нет.


                                                                                    Но не суть, вы же уже сказали, что "классического MVC нет".


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

                                                                                    В таком случае этот спор изначально бессмысленнен. Я и не сомневался.

                                                                                • 0
                                                                                  RoR, Django и ASP.net mvc


                                                                                  А ничего, что все три фреймворка построены на паттернах из PoEAA?
                                                                                  Ну, наверное, все-таки стоит ознакомиться с источником перед тем как его критиковать.
                                                                                  • –1

                                                                                    Вообще всё построено на паттернах. Elm Architecture, Erlang/OTP например — квинтэссенция применения всевозможных паттернов. В то же время ничего не построено на паттернах. Такая вот особенность применения гуманитарных знаний, к которым относятся паттерны, в технических областях. Знакомится со всеми книжками по паттернам нет смысла. Можно прочитать один раз одну книжку чисто чтобы словарь понимать, и забыть про ООП-литературу насовсем.

                                                                                    • 0
                                                                                      Знакомится со всеми книжками по паттернам нет смысла. Можно прочитать один раз одну книжку чисто чтобы словарь понимать, и забыть про ООП-литературу насовсем.

                                                                                      Мимими, книжки по паттернам в ООП-литературу записали.

                                                                                      • 0
                                                                                        Продолжайте в том же духе. Серьезно.
                                                                                • +1
                                                                                  Для SPA серверный фреймворк, в котором нельзя безболезненно прикрутить вебсокеты следует выкинуть на помойку. и не надо пожалуйста рассказывать, что это субъективное мнение

                                                                                  Конечно субъективное, причём и неверное. Термин SPA появился в начале 2000-х, вебсокеты — через 10 лет. 10 лет надо было выкидывать фреймворки и писать всё с ноля?
                                                                                  • –1

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

                                                                                    • +1
                                                                                      Я не вижу никакого указание на время до или после вебсокетов вообще. Вижу только абсолютное утверждение: нет вебсокетов — на помойку.
                                                              • +3
                                                                Но разработчики упустили из вида важнейший вклад, который принес миру React, — компонентно-ориентированную архитектуру. React не изобрел компонентный подход, но перенес идею на новый уровень.

                                                                Чем же, по-вашему концепция реактового компонента принципиально отличается от директивы из первого Ангуляра или даже компонента jQuery?


                                                                Более того, реализации однонаправленного потока как раз-таки разрушают компонентную архитектуру, деинкапсулируя состояние компонентов.

                                                                • +3
                                                                  > Будет ли такой тип архитектуры лучшим решением через пять лет?

                                                                  Через 5 лет будут веб компоненты. Встроят ли в них реакт? К сожалению, да
                                                                  • +1

                                                                    Блин, да откуда берутся эти люди, утверждающие, что MVC не однонаправленный? Разница между MVC и Flux лишь в том, как взаимодействуют контроллер и модель. В MVC контроллер напрямую ковыряется в модели, в Flux он лишь генерит событие с данными, а модель уже сама решает как эти данные разложить по себе. Где тут изменение потока?

                                                                    • +3

                                                                      Мне кажется, что всё дело в пиаре Фейсбука, и в том хайпе, с которым он продвигал свой Flux.


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


                                                                      Программисты в основной массе — молодёжь, а молодёжи свойственны революционные настроения. Вот и с Реактом так же получилось. Типичные компоненты, новизна только в синтаксисе (PHP-way, ага :) и в способе обновления DOM-элементов. Причём писать на голом Реакте оказалось не по зубам даже его авторам, что и сподвигло их сделать свой фреймворк, объединив паттерны MVC, State Machine (в Redux это особенно хорошо видно) и Event Loop. Но толпа уже лезет на баррикады: "Землю рабочим, фабрики крестьянам!"

                                                                      • +1

                                                                        Меня больше удивляет термин "однонаправленный поток данных", который не может быть однонаправленным в принципе, если речь идёт о пользовательских интерфейсах. События от пользователя — такой же поток данных, как и события от сервера.


                                                                        А то, что продвигает фейсбук — антипаттерн "перескакивание через слои", когда глубоко вложенный слой взаимодействует напрямую с корневым слоем, а промежуточные слои не имеют над вложенными слоями полного контроля (контролируют они только один поток, ага).


                                                                        Так что это не "однонаправленный поток данных", а "контролируемый лишь в одном направлении поток данных". И придуман он был не потому, что это какая-то вся из себя классная архитектура, где корневой контроллер (который обозвали actions) знает о каждой кнопке на странице, а потому, что сначала запилили кривой шаблонизатор (реакт), не способный полноценно абстрагировать внешние компоненты, от внутренних, а потом стали думать, как бы прикрутить к нему обратный поток. И ничего умнее не придумали, как забить на все слои, стучаться напрямую в корневой и копипастить эти бесконечные редьюсеры+константы+экшены на каждый чих.

                                                                        • 0
                                                                          Меня больше удивляет термин "однонаправленный поток данных", который не может быть однонаправленным в принципе, если речь идёт о пользовательских интерфейсах. События от пользователя — такой же поток данных, как и события от сервера.

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

                                                                          • 0

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

                                                                            • 0

                                                                              Нет никакого обратного потока данных в рамках приложения. Пользователь взаимодействует с элементом UI в целом вне приложения, равно как и сервер и т. п., событие генерирует платформа (например, браузер) и сообщает о нём приложению. Обработчик события в приложении изменяет состояние приложения, и в ответ на изменение состояния приложение ререндерит интерфейс средствами платформы и на этом всё. Один поток: событие платформы -> обработчик в приложении -> изменения состояние приложения -> рендерер приложения -> рендерер платформы. Какие-то события платформы могут не изменять состояния приложения, какие-то обрабатываться самой платформой, какие-то изменять состояние, но это изменение не нуждается в рендеринге, что-то отрендеренное приложением, может не рендериться платформой (например часть окна за пределами вьюпорта), в общем поток обрубается раньше чем доходит до непосредственного аппаратного рендеринга, но вспять он не обращается, единственный способ как бы повернуть его вспять — инициировать новое событие через платформу или сэмулировать его. Часто, конечно, технически есть способы, например, изменить
                                                                              состояние из рендерера приложения, минуя основной поток, но это грубый выход за рамки архитектуры, которые не должен пройти ревью.

                                                                              • 0
                                                                                Нет никакого обратного потока данных в рамках приложения. Пользователь взаимодействует с элементом UI в целом вне приложения

                                                                                Ага, через libastral.


                                                                                Далее вы описываете типичное монолитное приложение, без какой-либо компонентной декомпозиции, которое невозможно масштабировать без мозолей на пальцах, да кодогенераторов. Задача компонента — инкапсулировать в с бе сложность, предоставляя простой интерфейс его управления. А во флоу, всё внутреннее состояние (на которое внешнему коду должно быть плевать), выпячивается наружу и требует аккуратной копипасты.

                                                                                • 0

                                                                                  Через платформу, тот же браузер, которая частью приложения не является.


                                                                                  Никто не мешает вам разбить свое приложение на компоненты, которые будут практически являться мини-приложениями в рамках того же флоу изнутри, и как бы элементами платформы снаружи. Классический пример для реакта — компоненты форм. Изнутри компонент выглядит как полноценное приложение со своим богатым состоянием, возможно даже со своими потоками взаимодействия с внешним миром, о которых приложение в целом не знает, а снаружи практически ничем не отличается от контрола платформы типа одиночного инпута, кроме разве того, что его value не скаляр, а объект со сколь угодно сложной структурой.


                                                                                  Вынесению состояния из компонента на уровень (или несколько) выше до первого общего вплоть до корневого компонента приложения подлежат только те состояния, которые шарятся между разными компонентами.

                                                                                  • 0

                                                                                    Только вот какие состояния шарить решать может лишь компонент-владелец. Поэтому для обеспечения максимальной гибкости из компонента выносятся вообще все состояния, делая их "чистыми". Кривая архитектура порождает кривые решения. Для сравнения, в $mol любой компонент является вещью в себе, но компонент владелец через биндинги может управлять любым его состоянием.

                                                                                    • 0
                                                                                      Только вот какие состояния шарить решать может лишь компонент-владелец.

                                                                                      И это логично. Хуже нет, когда компонент "думает": "это мои данные", а по факту их меняют все кому не лень, его даже не уведомляя.


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

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

                                                                                    • 0

                                                                                      Вот это не совсем правда: сложный компонент потребует для себя отдельного места в общем сторе и, соответственно, отдельного редьюсера. В общепринятом подходе, когда редьюсеры объединяются через combineReducers, вроде бы состояние компонента относительно инкапсулировано. Но, во-первых, до этих данных всё-таки можно достучаться как с целью чтения, так и записи; во-вторых, компонент должен знать точку монтирования своего редьюсера (так, документация к redux-form говорит, что редьюсер крайне желательно монтировать именно по ключу form и менять этот ключ не рекомендуется); в-третьих, если нам понадобятся одновременно два или несколько таких компонентов, то это или должен предусматривать компонент изначально, или потребуются костыли вроде одновременного размещения нескольких редьюсеров с разными ключами; в-четвёртых, потенциально переиспользуемый компонент должен упредить возможный конфликт типов событий, для чего, как правило, используют тот или иной префикс в строке типа; наконец, в-пятых, компонент должен или рассчитывать на некоторое требуемое множество middlewares, или не использовать их совсем. В итоге — никакой реальной инкапсуляции и переиспользуемости.

                                                                                      • 0

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

                                                                                        • 0

                                                                                          А какая реальная альтернатива? Flux?

                                                                                          • 0

                                                                                            Альтернатив много, выбирать нужно по требованиям и просто вкусу даже, если по требованиям особой разницы нет. Я сейчас вовсю юзаю обсерваблы MobX и как сторы глобальных или близких к тому состояний, и как стейт отдельного компонента. Главный плюс субъективный — легко применимы обычные принципы ООП с вкраплениями ФП в рамках декомпозиции по DDD.

                                                                                            • 0

                                                                                              А цепочка обсерваблов MobX не возвращает ли нас к multidirection flow?

                                                                                              • 0

                                                                                                По умолчанию нет, хотя постараться можно.

                                                                                        • +1

                                                                                          Подходы к декомпозиции redux-based приложений отличаются от "классических", унаследованных от jQuery-based приложений.


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


                                                                                          В редаксе точкой декомпозиции являются именно sub-state + actions + middleware. Это делается довольно неудобно, и ни в какое сравнение не идет, конечно, с "классическими" компонентами, особенно с непривычки. В первую очередь это связано с тем, что точка монтирования зашита в коде middleware и mapStateToXXX.


                                                                                          В редаксе отдельно переиспользуется логика и отдельно переиспользуется UI (в виде dumb components). Ну и, соответсвенно, контейнеры пишем каждый раз.


                                                                                          Но сила редакса в том, что он "развязывает" связь один-к-одному между данными и структурой UI. Он легко позволяет отображать одни и те же данные в нескольких видах, без дублирования кода.


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

                                                                                          • 0

                                                                                            В целом конечно так и есть, почему я ранее и писал, что подобная архитектура не позволяет использовать классические компоненты.


                                                                                            Однако вот я почти не видел примеров переиспользования редьюсеров кроме redux-form. Кажется, что на низком уровне просто переиспользуются statefull react components, а на более высоком все приложения намного более специфичны.


                                                                                            Редакс это позволяет делать относительно легко (послать экшен можно из любого места), на "классических" компонентах это сделать красиво невозможно без введения аналога сообщений.

                                                                                            Так редаксовские экшены и есть по сути сообщения.

                                                                                            • 0

                                                                                              Редьюсеры редко переиспользуются потому, что это обычно часть логики приложения (а не компонента). Поведение именно компонента обычно делается внутренним состоянием этого компонента (и его довольно мало, если разобраться).


                                                                                              Но это все-равно отличается от классических компонентов, где смешана логика приложения и логика компонента. Особенно это видно на сложных компонентах типа гридов, которые на реакте и редаксе теряют 80% своей сложности.


                                                                                              Я не смотрел и не использовал именно redux-forms, и, честно говоря, не совсем понимаю, какую выгоду они несут и что именно автоматизируют.

                                                                                              • 0
                                                                                                Особенно это видно на сложных компонентах типа гридов, которые на реакте и редаксе теряют 80% своей сложности.

                                                                                                И набирают два порядка сложности на дублицировании кода для каждого одинакового грида.
                                                                                            • 0
                                                                                              $my_app $mol_book
                                                                                                  sub /
                                                                                                      <= Menu $my_menu
                                                                                                          items <= menu_items /
                                                                                                      <= Grid $my_grid
                                                                                                          rows <= grid_rows /
                                                                                                          selected_row => grid_row_selected

                                                                                              namespace $.$mol { export class $my_app extends $.$my_app {
                                                                                              
                                                                                                  menu_items() {
                                                                                                      const selected = this.grid_row_selected()
                                                                                                      if( !selected ) return []
                                                                                              
                                                                                                      return [
                                                                                                          this.Menu().Copy() ,
                                                                                                          selected.removable() ? this.Menu().Remove() : null ,
                                                                                                          this.canEdit( selected ) ? this.Menu().Edit() : null ,
                                                                                                      ]
                                                                                                  }
                                                                                              
                                                                                              } }

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

                                                                                            • 0
                                                                                              В ember.js как-то решили эти противоречия? Да и выбор оставили — хочешь компоненты используй, хочешь контроллеры.
                                                                                              • 0

                                                                                                Вы у меня спрашиваете? С Эмбером я не работал, но если вы знаете, как они это обошли — не поделитесь?

                                                                                  • +1

                                                                                    Не могли бы Вы чуть раскрыть про неспособность реакта "полноценно абстрагировать внешние компоненты, от внутренних"? Я пока что не встречал вразумительных доводов в пользу хранения всего стэйта в отдельной от реакта корневой подсистеме. Вот для меня и мне подобных, привыкших к дектопу, Redux выглядит как абсолютный архитектурный костыль и вообще концептуально чуждая реакту штука с его componentDidMount, setState, и т.п.

                                                                                    • 0

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


                                                                                      Как это делается с нормальными компонентами: создаём компонент, который имеет собственное состояние. Если родителю нужно управлять этим состоянием — он просто биндит его на собственное состояние. В результате — оба компонента работают совместно с одним состоянием. А вложенному компоненту не надо знать про окружение.

                                                                                      • 0
                                                                                        Если родителю нужно управлять этим состоянием — он просто биндит его на собственное состояние. В результате — оба компонента работают совместно с одним состоянием. А вложенному компоненту не надо знать про окружение.

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

                                                                                        • 0

                                                                                          Но в таком случае мы лишаемся возможности делать независимый компонент TabBar, поскольку ему обязательно понадобится принимать текущий таб из родительского компонента и передавать в него индекс нового таба при клике на таб. То есть мало того, что имеем разрушение абстракции компонента, но и вынуждены вручную заботиться об обработке кликов, то есть ровно того, для чего и был предназначен этот компонент.

                                                                                          • 0

                                                                                            Не лишаемся. Таббар будет принимать из родителя нужный родителю таб как значение по умолчанию (не передали — принимает свое значение), а рапортовать ему не о клике, а об изменении пользователем текущего таба как рапортует тот же селект, которым таббар по сути и является.

                                                                                            • 0

                                                                                              А если надо не значением по умолчанию управлять, а текущим значением?

                                                                                              • 0

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

                                                                                          • 0
                                                                                            Плохой подход. Родитель изменяет состояние вложенного, а вложенный об этом и не догадывается.

                                                                                            Обоснуйте. Зачем вложенному компоненту знать где хранится его состояние?


                                                                                            Нарушение инкапсуляции в общем.

                                                                                            Не нарушается, ведь все свойства — публичные.

                                                                                            • +1

                                                                                              Чтобы среагировать должным образом на его изменение и "доложить" о нём тем компонентам (как родительским так и дочерним) об изменении если те в нём заинтересованы.


                                                                                              То есть просто изначально нарушенная :) Если всё всем публично доступно и все всё о структуре состояний знают на всех уровнях, то вообще нет смысла городить компонентную архитектуру. Компоненты предполагают изоляцию, при которой могут иметь свое полностью скрытое от родителей состояние либо не иметь его вовсе, а детям только явно передавать его часть.

                                                                                              • 0

                                                                                                Реактивное программирование избавляет от этой головной боли.


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


                                                                                                И не путайте изоляцию (нет доступа из вне) и инкапсуляцию (могу не знать о внутренностях).

                                                                                                • +1

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


                                                                                                  Станет. Прежде всего компоненту.


                                                                                                  Не путаю. Изоляция не обязательна должна быть физической. Можно бить по рукам, разработчикам, которые лезут в состояние не через публичное апи.

                                                                                                  • 0

                                                                                                    Основная польза реактивности — во взаимодействии различных частей приложения. От реактивности в рамках одного приложения мало пользы.


                                                                                                    Обоснуйте.


                                                                                                    Так не делайте непубличного апи. Это владельцу решать насколько детально он хочет контролировать своё имущество.

                                                                                                    • +1

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


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


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

                                                                                                      • 0

                                                                                                        Поправлюсь: От реактивности в рамках одного компонента мало пользы.


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


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

                                                                                                        • 0

                                                                                                          Не в рамках одного. В рамках компонента его состояние, на изменение которого он реактивно (для внешнего наблюдателя) реагирует. Что у него под капотом — его личное дело, экспозит наружу он только то, что считает нужным.


                                                                                                          Способов реализовать реактивность множество и вовсе незачем стремиться реализовать её во всём приложении одним способом. Нужно брать лучше подходящий для конкретного компонента, а то и брать тот, который предоставляет разработчик компонента, даже не заглядывая под капот. Вот есть какой-то компонент, например, автодополнения — какая мне разница как в нем реализована реактивность, если он ведет себя реактивно? Ну пока не окажется, что он слишком много ресурсов потребляет, конечно. Что обычно бывает, когда реактивность реализуется с помощью сторонних библиотек типа RxJs или MobX, есть и на Redux реактивность.


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

                                                                                                          • 0

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


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


                                                                                                            Ну и чего мы добьёмся этими форками? Сделаем вид, что не влезли во внутренности стороннего компонента и не огребём проблем с обновлениями? Мастурбация какая-то.

                                                                                                            • 0

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


                                                                                                              Зачем-то надо людям. Я сам предпочитаю атомарный MobX, но если что-то нужное мне использует под капотом RxJs или, прости господи, Redux для реализации реактивности, то меня это волнует только в плане объёма зависимостей.


                                                                                                              Обычный путь развития опен-сорс продуктов.

                                                                                                              • 0
                                                                                                                Не надо, не должно, не должны и надо

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


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

                                                                                                                Достигается это за счёт того, что компонент — это не просто какая-то чёрная коробочка, а крайне простая штука:


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


                                                                                                                Зачем-то надо людям.

                                                                                                                Ну так, React не предоставляет никакой реактивности, вот люди и лепят кто во что горазд. Потребность есть, стандарта нет.


                                                                                                                Обычный путь развития опен-сорс продуктов.

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

                                                                                                                • 0
                                                                                                                  Достигается это за счёт того, что компонент — это не просто какая-то чёрная коробочка, а крайне простая штука:

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


                                                                                                                  Ну так, React не предоставляет никакой реактивности, вот люди и лепят кто во что горазд. Потребность есть, стандарта нет.

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


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

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

                                                                                                                  • 0
                                                                                                                    писать код так, зная что всё API у тебя публичное гораздо сложнее, чем если хотя бы словах сообщил пользователям своего кода "это моё

                                                                                                                    Ну так на словах и сообщите. А уже пользователь пусть решает стоит ли ему рисковать.


                                                                                                                    Реакт предоставляет реактивность в виде перерендеринга компонента в ответ на передачу ему новых свойств

                                                                                                                    Нет, ручной вызов setState с последующей полной генерации дома и сравнения его с существующим — это ни разу не реактивность :-)


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

                                                                                                                    А вот это уже частный случай реактивности.


                                                                                                                    Или просто все станут пользоваться вашим форком, а не оригинальным.

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

                                                                                        • +1

                                                                                          Архитектура приложения, особенно бэкенд части, обычно строго иерархическая. У нас есть подсистемы, в них модули, в них компоненты, в компонентах используются другие компоненты и т.д. Все хорошо и красиво, исключения редкие, это хорошо работает и хорошо ложится на ООП.


                                                                                          Во фронтэнде это тоже работает, особенно когда речь идет об отображении данных (или, в терминологии стейт-менеждеров, о "потоке данных из состояния в представление"). Это привело к тому, что многие UI фреймворки ориентированы на создание замкнутых сложных компонентов с внутренним состоянием (как правило, смесью данных, состояния приложения и состояния самого компонента + бизнес логика в смеси с логикой UI).


                                                                                          Это перестает работать хорошо, когда начинается взаимодействие с пользователем. UI паттерны для взаимодействия не иерархичны сплошь и рядом. Возьмем текстовый редактор. У нас есть меню (которое находится на верхнем уровне иерархии компонентов), которое изменяет состояние выбранного абзаца в документе (которые находится на самом нижнем уровне). Состояние пункта меню зависит от нижнего уровня. Это нарушает иерархию (в которой нижний уровень должен зависить от верхнего, но не наоборот).


                                                                                          Возникает проблема коммуникации от нижнего уровня к верхнему. Особенно это костыльно получается при использовании изолированных компонентов. Нарушается изоляция — компонент должен сообщать информацию о состоянии наружу (выбран ли текст, или выбрана картинка). Это, конечно, утрировано, для демонстрации проблемы.


                                                                                          App-state management framework (redux, flux, частично MobX) решают эту проблему радикально — они полностью развязывают иерархию компонентов и иерархию состояния приложения. Плюс они дают единую точку входа для любого действия (своего рода универсальный контроллер), и адресация к нужному элементу — это теперь обязанность контроллера.


                                                                                          Это заодно решает и другую, менее острую и реже встречающуюся, проблему — отображение одних и тех же данных в разных видах.


                                                                                          У этой архитектуры достаточно своих проблем, начиная от многословности и слабой связности, заканчивая тем, что привычные паттерны не работают, переиспользование — это боль и мучения (в отличие от "кинь на форму и настрой свойства" подхода с изолированными компонентами).


                                                                                          Главным вопросом при выборе архитектуры UI должен быть "насколько иерархия данных отличается от ирерархии компонентов". Если она не отличается — инкапсулированные классические компоненты будут отлично работать. Если отличается сильно лучше их сразу развязать, чтобы не городить костыли и в итоге не прийти к самодельному flux-у.

                                                                                          • 0

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


                                                                                            Для вашего примера с меню редактора:


                                                                                            $my_ide $mol_page
                                                                                                file_opened?val \
                                                                                                selection_current?val \
                                                                                                -
                                                                                                head /
                                                                                                    <= Menu $my_menu
                                                                                                        selection?val <=> selection_current?val -
                                                                                                body /
                                                                                                    <= Panels $mol_book
                                                                                                        pages /
                                                                                                            <= Navigator $my_navigator
                                                                                                                current?val <=> file_opened?val -
                                                                                                            <= Editor $my_editor
                                                                                                                file <= file_opened -
                                                                                                                selection?val <=> selection_current?val -

                                                                                            Тут мы задаём меню, навигатор по файлам и собственно редактор текста, провязывая их свойства друг с другом, через свойства владельца. При этом, $my_menu, $my_navigator и $my_editor не знают друг о друге ровным счётом ничего. Но это не мешает им чётко друг с другом взаимодействовать: при выборе файла в навигаторе — он открывается в редакторе; при выборе текста в редакторе — активируются пункты меню, завязанные на работу с текстом; при выборе пункта меню — в редакторе соответствующе меняется текст.

                                                                                            • 0
                                                                                              В результате они будут взаимодействовать друг с другом, ничего друг про друга не зная.

                                                                                              Как это взаимодействие отлаживать?

                                                                                            • 0

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


                                                                                              Для redux можно решить задачу с меню вообще не имея общего состояния, а лишь подписываясь на события изменения выбранного элемента (и предусмотрев "null-событие" сброса выделения).

                                                                                              • 0
                                                                                                общие данные будут стремится на самый верхний уровень

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


                                                                                                нужны специальные механизмы для получения уведомлений об изменениях

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


                                                                                                потоки данных не однонаправленные

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

                                                                                                • 0

                                                                                                  Обычно потоки данных всё же однонаправленные — какое-то входное событие (действие пользователя, событие XHR или WS, сработавший таймер) преобразуется в какое-то действие, изменяющее состояние чего-то внешнего по отношению к приложению — страница перед глазами пользователя, ушедший на сервер запрос и т. п. По дороге потоки могут разветвляться, сливаться, какие-то терминироваться, но крайне редко требуется зацикливание, передача данных назад.

                                                                                                  • 0

                                                                                                    В смысле "не могут быть однонаправленными" и "притворяться, что обратного потока нет"? Могут быть, и никто не отрицает обратный поток.


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


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

                                                                                                    • 0

                                                                                                      Многие отрицают, смотрите выше :-)


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