Вам действительно нужен Redux?

    Не так давно React позиционировал себя как "V in MVC". После этого коммита маркетинговый текст изменился, но суть осталась той же: React отвечает за отображение, разработчик — за все остальное, то есть, говоря в терминах MVC, за Model и Controller.


    Одним из решений для управления Model (состоянием) вашего приложения стал Redux. Его появление мотивировано возросшей сложностью frontend-приложений, с которой не способен справиться MVC.


    Главный Технический Императив Разработки ПО — управление сложностью

    Совершенный код

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


    • состояние всего приложения хранится в одном месте
    • единственный способ изменить состояние — отправка Action'ов
    • все изменения происходят с помощью чистых функций

    Смог ли Redux побороть возросшую сложность и было ли с чем бороться?


    MVC не масштабируется


    Redux вдохновлен Flux'ом — решением от Facebook. Причиной создания Flux, как заявляют разработчики Facebook (видео), была проблема масштабируемости архитектурного шаблона MVC.


    По описанию Facebook, связи объектов в больших проектах, использующих MVC, в конечном итоге становятся непредсказуемыми:


    1. modelOne изменяет viewOne
    2. viewOne во время своего изменения изменяет modelTwo
    3. modelTwo во время своего изменения изменяет modelThree
    4. modelThree во время своего изменения изменяет viewTwo и viewFour

    О проблеме непредсказуемости изменений в MVC также написано в мотивации Redux'a. Картинка ниже иллюстрирует как видят эту проблему разработчики Facebook'а.



    Flux, в отличии от описанного MVC, предлагает понятную и стройную модель:


    1. View порождает Action
    2. Action попадает в Dispatcher
    3. Dispatcher обновляет Store
    4. Обновленный Store оповещает View об изменении
    5. View перерисовывается


    Кроме того, используя Flux, несколько Views могут подписаться на интересующие их Stores и обновляться только тогда, когда в этих Stores что-нибудь изменится. Такой подход уменьшает количество зависимостей и упрощает разработку.



    Реализация MVC от Facebook полностью отличается от оригинального MVC, который был широко распространен в Smalltalk-мире. Это отличие и является основной причиной заявления "MVC не масштабируется".


    Назад в восьмидесятые


    MVC — это основной подход к разработке пользовательских интерфейсов в Smalltalk-80. Как Flux и Redux, MVC создавался для уменьшения сложности ПО и ускорения разработки. Я приведу краткое описание основных принципов MVC-подхода, более детальный обзор можно почитать здесь и здесь.


    Ответственности MVC-сущностей:


    • Model — это центральная сущность, которая моделирует реальный мир и бизнес-логику, предоставляет информацию о своем состоянии, а также изменяет свое состояние по запросу из Controller'a
    • View получает информацию о состоянии Model и отображает ее пользователю
    • Controller отслеживает движение мыши, нажатие на кнопки мыши и клавиатуры и обрабатывает их, изменяя View или Model

    А теперь то, что упустил Facebook, реализуя MVC — связи между этими сущностями:


    • View может быть связана только с одним Сontroller'ом
    • Сontroller может быть связан только с одним View
    • Model ничего не знает о View и Controller и не может их изменять
    • View и Controller подписываются на Model
    • Одна пара View и Controller'а может быть подписана только на одну Model
    • Model может иметь много подписчиков и оповещает всех их после изменения своего состояния

    Посмотрите на изображение ниже. Стрелки, направленные от Model к Controller'у и View — это не попытки изменить их состояние, а оповещения об изменениях в Model.



    Оригинальный MVC совершенно не похож на реализацию Facebook'a, в которой View может изменять множество Model, Model может изменять множество View, а Controller не образует тесную связь один-к-одному с View. Более того, Flux — это MVC, в котором роль Model играют Dispatcher и Store, а вместо вызова методов происходит отправка Action'ов.


    React через призму MVC


    Давайте посмотрим на код простого React-компонента:


    class ExampleButton extends React.Component {
      render() { return (
        <button onClick={() => console.log("clicked!")}>
            Click Me!
        </button>
      ); }
    }

    А теперь еще раз обратимся к описанию Controller'a в оригинальном MVC:


    Controller отслеживает движение мыши, нажатие на кнопки мыши и клавиатуры и обрабатывает их, изменяя View или Model

    Сontroller может быть связан только с одним View

    Заметили, как Controller проник во View на третьей строке компонента? Вот он:


    onClick={() => console.log("clicked!")}

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


    Работая с React, нам остается только реализовать Model. React-компоненты смогут подписываться на Model и получать уведомления при обновлении ее состояния.


    Готовим MVC


    Для удобства работы с React-компонентами, создадим свой класс BaseView, который будет подписываться на переданную в props Model:


    // src/Base/BaseView.tsx
    import * as React from "react";
    import BaseModel from "./BaseModel";
    
    export default class <Model extends BaseModel, Props> extends React.Component<Props & {model: Model}, {}> {
        protected model: Model;
    
        constructor(props: any) {
            super(props);
            this.model = props.model
        }
    
        componentWillMount() { this.model.subscribe(this); }
    
        componentWillUnmount() { this.model.unsubscribe(this); }
    }

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


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


    // src/Base/BaseModel.ts
    export default class {
        protected views: React.Component[] = [];
    
        subscribe(view: React.Component) {
            this.views.push(view);
            view.forceUpdate();
        }
    
        unsubscribe(view: React.Component) {
            this.views = this.views.filter((item: React.Component) => item !== view);
        }
    
        protected updateViews() {
            this.views.forEach((view: React.Component) => view.forceUpdate())
        }
    }

    Я реализую всем известный TodoMVC с урезанным функционалом, весь код можно посмотреть на Github.


    TodoMVC является списком, который содержит в себе задачи. Список может находится в одном из трех состояний: "показать все задачи", "показать только активные задачи", "показать завершенные задачи". Также в список можно добавлять и удалять задачи. Создадим соответствующую модель:


    // src/TodoList/TodoListModel.ts
    import BaseModel from "../Base/BaseModel";
    import TodoItemModel from "../TodoItem/TodoItemModel";
    
    export default class extends BaseModel {
        private allItems: TodoItemModel[] = [];
        private mode: string = "all";
    
        constructor(items: string[]) {
            super();
            items.forEach((text: string) => this.addTodo(text));
        }
    
        addTodo(text: string) {
            this.allItems.push(new TodoItemModel(this.allItems.length, text, this));
            this.updateViews();
        }
    
        removeTodo(todo: TodoItemModel) {
            this.allItems = this.allItems.filter((item: TodoItemModel) => item !== todo);
            this.updateViews();
        }
    
        todoUpdated() { this.updateViews(); }
    
        showAll() { this.mode = "all"; this.updateViews(); }
    
        showOnlyActive() { this.mode = "active"; this.updateViews(); }
    
        showOnlyCompleted() { this.mode = "completed"; this.updateViews(); }
    
        get shownItems() {
            if (this.mode === "active") { return this.onlyActiveItems; }
            if (this.mode === "completed") { return this.onlyCompletedItems; }
            return this.allItems; 
        }
    
        get onlyActiveItems() {
            return this.allItems.filter((item: TodoItemModel) => item.isActive());
        }
    
        get onlyCompletedItems() {
            return this.allItems.filter((item: TodoItemModel) => item.isCompleted());
        }
    }

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


    // src/TodoItem/TodoItemModel.ts
    import BaseModel from "../Base/BaseModel";
    import TodoListModel from "../TodoList/TodoListModel";
    
    export default class extends BaseModel {
        private completed: boolean = false;
        private todoList?: TodoListModel;
        id: number;
        text: string = "";
    
        constructor(id: number, text: string, todoList?: TodoListModel) {
            super();
            this.id = id;
            this.text = text;
            this.todoList = todoList;
        }
    
        switchStatus() { 
            this.completed = !this.completed
            this.todoList ? this.todoList.todoUpdated() : this.updateViews();
        }
    
        isActive() { return !this.completed; }
    
        isCompleted() { return this.completed; }
    
        remove() { this.todoList && this.todoList.removeTodo(this) }
    }

    К получившимся моделям можно добавлять любое количество View, которые будут обновляться сразу после изменений в Model. Добавим View для создания новой задачи:


    // src/TodoList/TodoListInputView.tsx
    import * as React from "react";
    import BaseView from "../Base/BaseView";
    import TodoListModel from "./TodoListModel";
    
    export default class extends BaseView<TodoListModel, {}> {
        render() { return (
            <input 
                type="text"
                className="new-todo" 
                placeholder="What needs to be done?"
                onKeyDown={(e: any) => {
                    const enterPressed = e.which === 13;
                    if (enterPressed) { 
                        this.model.addTodo(e.target.value);
                        e.target.value = "";
                    }
                }}
            />
        ); }
    }

    Зайдя в такой View, мы сразу видим, как Controller (props onKeyDown) взаимодействует с Model и View, и какая конкретно Model используется. Нам не нужно отслеживать всю цепочку передачи props'ов от компонента к компоненту, что уменьшает когнитивную нагрузку.


    Реализуем еще один View для модели TodoListModel, который будет отображать список задач:


    // src/TodoList/TodoListView.tsx
    import * as React from "react";
    import BaseView from "../Base/BaseView";
    import TodoListModel from "./TodoListModel";
    import TodoItemModel from "../TodoItem/TodoItemModel";
    import TodoItemView from "../TodoItem/TodoItemView";
    
    export default class extends BaseView<TodoListModel, {}> {
        render() { return (
            <ul className="todo-list">
                {this.model.shownItems.map((item: TodoItemModel) => <TodoItemView model={item} key={item.id}/>)}
            </ul>
        ); }
    }

    И создадим View для отображения одной задачи, который будет работать с моделью TodoItemModel:


    // src/TodoItem/TodoItemView.jsx
    import * as React from "react";
    import BaseView from "../Base/BaseView";
    import TodoItemModel from "./TodoItemModel";
    
    export default class extends BaseView<TodoItemModel, {}> {
        render() { return (
            <li className={this.model.isCompleted() ? "completed" : ""}>
                <div className="view">
                    <input
                        type="checkbox"
                        className="toggle"
                        checked={this.model.isCompleted()}
                        onChange={() => this.model.switchStatus()}
                    />
                    <label>{this.model.text}</label>
                    <button className="destroy" onClick={() => this.model.remove()}/>
                </div>
            </li>
        ); }
    }

    TodoMVC готов. Мы использовали только собственные абстракции, которые заняли меньше 60 строк кода. Мы работали в один момент времени с двумя движущимися частями: Model и View, что снизило когнитивную нагрузку. Мы также не столкнулись с проблемой отслеживания функций через props'ы, которая быстро превращается в ад. А еще нам не пришлось создавать фэйковые Container-компоненты.


    Что не так с Redux?


    Меня удивило, что найти истории с негативным опытом использования Redux проблематично, ведь даже автор библиотеки говорит, что Redux подходит не для всех приложений. Если ваше frontend-приложения должно:


    • уметь сохранять свое состояние в local storage и стартовать, используя сохраненное состояние
    • уметь заполнять свое состояние на сервере и передавать его клиенту внутри HTML
    • передавать Action'ы по сети
    • поддерживать undo состояния приложения

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


    Redux слишком сложный, и я говорю не про количество строк кода в репозитории библиотеки, а про те подходы к разработке ПО, которые он проповедует. Redux возводит indirection в абсолют, предлагая начинать разработку приложения с одних лишь Presentation Components и передавать все, включая Action'ы для изменения State, через props. Большое количество indirection'ов в одном месте делает код сложным. А создание переиспользуемых и настраиваемых компонентов в начале разработки приводит к преждевременному обобщению, которое делает код еще более сложным для понимания и модификации.


    Для демонстрации indirection'ов можно посмотреть на такой же TodoMVC, который расположен в официальном репозитории Redux. Какие изменения в State приложения произойдут при вызове callback'а onSave, и в каком случае они произойдут?


    При отсутствии желания устраивать расследование самостоятельно, можно заглянуть под спойлер
    1. hadleSave из TodoItem передается как props onSave в TodoTextInput
    2. onSave вызывается при нажатии Enter или, если не передан props newTodo, на действие onBlur
    3. hadleSave вызывает props deleteTodo, если заметка изменилась на пустую строку, или props editTodo в ином случае
    4. props'ы deleteTodo и editTodo попадают в TodoItem из MainSection
    5. MainSection просто проксирует полученные props'ы deleteTodo и editTodo в TodoItem
    6. props'ы в MainSection попадают из контейнера App с помощью bindActionCreator, а значит являются диспетчеризацией action'ов из src/actions/index.js, которые обрабатываются в src/reducers/todos.js

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


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


    Создание Flux и Redux было мотивировано немасштабируемостью MVC, но эта проблема исчезает, если применять оригинальный MVC. Redux пытается сделать изменение состояния приложения предсказуемым, но водопад из callback'ов в props'ах не только не способствует этому, но и приближает вас к потере контроля над вашим приложением. Возросшей сложности frontend-приложений, о которой говорят авторы Flux и Redux, не было. Было лишь неправильное использование подхода к разработке. Facebook сам создал проблему и сам же героически ее решил, объявив на весь мир о "новом" подходе к разработке. Большая часть frontend-сообщества последовала за Facebook, ведь мы привыкли доверять авторитетам. Но может настало время остановиться на мгновение, сделать глубокий вдох, отбросить хайп и дать оригинальному MVC еще один шанс?


    UPD


    Изменил изначальные view.setState({}) на view.forceUpdate(). Спасибо, kahi4.

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

    Стоит ли использовать оригинальный MVC вместо Redux?
    Поделиться публикацией
    Ой, у вас баннер убежал!

    Ну. И что?
    Реклама
    Комментарии 632
    • +2

      У вас получился MV без C с последующими проблемами: жесткая завязка представления на модель с невозможностью подменять последнюю.


      Сontroller может быть связан только с одним View

      Сильное заявление, проверять я его конечно же не буду.


      А еще это this.views.forEach((view: React.Component) => view.setState({})) просто лютая жесть. Мало того, что в реакте есть специально для этого forceUpdate, мало того, что с непонятного перепуга модель должна знать о всех вью, на нее подписанных, так еще фактически убивается использование shouldComponentUpdate… Погодите-ка… Он ведь вызовется и в нем можно проверить модель. Спорим, что это просто случайная "фича", а не обдуманный ход.


      Возможно, такое решение является не самым лучшим, но его легко изменить, и оно не влияет на суть статьи.

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


      Сейчас вы же попросту построили backbone на React, что создает аж два вопроса: зачем он на реакте и чем сам бэкбон в таком случае не угодил?


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

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


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


      Ну и еще немного критики:


      onClick={() => this.model.remove()}

      Стандартный вопрос на собеседовании: почему так делать не стоит и как это писать правильно.


      Где у BaseModel метод componentWillReceiveProps?

      • 0
        У вас получился MV без C с последующими проблемами: жесткая завязка представления на модель с невозможностью подменять последнюю.

        Создаем новую модель с таким же интерфейсом и заменяем старую, в чем проблема?
        Controller есть — это callback'и onClick, onBlur и прочие.


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

        Можете проверить, это несложно. Откройте описание MVC в Smalltalk-80 и найдите пункт "The View — Controller Link". В этом пункте написано:


        Unlike the model, which may be loosely connected to multiple MVC triads, Each view is
        associated with a unique controller and vice versa.

        с непонятного перепуга модель должна знать о всех вью, на нее подписанных, так еще фактически убивается использование shouldComponentUpdate

        Опять же цитата из описания MVC:


        In that case the object which depends upon the model's state — its view — must be notified that the model has changed. Because only the model can track
        all changes to its state, the model must have some communication link to the view.

        When a new view is given its model, it
        registers itself as a dependent of that model. When the view is released, it removes itself as a
        dependent

        Действительно можно использовать forceUpdate, спасибо. На самом деле нужно было у базового View реализовать метод modelUpdated(model: BaseModel), но я поленился.


        Стандартный вопрос на собеседовании: почему так делать не стоит и как это писать правильно.

        Правильно так:


        handleClick = () => this.model.remove();
        
        onClick={this.hadleClick}

        Но выглядит это немного громоздко


        Где у BaseModel метод componentWillReceiveProps?

        А зачем он нужен модели?

        • 0
          А зачем он нужен модели?

          У baseView, ошибся. Нужен, если модель поменяли, привязаться к новой.


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


          Про modelUpdated у базовой вью тоже не совсем верно. Так уж сложилось, что в реакте для этого принято использовать High ordered component, по типу редуксовского коннекта, observer из mobx-react и подобного. Почему? Реактивность, все дела, вы не вмешиваетесь в стандартный жизненный цикл компонента и все прочее.


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


          Если говорить идеологически то а: смалталк не единственный канон MVC, обычно распространено все таки явление что контроллер и вью развязаны, на это есть причины (хотя бы посмотрите где этот смалталк сейчас, а сколько реализаций с более привычным подходом)?


          Б: вы все ещё превращаете реакт в кривую версию бэкбона, от чего в целом при его создании и пытались уйти,


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


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


          P.s. Я не защищаю редукс, он мне жутко не нравится, лично я бы смотрел в сторону cyclejs, но там тоже не все гладко.

          • 0
            У baseView, ошибся. Нужен, если модель поменяли, привязаться к новой.

            Я понял сам кейс, но не могу представить случай, когда в рантайме нужно будет поменять модель у компонента. Если это реальная необходимость, то функциональность всегда можно дописать, это же Software. За такую гибкость его многие и любят.


            Про modelUpdated у базовой вью тоже не совсем верно. Так уж сложилось, что в реакте для этого принято использовать High ordered component

            HOC — это же лишь подход, и при использовании MVC я им принебрегаю и обновляю компоненты при изменении модели. Плюс ко всему, как я написал в статье, мне не нравится сама идея Container Components.


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

            Не стоит презирать этот язык только потому что он сейчас не фигурирует на рынке. Благодаря Smalltalk мы с вами можем как пользоваться UI, так и программировать его. Smalltalk дал толчок развитию Apple и персональных компьютеров. Если будет время и желание. предлагаю прочитать The Early History Of Smalltalk.


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

            Но я же использую React по назначению — только как View. Моя реализация модели никак не отражается на удобстве его использования. А если вы видите, что отражается, то всегда можете изменить реализацию BaseModel и BaseView, я лишь направил вектор размышлений по направлению оригинального MVC.


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

            Не будет много моделек, Facebook обманывает :)
            Будет модель Chat, которая содержит список Thread. Каждый Thread имеет количество непрочитанных сообщений. Chat суммирует количество всех непрочитанных сообщений в Thread'е и возвращает их с помощью getCountOfUnreadMessages(). При получении нового сообщения Thread оповещает Chat, что он обновился.


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

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

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

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


              По поводу реакта как вью — тут вопрос сильно глубже. В данном примере можно докопаться до прямого вызова методов, но вот что больше интересно — как вы решите сопутствующие проблемы в виде роутинга? Да и вообще обобщить: кто порождает модели? Кто делает асинхронные запросы? Я так понял, за все отвечают модели, реакт только отображение. Хорошо, не спорю, поддерживаю всеми руками.


              По поводу компонентов высшего порядка — а выбор то небольшой. Лезть хуками в жизненный цикл компонента не стоит, потому что внутренности могут меняться, потом будет куча геморроя с миграциями на новые версии как при миграции на 16 версию. Я просто рекомендую, я понимаю что наследование выглядит крайне уместно, но проблема в том что за жизненный цикл отвечает не сам базовый компонент как таковой, а то, что его рендерит (именно поэтому все методы публичные) и могут возникнуть проблемы например с enzyme.


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


              Теперь про пример с фейсбуком. Дело в том, что помимо Chat есть так же SideBar, который так же должен отобразить эту цифру, и есть шапка — в итоге три места. Нужно либо шарить между ними Chat, либо искать другой метод. В ангуларе, шарпе, Джаве и всем здоровом мире используют DI для этого, но в реакте вот религия не позволяет. На самом деле, проблемы то и заключаются в том, когда разные элементы страницы начинают между собой общаться событиями, возникает вопрос кто кого должен порождать, кто кого передавать и все такое. Проблемы начинаются именно тогда, когда зависимости асинхронны, например что-то внизу должно среагировать на то, что что-то вверху изменилось, да ещё и подтянуть актуальные данные. Модели начинают знать слишком много друг о друге, им нужно подписываться друг на друга. Это попытались решишь через однонаправленный поток событий и возможность любому компоненту выловить любое событие. Потом кто-то насмотрелся на хаскель, захотел чистых функций и все такое и создал редукс. Иммутабельнотсь стейта скорее техническое ограничение, чем прихоть. От идеального MV* проблема все равно не решится.


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


              Mobx — да, прям советую. У него тоже не без проблем, но фактически он просто уберёт с вас будущие проблемы с подпиской/отпиской и всем таким.

              • 0
                Теперь про пример с фейсбуком. Дело в том, что помимо Chat есть так же SideBar, который так же должен отобразить эту цифру, и есть шапка — в итоге три места.

                Chat, SideBar, Header — это все View, которые могут содержать в себе другие View. На словах можно рассказывать слишком долго, поэтому приведу пример кода, в котором буду использовать описанные в статье BaseView и BaseModel.


                Я бы создал модель чата:


                import BaseModel from "./Base/BaseModel";
                import ThreadModel from "./ThreadModel";
                
                export default class extends BaseModel {
                    threads: ThreadModel[] = [];
                
                    get unreadMessagesCount() { 
                        return this.threads.reduce((count: number, thread: ThreadModel) => {
                            return count + thread.unreadMessagesCount
                        }, 0)
                    }
                
                    threadUpdated() { this.updateViews; }
                }

                А затем CounterView:


                import * as React from "react";
                import BaseView from "./Base/BaseView";
                import ChatModel from "./ChatModel";
                
                export default class extends BaseView <ChatModel, {}> {
                    render() { return (
                        <span>{this.model.unreadMessagesCount}</span>
                    ); }
                }

                После чего инстанцировал бы все модели и общие компоненты на уровне PageView, например. А в HeaderView передал бы инстанс ChatView в качестве props'а:


                import * as React from "react";
                import BaseView from "./Base/BaseView";
                import HeaderModel from "./HeaderModel";
                import CounterView from "./CounterView";
                
                export default class extends BaseView <HeaderModel, {counterView: CounterView}> {
                    render() { return (
                        <header>
                            <section>Facebook</section>
                            {this.props.counterView}
                        </header>
                    ); }
                }

                Если CounterView нужно как-то кастомизировать внутри HeaderView, то можно в HeaderView передавать ChatModel через props'ы и инстанцировать CounterView уже внутри HeaderView. Или можно использовать ChatView как child внутри HeaderView. Возможно, есть более элегантное решение, но я бы сделал так.


                Интересная беседа получается, спасибо за нее.

                • +1

                  Могу сразу сказать, что при таком подходе вам придётся таскать сверху вниз огромную кучу моделей. Спор как это решить действительно идёт с 80х, причём как в C++, C#, так вон и в фронте, потому что у каждого решения есть свои недостатки (поправьте если я не прав и есть прям золотое универсальное решение).


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


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


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


                  Есть запрос /project, по которому рисуется весь каркас приложения, шапка и прочее. Есть запрос /data, по которому рисуются данные в табличке. Пока все нормально. А теперь есть кнопка «подключить источник данных», которая сперва отправляет запрос на /add-source, рисует иконку загрузки в шапке и в таблице, когда он выполнился, начинает опрашивать каждые 10 секунд проект на предмет изменения флага, после чего идёт обновлять данные и только после этого убирает иконку загрузки и показывает реальные данные на своих местах. Нажать кнопку можно в двух местах. Начнём с этого. В действительности все гораздо сложнее, например пользователь за это время может банально переключить на другое представление и впустую ходить за данными смысла нет, ещё проекты могут измениться на сервере независимо, их вообще могут сбросить, самих кнопок три группы по 6, при этом данные берутся из большего количества источников и мне кажется нам пора переделывать API (злой комментарий про то, что на бэкэнде сделают как им удобнее, а потом «че вы так все переусложняете»).


                  P.S. Я использую rx и счастлив, в целом то. Там такое делается просто, но есть другие проблемы.

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

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

                    • +2
                      Не надо так радикально. Да, MVC предписывает выделить только одну модель, но никто не запрещает проводить дальнейшую декомпозицию.
                      • +1
                        Модель может быть декомпозирована на несколько классов, модулей и т. п., но в рамках MVC подписываются они на события друг друга, как-то по другому взаимодействуют или совсем не зависят друг от друга никакого значения не имеет, для контроллера и вью — это одна модель. Класс модели — это не класс, хранящий данные и обеспечивающий (не всегда) уведомление об изменениях, а класс, принадлежащий слою модели, что-то из обязанностей модели в MVC реализующий.

                        P.S. Уверен, что вы лично это понимаете, но очень уж часто моделью называют именно класс, хранящий данные по типу ActiveRecord. Когда вижу что-то типа class User extends Model биться головой об стену хочется. :)
                        • –2
                          > но в рамках MVC подписываются они на события друг друга, как-то по другому взаимодействуют или совсем не зависят друг от друга никакого значения не имеет, для контроллера и вью — это одна модель.

                          Вид вообще и не в курсе о модели (и даже, возможно, о том, что она есть). Он просто предоставляет некоторое апи, по выводу информации пользователя, модель ее дергает. Либо вид может запрашивать у модели данные. У кого он запрашивает данные (и кто пользуется его апи) — не волнует никого.
                          • 0
                            Он просто предоставляет некоторое апи, по выводу информации пользователя, модель ее дергает

                            Простите, а можете написать псевдокод, как это выглядит? Ну то есть в какой момент модель решает, что надо отрендерить ту или иную вьюшку? Как она решает, какую именно и какие данные ей будут нужны? Как в коде выглядит знание модели о вьюшке?
                            • –2
                              > Как она решает, какую именно и какие данные ей будут нужны?

                              Она это знает, это же модель. В ней хранится вся информация о стейте приложения.

                              > Как в коде выглядит знание модели о вьюшке?

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

                              Ну в общем то же самое, что с моделью и контроллером.
                              • 0
                                Это не псевдокод. Вашу игру словами я, увы, не понимаю.
                                • –2
                                  class view {
                                  public viewMethod(...) {...}
                                  }

                                  class model {
                                  public modelMethod() { view.viewMethod() }
                                  }
                                  • 0
                                    Он не отвечает на те вопросы, которые я предусмотрительно задал.
                                    • 0
                                      На какие именно не отвечает, по-вашему? Вроде, на все, по списку. Что я пропустил?
                                      • 0
                                        Ну то есть в какой момент модель решает, что надо отрендерить ту или иную вьюшку? Как она решает, какую именно и какие данные ей будут нужны?
                                        • 0
                                          > Ну то есть в какой момент модель решает, что надо отрендерить ту или иную вьюшку?

                                          Это вопрос ее спецификации.

                                          > Как она решает, какую именно и какие данные ей будут нужны?

                                          public viewMethod(...) {...} — какие аргументы во viewMethod, те данные и нужны. Спецификацию метода мы ведь знаем. Момент показа тоже известен из спецификации. Для определенности — пусть будет
                                          class view {
                                          public showCart(...) {...}
                                          }

                                          Этот метод может дернуть или модель (если надо показать корзину при определенных изменениях модели, каких именно — это спецификация модели), или контроллер (опять же, по спецификации, например, при клике на какую-то кнопку). Сам вид вообще в данном случае пассивен, он ничего не решает, а только предоставляет апи (содержит саму логику отображения корзины, но не отвечает за сам факт ее вывода), активна модель. MVC позволяет и другой подход — когда вид сам опрашивает модель и решает, что делать. И тот и тот подход допустим (но, наверное, их нежелательно смешивать).
                                          • 0
                                            А зачем модели инстанс вьюхи вообще?
                                            Модель не может работать без вью совсем?
                                            • 0
                                              > А зачем модели инстанс вьюхи вообще?

                                              Если модель активная — то затем, чтобы обновлять view.

                                              > Модель не может работать без вью совсем?

                                              Может, если пассивная.
                                              • 0
                                                Модель бросающая ивенты — активная?
                                                • 0
                                                  Да. Бросание ивента Х ничем не отличается от вызова соответствующего метода Х на вью, это ровно одно и то же. В обоих случаях у модели будет ссылка на инстанс вида, прямая или транзитивная.
                                                  • 0
                                                    Нет, ибо в случае с ивентом модель не знает, да и по сути ей всеравно, слушает кто-то эти ивенты или нет.
                                                    • 0

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

                                                      • 0
                                                        > Нет, ибо в случае с ивентом модель не знает

                                                        Знает. У нее необходимо есть ссылка на вид, как она может не знать?
                                                        • +1
                                                          Зачем ей ссылка на вью? Да и на какой из этих самых вью? У одной модели может быть много представлений (Вся инфа на странице профиля, имя и фоточка дето в чатике, имя, мыло, баланс в личном кабинете).
                                                          Вы ссылки на все эти вью будете пихать в модель?
                                                          В лучше случае у модели будет ссылка на ивент диспатчер, а еще лучше если модель имплементит какой-то EventProducerInterface и ивенты забирает внешняя сущность, которая ни про вид, ни про модель с которой работает ничего не знает.
                                                          • 0
                                                            > Зачем ей ссылка на вью?

                                                            За тем, что объект А не может взаимодействовать с объектом Б если нет ссылки. Если вы отправляете некое сообщение кому-либо через подписку — у вас есть ссылка. Иначе бы сообщение не дошло.

                                                            > Вы ссылки на все эти вью будете пихать в модель?

                                                            Все пихают, чем я хуже? Если другого способа не придумано.

                                                            > В лучше случае у модели будет ссылка на ивент диспатчер

                                                            А ивент диспетчер имеет ссылку на вью. Таким образом, модель, транзитивно, имеет ссылку на вью. О чем я и говорю.
                                                            • 0
                                                              Если вы отправляете некое сообщение кому-либо через подписку — у вас есть ссылка. Иначе бы сообщение не дошло

                                                              Сообщение — это не прямая ссылка, это символьное описание того контекста метода, который вы собирайтесь активировать. Где лежит этот метод не принципиально. Пусть даже это self метод
                                                              disp["cобытие", func] Массив disp связывает одно с другим. oximod прав.

                                                              • 0
                                                                > Сообщение — это не прямая ссылка, это символьное описание того контекста метода, который вы собирайтесь активировать.

                                                                Да какая разница? Важно, что view.someMethod() это ровно то же самое, что dispatcher.notify(«someMethod»). В обоих случаях отправитель прибит гвоздями к апи получателя. Он должен знать это апи и понимать его семантику. И прямая у вас ссылка или транзитивная — тоже совершенно не важно.
                                                                • 0

                                                                  Да но получатели могут быть разными, и это не обязателью V, Тот кто подписан тот и получит. Это может быть С. Я бы сказал [должен быть]

                                                                  • 0
                                                                    > Да но получатели могут быть разными, и это не обязателью V

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

                                                                      Да модель знает, но не о спецификации API. Эвенты это своего рода человеко удобный интерфейс, за которым может быть скрыта разная реализация. Впрочем как и за пропсами. И локация диспетчеров может быть разная. В случае MVC должна соблюдаться независимость V от M в контекстном плане. Им так конечно можно иметь свои методы работы с сервисами или рендерами. Главное, чтобы методы одной части не влияли на методы другой. Иначе будет каша:)

                                                                      • +1
                                                                        > Да модель знает, но не о спецификации API.

                                                                        Именно о них и знает. Вы не можете отсылать сообщение потребителю, если не знаете, какие сообщения он принимает и какова их семантика.

                                                                        > В случае MVC должна соблюдаться независимость V от M в контекстном плане.

                                                                        Еще раз, когда говорите о babylon-MVC, то называйте его babylon-MVC.

                                                                        > Главное, чтобы методы одной части не влияли на методы другой.

                                                                        А это невозможно. Если в области Х вызывается метод Y, то значит в этой области должна быть известна семантика данного метода.
                                                                        • 0
                                                                          Именно о них и знает. Вы не можете отсылать сообщение потребителю, если не знаете, какие сообщения он принимает и какова их семантика.

                                                                          Диспетчер принимает все сообщения. Если те или иные объекты подписаны на них происходит обработка в соответствующих хэндлерах. Эвент это в общем виде — текст+ объект с пропсами, который вы передаете. Сами писали ручками диспетчеры или пользуетесь готовыми?


                                                                          Еще раз, когда говорите о babylon-MVC, то называйте его babylon-MVC.

                                                                          Ещё два-с


                                                                          https://mikkegoes.com/model-view-controller-mvc/

                                                                          Главное, чтобы методы одной части не влияли на методы другой.

                                                                          А это невозможно. Если в области Х вызывается метод Y, то значит в этой области должна быть известна семантика данного метода.

                                                                          Кто же спорит? В области Модели ничего не вызывается
                                                                          В модели размещаются приготовленные сервисными классами данные, также как и в виде размещаются отрендеренные UI. Но управление или проброс осуществляется через контроллер. Вы можете заменить UI независимо от модели и модель не зависимо от UI. Это также не касается конкретной имплементации метода. На этапе проектирования или тестирования у вас могут быть заглушки или прокси объекты.
                                                                          Если знал бы, что понадобится, то сохранил статью этого японца. Давайте не будем возращаться к этому. Делайте что хотите. Хотя по памяти основные мысли вроде точно воспроизвел.

                                                                          • 0
                                                                            > Диспетчер принимает все сообщения.

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

                                                                            > Но управление или проброс осуществляется через контроллер.

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

                                                                              Контроллер знает контексты ноды вида и модели (пробросы). Этого вполне достаточно. Или контроллер получая события определяет контекст ноды и обрабатывает ее хэндлером из соответствующего репо методов модели или вида. Все дела.

                                                                              • 0
                                                                                > Или контроллер получая события определяет контекст ноды и обрабатывает ее хэндлером из соответствующего репо методов модели или вида.

                                                                                Только не MVC, а MVP, и не контроллер, а презентер.
                                                                                • 0

                                                                                  Для старообрядцев — презентер

                                                                                  • 0
                                                                                    www.martinfowler.com/eaaDev/uiArchs.html
                                                                                    2006
                                                                                    aspiringcraftsman.com/2007/08/25/interactive-application-architecture
                                                                                    2007

                                                                                    Нестарообрядцы — это кто? Вчерашние выпускники?
                                                                                    • 0

                                                                                      Контроллер в классическом смысле, используется как источник данных для модели и требует резолвинга с её стороны. Так же как в случае с роутингом и ui вводом. Поэтому появляются такие паттерны как VIPER где "С" в том виде как вы его понимаете даже не упоминают. И правильно. Я же понимаю MVC так как я указал по ссылке. Я не помню когда первый раз услыщал про MVC. PureMVC это был фреймворк который я юзал.

                                                                                      • +1
                                                                                        > Контроллер в классическом смысле, используется как источник данных для модели

                                                                                        Это уже вообще бредни. Никогда и нигде контроллер не используется как «источник данных». Источник данных в MVC сидит за моделью и самим MVC не описывается.
                                                                                        Кроме того, это противоречит даже вашему описанию выше — если контроллер находится _между_ моделью и видом, то уж точно он не может находиться _за_ моделью.

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

                                                                                        > Я же понимаю MVC так как я указал по ссылке. Я не помню когда первый раз услышал про MVC. PureMVC это был фреймворк который я юзал.

                                                                                        Ну то есть какой-то дурак сказал глупость, а вы теперь упорно ее повторяете.
                                                                                        • 0

                                                                                          Упомянутый мною классический контроллер это тот который на рисунке вашем. Куда ведет стрелочка от него? Правильно к модели. Что это как не источник? Но любые данные с клиента должны подтверждаться на стороне сервера. А мое описание было дано для презентера. И это как я уже не раз говорил — прокладка.Нет никакого отдельного MVC на стороне клиента и сервера.
                                                                                          Сервер резолвит данные для клиентских сервисов модели. Cервисы их подготавливают для работы с моделью.

                                                                                          • 0
                                                                                            > Правильно к модели. Что это как не источник? Но любые данные с клиента должны подтверждаться на стороне сервера

                                                                                            Та стрелочка — это не данные с клиента. Там вообще нету ни сервера ни клиента. У вас горячка.
                                                                                            • 0
                                                                                              .Нет никакого отдельного MVC на стороне клиента и сервера.

                                                                                              Там вообще нету ни сервера ни клиента.
                                                                                              Там это где??? Про сервер не я начал…

                                                                                              Та стрелочка — это не данные с клиента

                                                                                              Это данные контроллера источающего данные для модели.


                                                                                              Бред выходит у вас. Если вы даже свои посты отрицаете.

                                                                                              • 0
                                                                                                > Это данные контроллера источающего данные для модели.

                                                                                                Я же говорю — у вас горячка.
                                                                                            • 0

                                                                                              Именно. Стрелочка ведёт от контроллера к модели, стрелочка показывает поток данных, контроллер передаёт модели данные, а не наоборот.

                                                                            • 0

                                                                              Модель не знает об API view или controller. Она предоставляет им своё API, в частности она определяет механизм подписки и доставки сообщений об изменении своего состояния, которым они будут пользоваться. Она определяет его синтаксис и семантику. Вью при подписки на события модели должно строго соблюдать их, для адекватного отображения нужного ему среза стейта модели. Для модели же вью — абстрактный подписчик на её данные по её API, реализующий её интерфейсы типа /App/UI/MVC/Model/EventSubsriberInterface для подписки на её события /App/UI/MVC/Model/Event. Модель знает только как отправить такое своё событие подписчику, реализующему такой её интерфейс и прошедшему её флоу подписки на события. Вью это, логгер, транслятор событий во внешнюю MQ-систему — ей всё равно. Семантика вызываемых методов этого подписчика для модели ровно одна: "я сообщаю в удобной мне форме удобным мне способом подписчику, прошедшую удобную мне процедуру подписки, о том, что со мной случилось такое-то моё событие, в котором он высказал заинтересованность. Я своё дело сделала, обработает ли подписчик это сообщение с той семантикой, которую я в него вложила, мне всё равно".

                                                                              • 0
                                                                                > Модель не знает об API view или controller. Она предоставляет им своё API

                                                                                Это зависит от конкретной реализации. Если вид получает от модели сообщения, то модель знает АПИ вида, по определению. Ведь она знает, какие сообщения ему слать и когда. А это и есть АПИ.
                                                                                • 0
                                                                                  Но ведь вид реализует интерфейс, который предоставляет модель. То есть она знает свой интерфейс, а вид уже подстраивается
                                                                                  • 0
                                                                                    > а вид уже подстраивается

                                                                                    Подстраивается модель.
                                                                                    • 0

                                                                                      Druu умеешь поднять настроение. Только один вопрос. Подстраивание в твоей вселенной это что такое? Каждому программисту — по Катющику.

                                                                                      • 0
                                                                                        Не, подстраивается вид
                                                                                        • 0
                                                                                          > Не, подстраивается вид

                                                                                          Мы уже это обсуждали. В этом случае ваше приложение просто не работает. Вам же нужно получить работающее приложение? Значит, вам придется подстраивать модель.
                                                                                          Например, вашему виду надо вывести некие данные. Модель эти данные не содержит. Вам надо изменить модель (подстроить под вид) так, чтобы она эти данные содержала. Еще вы, конечно, можете прям из вида сделать запрос и эти данные минуя модель получить. Но мы ведь такие варианты не рассматриваем в всерьез?

                                                                                          2VolCh
                                                                                          > Нет, сообщение, механизм подписки и механизм доставки — это API модели.

                                                                                          Механизм подписки и доставки — это апи механизма подписки и доставки. Не модели и не вида. Просто ваша модель и вид не взаимодействуют с механизмом доставки (в том смысле, что не замечают его), они взаимодействуют друг с другом. То, что там есть какой-то промежуточный механизм — они вообще должны быть не в курсе, желательно совсем не в курсе (то что на практике они все-таки об этом в курсе — просто еще один случай протекшей абстракции).
                                                                                          • 0
                                                                                            Механизм подписки и доставки — это апи механизма подписки и доставки. Не модели и не вида. Просто ваша модель и вид не взаимодействуют с механизмом доставки (в том смысле, что не замечают его), они взаимодействуют друг с другом. То, что там есть какой-то промежуточный механизм — они вообще должны быть не в курсе, желательно совсем не в курсе (то что на практике они все-таки об этом в курсе — просто еще один случай протекшей абстракции).

                                                                                            В рамках MVC механизм доставки сообщений не выделяется, он или в модели, или в виде. Поскольку видов много разных, а модель одна, то место ему в модели. Этот механизм часть API модели, модель предоставляет любому виду свой API чтобы любой вид, включая те, которых даже в проекте не было на момент создания модели, мог получить её актуальное состояние. Механизм может быть сторонний по факту, но входить он будет в API модели.


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

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

                                                                                            • 0
                                                                                              > В рамках MVC механизм доставки сообщений не выделяется, он или в модели, или в виде.

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

                                                                                              > Механизм может быть сторонний по факту, но входить он будет в API модели

                                                                                              У вас куча путаницы. Это апи модели в рассматриваемом вами случае будет входить в апи механизма передачи сообщений. Потому что этот механизм будет изо всех сил притворяться самой моделью. Поменяете апи модели — поменяется синхронно и апи передатчика.

                                                                                              > Хоть один практический пример такой ситуации можете привести?

                                                                                              Да постоянно. Любое серьезное изменение требований к подобным вещам и сводится.

                                                                                              > Как могут появиться требования к виду, ссылающееся на что-то, что ранее не появилось в требованиях к модели?

                                                                                              Потому что любые бизнес-требования формулируются как требования к виду (естественно, тот, кто требования формулирует, может и не знать, что это называется видом, но он формулирует требования именно к той части системы, что реализована как вид, если мы используем MVC). Мы же уже это обсуждали. «ваша система должна предоставить возможность клиенту насовать товары в корзину и оповестить менеджера о ее содержимом» — требования к виду.
                                                                                              • 0
                                                                                                Он не выделяется потому, что является деталью реализации и, еще раз, потому что ни вид ни модель вообще про этот механизм знать-то и не должны. Он должен быть прозрачен (максимально).

                                                                                                Они должны о нём знать, иначе как они им будут пользоваться?


                                                                                                У вас куча путаницы. Это апи модели в рассматриваемом вами случае будет входить в апи механизма передачи сообщений. Потому что этот механизм будет изо всех сил притворяться самой моделью. Поменяете апи модели — поменяется синхронно и апи передатчика.

                                                                                                Откуда у вас путаница? Апи модели состоит в рамках MVC из апи для изменения состояния модели, которым пользуются контроллеры, и апи для предоставления состояния модели, которым пользуется виды. Не механизм притворяется моделью, а модель предоставляет механизм, скрывая за ним свою внутреннюю сложность.


                                                                                                Да постоянно. Любое серьезное изменение требований к подобным вещам и сводится.

                                                                                                Любое серьезное изменение требований сводится, вернее разбивается на параллельным изменения требованиям к модели, видам и контроллерам.


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

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


                                                                                                Бизнес-требования к системе разбиваются на:


                                                                                                • что система должна делать, что хранить, что показывать. Это про модель в MVC.
                                                                                                • как пользователь будет управлять системой. Это про контроллер.
                                                                                                • как пользователь будет получать состояние системы, результат действий своих и других пользователей. Это про виды.
                                                                                                • 0
                                                                                                  > Они должны о нём знать, иначе как они им будут пользоваться?

                                                                                                  Вы же пользуетесь АТС, но о ней не знаете. Она для вас прозрачна — вы набрали номер нужного человека и с ним разговариваете. Или лучше представьте себе разговор с человеком по скайпу — там и роутеры и инфраструктура провайдера и бекенд самого скайпа. Но вы не замечаете, что взаимодействуете с ними, вы разговариваете с конкретным лицом.

                                                                                                  > Откуда у вас путаница?

                                                                                                  Не у меня, а у вас.

                                                                                                  > Апи модели состоит в рамках MVC из апи для изменения состояния модели, которым пользуются контроллеры, и апи для предоставления состояния модели, которым пользуется виды. Не механизм притворяется моделью, а модель предоставляет механизм, скрывая за ним свою внутреннюю сложность.

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

                                                                                                  > Любое серьезное изменение требований сводится, вернее разбивается на параллельным изменения требованиям к модели, видам и контроллерам.

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

                                                                                                  > Вот ничего тут о виде вообще.

                                                                                                  Это о виде и только о виде.

                                                                                                  > Это о модели в чистом виде.

                                                                                                  Вы опять путаете MVC-модель и модель предметной области. Аналитик описывает требования к предметной области. Ответственность за выражение предметной области в MVC несет вид. У модели такой ответственности нет, она вообще не про то. Там может не быть ни корзины ни товаров, в рассматриваемом случае.

                                                                                                  > что система должна делать, что хранить, что показывать. Это про модель в MVC.

                                                                                                  Нет, это все про вид.

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

                                                                                                  А вот это как раз про модель.
                                                                                                  • 0
                                                                                                    Вы же пользуетесь АТС, но о ней не знаете. Она для вас прозрачна — вы набрали номер нужного человека и с ним разговариваете. Или лучше представьте себе разговор с человеком по скайпу — там и роутеры и инфраструктура провайдера и бекенд самого скайпа. Но вы не замечаете, что взаимодействуете с ними, вы разговариваете с конкретным лицом.

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


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

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


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

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


                                                                                                    Ответственность за выражение предметной области в MVC несет вид. У модели такой ответственности нет, она вообще не про то. Там может не быть ни корзины ни товаров, в рассматриваемом случае.

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


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


                                                                                                    Нет, это все про вид.

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


                                                                                                    А вот это как раз про модель.

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

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

                                                                                                      При этом данный механизм максимально полностью передает АПИ диалога собеседника.

                                                                                                      > Чтобы модель могла посылать сообщения виду — механизм должен быть.

                                                                                                      Для этого достаточно метод вида дернуть. Это одно и то же.

                                                                                                      > Требования и к модели, и к виду, и к контроллеру создаёт не программист, а аналитик, архитектор.

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

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

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

                                                                                                      > Грубо, модель должна знать данные из какой таблицы базы данных надо отдать абстрактному программному клиенту

                                                                                                      Модель не знает ничего ни про БД ни про ее таблицы. Слой взаимодействия с БД выходит за рамки MVC, он сидит (если вообще есть) где-то за моделью.

                                                                                                      > И в какую табличку что надо записать, когда абстрактный клиент просит добавить товар в корзину. Только она знает, что есть товар, а что есть корзина, напрямую из требований предметной области от аналитика.

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

                                                                                                      > И контроллер, и вид, оперируют теми и только теми терминами предметной области, которые им предоставляет модель.

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

                                                                                                      > Нет, это именно про модель. Фраза «пользователь может добавлять товары в корзину из каталога, после чего создать заказ» не содержит ни слова про вид или контроллер.

                                                                                                      Про модель она тоже ни слова не содержит. Но тот программный модуль, который будет выполнять эту задачу в MVC — это будет вид. Не модель.
                                                                                                      • +1
                                                                                                        Для этого достаточно метод вида дернуть. Это одно и то же.

                                                                                                        Дергание метода — стандартный механизм доставки сообщений от объекта к объекту в мэйнстрим ООП-языках.


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

                                                                                                        Требования к модели создаются тем, кто анализирует и декопомозирует требования к системе в целом, формируя на их базе требования к подсистемам. И создаются они непосредственно от требований к системе в целом, а не через требования к виду. Более того, требования к модели можно создавать не имея требований к видам и контроллерам, не имея ещё требований к тому как система будет взаимодействовать с пользователем, но имея требования того что она должна делать. Физически можно попробовать создавать сначала требования ко всем видам и контроллерам системы, а потом учитывая их создавать требования к модели, но это как создать требования к пульту управления, а потом на их базе создавать требования к телевизору, этим пультом управляемым. То есть в требованиях к пульту писать "при нажатии кнопки первого канала телевизор должен начать показывать изображение системы SECAM, принимаемое на частоте 49,75 МГц", а не "при нажатии кнопки первого канала пульт должен послать сигнал переключения на первый канал".


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

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


                                                                                                        Модель не знает ничего ни про БД ни про ее таблицы. Слой взаимодействия с БД выходит за рамки MVC, он сидит (если вообще есть) где-то за моделью.

                                                                                                        Именно модель и знает. В рамках MVC слой хранения в модели (именно "в", а не "за"). В MVC только три элемента.


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

                                                                                                        К виду требования могут формировать в форме типа "вывести в правом верхнем углу первых 5 элементов из списка, полученного по вызову метода Model.getCartItems(), а вверху вывести результат вызова Model.getCartMethadata.name". ни капли информации о предметной области, только об API модели.


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

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


                                                                                                        Они оперирует теми терминами, в которых с ними взаимодействует пользователь.

                                                                                                        Именно. Пользователь с ними оперирует терминами UI — окна, формы, кнопки, указатели мыши и т. п. Их задача транслировать эти термины в термины модели и обратно.


                                                                                                        При этом в модели нет никаких корзин и товаров. В ней, например, блоб неизвестной для модели структуры (условимся так, давайте, чтобы вам было проще). Модель может отдавать этот блоб виду и применять к нему диффы, которые отсылает контроллер.

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


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

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


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

                                                                                                        • 0
                                                                                                          > Требования к модели создаются тем, кто анализирует и декопомозирует требования к системе в целом, формируя на их базе требования к подсистемам.

                                                                                                          Это требования системе в общем. Требования же к модели в MVC создает конкретно программист и никто кроме него, потому что остальные люди ни о каких MVC (и, с-но, ни о каких mvc-моделях) не в курсе. Вы упорно продолжаете путать модель предметной области и модель MVC. Это РАЗНЫЕ сущности, которые лишь МОГУТ БЫТЬ связаны.

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

                                                                                                          Именно так все и поступают на практике, как мы уже выяснили на конкретных примерах.

                                                                                                          > Именно модель и знает. В рамках MVC слой хранения в модели (именно «в», а не «за»). В MVC только три элемента.

                                                                                                          MVC не описывает все систему. Он описывает только подсистему взаимодействия с пользователем (интерфейс), это его предназначение. Слой взаимодействия с хранилищем просто не включен в MVC, его там нет, он за пределами архитектуры.

                                                                                                          > К виду требования могут формировать в форме типа «вывести в правом верхнем углу первых 5 элементов из списка, полученного по вызову метода Model.getCartItems(), а вверху вывести результат вызова Model.getCartMethadata.name».

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

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

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

                                                                                                          > Именно в модели они и есть.

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

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

                                                                                                          Представьте себе, об этом _по отдельности_ не знает ни один из M, V, C! Только в совокупности. В этом и смысл разделения ответственности в MVC. Контроллер знает, что значит «нажать на кнопку», и что надо дернуть определенным образом модель (отослать дифф). модель ничего не знает про кнопки и окна, но знает, что когда получен дифф — надо оповестить об этом вид. Вид ничего не знает про кнопки и про диффы, но в курсе, что когда он получает новый блоб — надо перерендерить окно согласованно с новыми данными.

                                                                                                          > Знания о том, что нужно делать в ответ на те или иные желания пользователя — только в ней.

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

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

                                                                                                          Все верно, но именно к этим преобразованиям и формулируются требования. Нам совершенно плевать, что там в модели — блобы, корзины, есть ли там методы addToCart или просто голая трансляция данных неизвестной структуры (вы же не будете мне тут всерьез рассказывать о том, что в рассматриваемом случае у нас модель, которая блобы гоняет, действует «в терминах предметной области»? ну ведь правда не будете?). Нам важно чтобы пользователь мог заказать товар (о котором модель ничего не знает).
                                                                                                          • 0
                                                                                                            Это требования системе в общем. Требования же к модели в MVC создает конкретно программист и никто кроме него, потому что остальные люди ни о каких MVC (и, с-но, ни о каких mvc-моделях) не в курсе.

                                                                                                            MVC — архитектурный паттерн. В курсе об MVC прежде всего архитектор, это программисты как раз о нём могут не знать. Архитектор говорит одному программисту разработать модуль A трансляции событий UI в команды модуля Б, второму разработать модуль В трансляции событий модуля Б в графические элементы, а третьему разработать модуль Б, который будет принимать (не важно от кого) по своему API команды, изменять по ним своё состояние и эмитировать для подписавшехся по его же API подписчиков событий об изменении состояния. Вот первому точка входа в модуль Б и список команд (часть API модуля Б), вот второму точка регистрации листенеров и список событий (вторая часть API модуля Б), а третьему обе части и описание бизнес-процессов от аналитика. Все трое даже не знают, что реализуется MVC, но он реализуется и не случайно.


                                                                                                            Именно так все и поступают на практике, как мы уже выяснили на конкретных примерах.

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


                                                                                                            MVC не описывает все систему. Он описывает только подсистему взаимодействия с пользователем (интерфейс), это его предназначение. Слой взаимодействия с хранилищем просто не включен в MVC, его там нет, он за пределами архитектуры.

                                                                                                            Описывает. Как-то так:


                                                                                                            • V — показывает данные пользователю
                                                                                                            • С — обрабатывает действия пользователя на UI
                                                                                                            • M — вся остальная система, предоставляющая свой API для V и C

                                                                                                            Туда входит и слой хранения данных, если он есть.


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

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


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

                                                                                                            Это протечка абстракции по MVC. Вид вообще не должен знать, что может изменить состояние системы, его дело только вовремя эти изменения отображать. Контроллер не должен знать, к каким изменениям приводят действия пользователя и приводят ли, его дело только сообщать о действиях пользователя системе, скрывая детали UI-реализации. Остальная система, модель, знает как менять своё состояние при реакции на те или иные команды от C и как оповещать об изменениях V, которые хотят об этих изменениях знать. Системе всё равно есть ли UI или с ней программные общаются, ей поступила команда "добавить в корзину пользователя А товар Б" и если условия добавления соблюдаются, она добавляет и всем заинтересовавшимся, если они есть, сообщает "в корзину пользователя А добавлен товар Б". Ей всё равно, нажал пользователь кнопку в окне браузера, введена команда в консоли или запущена отдельная программа с единственной функцией дать команду модели "добавить в корзину пользователя А товар Б".


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

                                                                                                            Это вы описываете приложение по работе с блобами, в котором пользователь редактирует блобы, держа всю их семантику при себе? Редактор блобов какой-то? Только в этой ситуации, по-моему, можно говорить одновременно о MVC и посылке диффов от контроллера в модель. Но при этом и контроллер с видом должны воспринимать блоб как блоб.


                                                                                                            Если контроллер и/или вид, знают, что, например, этот blob — application/json представление корзины пользователя, то это не MVC. Это приложение, в котором есть тупое хранилище, а бизнес-логика не отделена от UI, пускай и разделена на модуль вывода и модуль ввода.


                                                                                                            Представьте себе, об этом по отдельности не знает ни один из M, V, C! Только в совокупности. В этом и смысл разделения ответственности в MVC. Контроллер знает, что значит «нажать на кнопку», и что надо дернуть определенным образом модель (отослать дифф). модель ничего не знает про кнопки и окна, но знает, что когда получен дифф — надо оповестить об этом вид. Вид ничего не знает про кнопки и про диффы, но в курсе, что когда он получает новый блоб — надо перерендерить окно согласованно с новыми данными.

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


                                                                                                            Все верно, но именно к этим преобразованиям и формулируются требования.

                                                                                                            Требования к виду формулируются на языке элементов UI и языке модели. Требования к контроллеру — на языке событий UI и языке модели. Требования к модели — на языке модели. Ели модель знает только про блоб, то требования к контроллеру и виду формируются только на языке элементов и событий UI и на языке блобов.

                                                                                                            • 0
                                                                                                              > Описывает.

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

                                                                                                              > MVC — архитектурный паттерн. В курсе об MVC прежде всего архитектор

                                                                                                              Ну так архитектор — это программист и есть.

                                                                                                              > Легко и просто. При модульном тестировании — просто эмулируя API модели. При интеграционном — создав фикстуры для модели или иным образом программно приведя её в тестируемое состояние.

                                                                                                              Интеграционное тестирование с фикстурами вы никак не проведете, только модульное. А на приемочном у вас будут требования к виду, так как модели там нет и она не проверяется.

                                                                                                              > Это протечка абстракции по MVC. Вид вообще не должен знать, что может изменить состояние системы, его дело только вовремя эти изменения отображать.

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

                                                                                                              > Это вы описываете приложение по работе с блобами, в котором пользователь редактирует блобы, держа всю их семантику при себе?

                                                                                                              Нет, это приложение, в котором пользователь формирует корзину товаров и отправляет заказ.

                                                                                                              > Если контроллер и/или вид, знают, что, например, этот blob — application/json представление корзины пользователя, то это не MVC.

                                                                                                              Конечно же, это MVC. С чего бы нет? Все элементы функционируют согласно своим определениям.

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

                                                                                                              Все верно. При этом модель не знает, что там за диффы и что за блобы. Она просто в нужный момент обновляет данные внутри себя и передает новые данные виду, все.

                                                                                                              > Требования к модели — на языке модели.

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

                                                                                                              > Ели модель знает только про блоб, то требования к контроллеру и виду формируются только на языке элементов и событий UI и на языке блобов.

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

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

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


                                                                                                                Ну так архитектор — это программист и есть.

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


                                                                                                                Интеграционное тестирование с фикстурами вы никак не проведете, только модульное. А на приемочном у вас будут требования к виду, так как модели там нет и она не проверяется.

                                                                                                                Как это не проведу? Псевдокод:


                                                                                                                DB.clearData
                                                                                                                DB.insert('fixtures.sql')
                                                                                                                model = new Model(userId = 1)
                                                                                                                view = new SomeView(model = model)
                                                                                                                
                                                                                                                view.show()
                                                                                                                assertInvoked(model.addCartEventListener, listener = view)
                                                                                                                assertInvoked(model.getCartItems, userId = 1)
                                                                                                                
                                                                                                                model.addCartItem(goodsId = 1)
                                                                                                                assertInvoked(view.handleCartEvent, type = 'itemAdded', goodsId = 1)

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

                                                                                                                Вид должен знать нужную ему часть API модели, должен знать как ему получать нужные ему данные из модели. Для модели же в худшем случае, вид — это клиент, который подписался по её API на сообщения об изменении стейта, и которому она о них сообщает. максимум что она знает о нём — это то, что реализует её интерфейс подписки на события. И это как раз уменьшает связанность. Это цель отделения в MVC M от VC — отсутствие зависимости модели, ядра приложения от его UI: пока разработчики ядра не изменяют API модели, они могут как угодно менять логику этого ядра. Могут прятать за моделью фикстуры, могут эмулятор адронного коллайдера, могут сам адронный коллайдер, не то что работу интернет-магазина


                                                                                                                Нет, это приложение, в котором пользователь формирует корзину товаров и отправляет заказ.

                                                                                                                Значит это или не MVC UI, или вы называете моделью слой хранения, не подозревая, что на самом деле модель у вас в каком-то адаптере вашей модели для вью и контроллера, который трансформирует блобы в товары в корзине и обратно. Из оригинального определения MVC (1979): "Models represent knowledge. A view is a (visual) representation of its model." Знания (предметной области) в модели, вид лишь визуализирует их. Модель должна знать, например, что нельзя помещать в корзину товар, которого нет на складе, какой бы "дифф" ей не прислал контроллер. И должна послать виду каталога сообщение о том, что на один товар стало меньше, а виду корзины, что на один больше.


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

                                                                                                                MVC про UI, где требования к M — это требования к её API, её контракту, её пред- и пост-условиям, её инвариантам. Грубо, "вызов метода cartModel.addItem(itemId) должен выбрасывать исключение, если на складе нет доступных товаров c этим id." Чистое требование к модели, которое можно проверить и без наличия V и C, хоть модульным тестированием, застабив склад, хоть интеграционным, занеся в базу нулевое количество.


                                                                                                                Это вы уже свое что-то выдумываете. Еще раз — требование к виду/контроллеру это предоставить пользователю возможность собрать и отправить корзину.

                                                                                                                Это требование к системе в целом. Из них в требования к модели входит примерно такие действия:


                                                                                                                • хранить и выдавать список доступных товаров на складе
                                                                                                                • хранить, выдавать и изменять корзину пользователя, при этом при изменениях менять остатки на складе, не допускать отрицательных остатоков, не допускать отрицательных количеств в корзине, не допускать превышения суммы заказа предела установленного ЦБ РФ для наличного расчёта и прочая, и прочая, и прочая.

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

                                                                                                                Какое это имеет отношение к MVC? Программные системы взаимодействуют по API, а MVC это про UI, про human–computer interaction, man–machine interface

                                                                                                                • 0
                                                                                                                  > За каким из этих слоев может находиться слой персистентности?

                                                                                                                  За моделью, конечно. Я же об этом и сказал. Смысл том, что в саму модель он не входит. Он где-то _за_ ней.

                                                                                                                  > Нет. Архитектор — постановщик задач программистам.

                                                                                                                  Тогда архитектор ничего не знает про MVC. А решение о том, что: «мы будем использовать mvc» — это уже роль программиста, в чистом виде.

                                                                                                                  > Как это не проведу? Псевдокод:

                                                                                                                  Ну вот ваш псевдокод — это модульный тест. Вы не проверили интеграцию. Любой assertInvoked гарантирует, что вы заменяете тест интеграции модульным. В интеграционных текстах никогда нету assertIvoked.

                                                                                                                  > Вид должен знать нужную ему часть API модели, должен знать как ему получать нужные ему данные из модели.

                                                                                                                  Дело даже не в знании. Дело в том, что модель ВСЕГДА проектируется с учетом нужд вида. Иначе будет кусок чего-то вонючего вместо модели, работать с которой потом будет невозможно.

                                                                                                                  > Это цель отделения в MVC M от VC — отсутствие зависимости модели, ядра приложения от его UI

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

                                                                                                                  > Из оригинального определения MVC (1979): «Models represent knowledge. A view is a (visual) representation of its model.»

                                                                                                                  Ну вот в данном случае мы это и имеем. Модель предоставляет блоб с данными (знания), а вид — их преобразует к понимаемому пользователем формату. И никто от модели MVC не требует знать структуры этих данных.

                                                                                                                  > MVC про UI, где требования к M — это требования к её API, её контракту, её пред- и пост-условиям, её инвариантам.

                                                                                                                  Но это все требования у УИ. Все требования к системе — это требования к поведению УИ.

                                                                                                                  > Грубо, «вызов метода cartModel.addItem(itemId) должен выбрасывать исключение, если на складе нет доступных товаров c этим id.»

                                                                                                                  А такого требования не будет. Будет требование: «в случае попытки добавления в корзину отсутствующего на складе товара, пользователь должен получить отказ». Это требование к виду, не к модели.

                                                                                                                  > Это требование к системе в целом.

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

                                                                                                                  > Какое это имеет отношение к MVC?

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

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


                                                                                                                    Тогда архитектор ничего не знает про MVC. А решение о том, что: «мы будем использовать mvc» — это уже роль программиста, в чистом виде.

                                                                                                                    Это роль архитектора. MVC — архитектурный паттерн.


                                                                                                                    Любой assertInvoked гарантирует, что вы заменяете тест интеграции модульным.

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


                                                                                                                    Дело даже не в знании. Дело в том, что модель ВСЕГДА проектируется с учетом нужд вида. Иначе будет кусок чего-то вонючего вместо модели, работать с которой потом будет невозможно.

                                                                                                                    С учётом, да. Если приложение в целом должно что-то показать, то это модель должна отдавать через свой API. Но если виду хочется, например, чтобы модель отдавала ФИО в нескольких форматах, то это попытка нарушить границы ответственности. Модель отдасть полное ФИО, а уж вид будет преобразовывать в отдних случаях в Иванов И. И., а в других в уважаемый Иван Иванович, ещё и пол из модели проанлизировав.


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

                                                                                                                    Да как же прибиваю, если модель знает о виде только то, что он реализует интерфейс, который является частью API модели?


                                                                                                                    Ну вот в данном случае мы это и имеем. Модель предоставляет блоб с данными (знания), а вид — их преобразует к понимаемому пользователем формату. И никто от модели MVC не требует знать структуры этих данных.

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


                                                                                                                    Но это все требования у УИ. Все требования к системе — это требования к поведению УИ.

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


                                                                                                                    А такого требования не будет. Будет требование: «в случае попытки добавления в корзину отсутствующего на складе товара, пользователь должен получить отказ». Это требование к виду, не к модели.

                                                                                                                    Это требование к системе. Оно будет декомпозировано на требования к модели "должно бросить исключение ZeroResidue при попытке добавления товара с нулевым остатком" и на требование к UI вывести сообщение об ошибке "Нулевой остаток" при появлении исключения ZeroResidue. Кстати, именно в модели будут знания о том, что такое нулевой остаток, в каких условиях он может появиться. И вполне может оказаться, что эти условия будут меняться, если бизнес, например, решится на оверселл. К UI требование не остатки проверять, а показывать сообщения при исключении в модели, не более.


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

                                                                                                                    Слой взаимодействия системы с внешним миром не только UI. Пример с письмом я уже приводил — это взаимодействие с SMTP сервером, а не пользователем. Ещё может быть требование "записать данные о продаже в базу данных учётной системы", "отправить чек в систему фискальной службы" и т. п. Сложные системы взаимодействуют не только с пользователем, но и с другими системами. Есть действия, которые в UI вообще не отображаются, типа сбор данных которые сейчас никак не обрабатываются, просто пишутся в базу на всякий случай, например по требованию закона или на перспективу внедрения анализа пользовательского поведения. У вас систему не примут, если приемщик сделает SQL запрос и не увидит там, например, IP с которогоо был заказа, хотя он нигде в системе не показывается.


                                                                                                                    Совершенно прямое. Нет никакой разницы является ли вам интерфейс интерфейсом между двумя сервисами или между человеком и компьютером. Это в обоих случаях одинаково MVC.

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

                                                                                                                    • 0
                                                                                                                      MVC — это про пользовательские интерфейсы, не про программные. Можно пробовать применять, но это использование не по назначению, другие методы организации API скорее всего будут намного более эффективны.

                                                                                                                      Не надо превращать MVC из парадигмы проектирования в парадигму программирования. Никчему это.

                                                                                                                      Druu же считает что всё что не Модель, то Вид. А куда девать современные CMS которые спроектированы с учётом принципов MVC

                                                                                                                      • 0
                                                                                                                        > Входит в рамках MVC.

                                                                                                                        Это ваша выдумка. Давайте будем понимать под MVC то, что под ним принято понимать, а не выдумки? Есть ведь статьи, в которых есть описание. Вы сами на эти статьи ссылались. А теперь несете отсебятину. Зачем?

                                                                                                                        > Отдельного слоя может и не быть, модель прямо с базой сожет работать.

                                                                                                                        Может и работать. Может вообще все во дну кучу быть свалено. Но в MVC в ответственность модели не входит работа с хранилищем, вот и все.

                                                                                                                        > Это роль архитектора. MVC — архитектурный паттерн.

                                                                                                                        MVC — это деталь реализации. Решение о применении MVC принимает программист, потому что принятие такого решения — часть непосредственно написания программы. Это задача программиста. Вы, конечно, можете называть его как угодно, архитектором или еще как.

                                                                                                                        > Да как же прибиваю, если модель знает о виде только то, что он реализует интерфейс, который является частью API модели?

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

                                                                                                                        > Не путайте данные и знания. Знания это как раз понимание формата данных.

                                                                                                                        В процитированном вами тексте ничего не было про знание в смысле понимания формата данных и прочие подобные вещи. Без отсебятины.

                                                                                                                        > В юнит-тестах я бы просто замокал один из модулей.

                                                                                                                        Так вы и замокали, по-этому у вас юнит-тест получился. В интеграционных assertInvokov не бывает.

                                                                                                                        > Но если виду хочется, например, чтобы модель отдавала ФИО в нескольких форматах, то это попытка нарушить границы ответственности.

                                                                                                                        Это уже несущественные детали отображения. Важно то, что если вид требует выводить информацию о пользователе, то модель его должна предоставить, вот и все. По-этому модель учитывает нужды вида, иначе потом это нельзя использовать. Например, вы будете эти данные пользователя получать какими-то кусками при помощи АПИ модели, которое вообще-то предназначено для каких-то других задач. Что будет адъ и содомия.

                                                                                                                        > Нет. UI — это механизм взаимодействия системы и пользователя, а не система.

                                                                                                                        А любые требования к системе и формулирются исключительно в виде описания взаимодействия с пользователем. Как иначе-то?

                                                                                                                        > Банально в требованиях к UI не напишешь «при нажатии на кнопку должно отправляться письмо менеджеру».

                                                                                                                        Как это? Именно так и напишешь. Почему нет?

                                                                                                                        > UI не оперирует понятием «отправка письма», это понятие совсем другого уровня по сравнению с экранами, окнами, мышами и т. п.

                                                                                                                        УИ оперирует теми терминами, которыми оперирует пользователь. Если пользователь подразумевает отправку письма, то УИ оперирует терминами отправки письма. УИ общается с пользователем в понятных ему терминах, иначе это провал, знаете ли, в виде очень, ОЧЕНЬ плохого УИ.

                                                                                                                        > Это требование к системе. Оно будет декомпозировано на требования к модели «должно бросить исключение ZeroResidue при попытке добавления товара с нулевым остатком»

                                                                                                                        То, что кто-то будет куда-то пробрасывать исключение — деталь реализации, которая находится в ответственности программиста.
                                                                                                                        То есть, еще раз — аналитик формулирует требования ко взаимодействию системы с пользователем (то есть, к УИ). Потом программист (ну или если хотите называйте его архитектором) уже преобразует это к требованиям для конкретных модулей системы. Если выбрана MVC то требования аналитика впрямую преобразуются к требованиям вида/контроллера MVC, на основе этих требований к виду контроллеру формируются требования к модели (и, возможно, что всем или части этих требований модель _уже_ удовлетворяет). И потом приступаем к реализации.

                                                                                                                        > Слой взаимодействия системы с внешним миром не только UI.

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

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

                                                                                                                        Ну как, вот есть у вас сервер, который принимает хттп-запросы и отдает хтмл-страницы. Это классическое MVC, вы хотите сказать, оно тут «не по назначению»?
                                                                                                                        • 0
                                                                                                                          MVC — это деталь реализации.

                                                                                                                          druu заканчивайте с демагогией. Сил уже нет понимать ваши комментарии.

                                                                                          • 0
                                                                                            Подстраивается модель.

                                                                                            Зависит от того изменяет ли Вид Модель. Это не запрещено.

                                                                                        • 0

                                                                                          Нет, сообщение, механизм подписки и механизм доставки — это API модели. Как, например, интерфейсы DOM Event, EventTarget и EventListener — это API DOM, а не API вашего кода. Чтобы получать события DOM вы должны вызвать метод EventTarget.addEventListener(string type, EventListener obj), где type интересующие вас события, а obj — ваш объект, реализующий интерфейс DOM EventListener.


                                                                                          EventListener — это чей API, DOM или вашего кода? Знаете вы способы указать DOM использовать не метод handleEvent, а ваш invokeEventHandler, передавать ему не Event, а массив?


                                                                                          Как по мне, то это API DOM. Он диктует как подписаться на события, в частности какие колбэки он примет и как будет их использовать для оповещения. Это API DOM, этого его контракт, изменить который разработчики софта, работающего с DOM могут только как-то повлияв на требования к системе в целом на уровне W3С, WHATWG и т. п. Спихнуть неработоспособность вашей системы на то, что DOM при событиях упорно вызывает handleEvent и передаёт ему Event, а не вызывает ваш invokeEventHandler и не передаёт ему массив с вашей структурой не получится, пока вы не измените требования к реализациям DOM, пока не измените стандарт DOM, да и в этом случае сомнительно, что получится. Ну, если вы разработчик GMail у вас есть некоторые шансы, что в одном из основных браузеров DOM таки будет присылать события так вам хочется, но даже в этом случае я бы не стал на них рассчитывать.

                                    • 0

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

                                      • +1

                                        Если модель дана как есть и какие-то EventEmitter'ы встроить не можем, то построил бы параллельное модели дерево прокси-объектов, фиксирующих вызов мутаторов модели, чтобы иметь после вызова мутатора полный список затронутых нод, чтобы перерендерить нужные виды.

                                        • –1

                                          Не проще ли при update вида или модели определить контексты затронутых данных в модели или виде и из контекста определить функцию обработки в которую передать контекст с затронутыми данными? Другими словами, нода модели или вида должна иметь ссылку на свои обработчики в контроллере. При этом сам контроллер не иерархичен.Update может быть как синхронным, так и асинхронным.

                                          • 0

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

                                            • –1

                                              Собственно предлагаю тоже самое:


                                              var contoller = {
                                                  "Model": {
                                                      "key1Model":[ key1ModelHandlers],
                                                      "key2Model":[key2ModelHandlers]
                                                  },
                                                  "View": {
                                                      "key1View":[key1ViewHandlers],
                                                      "key2View":[key2ViewHandlers]
                                                  }
                                              }
                                              
                                              • 0

                                                Совсем не то же самое. У вас хардкорд, а я о динамическом, в рантайме определении вью, интересующихся той или или нодой модели.

                                                • 0

                                                  В чем вы увидели нединамизм? Во вью и моделях нет собственных обработчиков. Они все работают через контроллер. Причём в обе стороны.

                                                  • 0

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

                                                    • 0

                                                      А где вы увидели модель у меня? Если вас смутило название "Model", то это хэндлеры для Модели или Вью.
                                                      У них ничего общего друг с другом нет. Вью и Модель работает с Контроллером, а не друг с другом. Причём работа может быть асинхронной или синхронной. В первом случае логично использовать эвенты.
                                                      Я не услышал ответа на вопрос. Вместо этого вы предложили прокси объекты. Я уже грешным делом подумал о сигналах, которые без контроллера уведомляют о действиях и доставляют данные. https://github.com/millermedeiros/js-signals/wiki/Comparison-between-different-Observer-Pattern-implementations

                                                      • +1

                                                        Ну, собственно это классическая реализация MVC:


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

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


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

                                                        • 0

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

                                                          • +1
                                                            Если у вас вид будет работать с моделью напрямую, то она будет зависеть от неё, и это не MVC

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

                                                            Там же, на википедии:
                                                            Среднестатистический ТТУК (контроллер) получал данные из БД (используя уровень абстракции базы данных, делая вид, что это модель) или манипулировал, проверял, записывал, а также передавал данные в Представление. Такой подход стал очень популярен потому, что использование таких контроллеров похоже на классическую практику использования отдельного php-файла для каждой страницы приложения

                                                            А также:
                                                            Контроллеры же, — как элементы информационной системы, — ответственны лишь за:

                                                            — приём запроса от пользователя;
                                                            — анализ запроса;
                                                            — выбор следующего действия системы, соответственно результатам анализа

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

                                                              — приём запроса от пользователя;
                                                              — анализ запроса;
                                                              — выбор следующего действия системы, соответственно


                                                              Да есть "терминальный" взгляд на контроллер. У меня он не только терминальный. Я и на Модель и на Вид имею свой вид:) Ну и что. Не надо превращать MVC из парадигмы проектирования в парадигму программирования. Никчему это.

                                                              • 0
                                                                Так это все лишь ваш взгляд на MVC или у вас есть авторитетные источники?
                                                                • –1

                                                                  Я сам для себя авторитет. Мне интересно самому с собой. Возраст позволяет.
                                                                  Когда подрастёте, то поймете о чём я. Да принцип php весьма неплох.

                                                                  • –1
                                                                    У тебя просто от старости мозг усох, дедуля, раз опустился к апелляции к возрасту. Твой возраст, кстати, скрыт в профиле.
                                                                    • –1

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

                                                                      • –1
                                                                        У тебя я бы даже время не спрашивал.
                                                                        • –2

                                                                          Что-то ты не в духе. Попрыгай перед сном. Работа в дегенеративной компании над диструктивными сюжетами видимо накладывает отпечаток на психику. Кстати часы я не ношу.

                                                            • 0

                                                              Клиент зависит от сервиса, а не наоборот. Если я в виде пишу user.getFullname() то это вид зависит от модели, а не наоборот.


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

                                                              • –1

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

                                                                • 0

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


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

                                                          • +1
                                                            > Вью и Модель работает с Контроллером, а не друг с другом.

                                                            Это тогда не mvc. В mvc вид и модель обязательно работают именно друг с другом, а контроллер с видом вообще может никак не взаимодействовать.

                                                            > У меня он не только терминальный. Я и на Модель и на Вид имею свой вид:)

                                                            Вы можете придумать любую архитектуру и назвать ее, например, «babylon-MVC». Но если вы говорите просто «MVC», то извольте использовать общепринятое определение, иначе вас просто люди не поймут.
                                                            • –1
                                                              Вы можете придумать любую архитектуру и назвать ее, например, «babylon-MVC».

                                                              Спасибо за идею, но название у неё более оригинальное. babylon уже есть, если что.

                                                              • 0

                                                                https://mikkegoes.com/model-view-controller-mvc/ — похоже на моё представление об MVC. Немного упрощённое.

                                          • 0
                                            Я использую rx и счастлив, в целом то.

                                            Буквально вчера услышал от одного такого адепта Rx, у которого "нет проблем ни с Rx, ни с Angular", когда обратился к нему за помощью: Не знаю что делает shareReply, но Rx надо использовать по минимуму, тогда не будет этих проблем. Ну ничего, пол дня поковырялся в этой головоломке и таки разобрался. Оказалось, что из-за того, что у shareReply не было сабсрипшенов, то события пролетали в /dev/null не доходя до него. А когда сабскрипшены появлялись, то событиям в стриме уже было не от куда взяться.


                                            Типичный костыль с Rx сейчас у меня выглядит так:


                                            .pipe(
                                            
                                                // в случае ошибок стрим разрушается, поэтому ошибки передаём как данные и везде проверяем
                                                // нас интересуют изменения не всех полей, а конкретных
                                                map( (data) => ( data instanceof Error ) ? data : { foo : data.foo , bar : data.bar } ) ,
                                            
                                                // игнорируем обновления, если интересные нам поля не изменились
                                                distinctUntilChanged( ( a, b )=> JSON.stringify(a) === JSON.stringify(b) ) ,
                                            
                                                // игнорируем множественные последовательные обновления
                                                debounce(0) ,
                                            
                                                // не исполняем стрим с самого начала для каждого нового подписчика
                                                shareReplay(1) ,
                                            )
                                            • +1
                                              Кажется, вы пытаетесь сделать $mol из rx — отсюда и такие «типичные» костыли…
                                              • 0
                                                > Не знаю что делает shareReply, но Rx надо использовать по минимуму, тогда не будет этих проблем.

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

                                                > Оказалось, что из-за того, что у shareReply не было сабсрипшенов, то события пролетали в /dev/null не доходя до него. А когда сабскрипшены появлялись, то событиям в стриме уже было не от куда взяться.

                                                shareReplay(n) эмитит последние n значений при подписке. Вы либо просто не прочитали описание функции, либо что-то выдумываете. Либо под дурочка косите.

                                                > Типичный костыль с Rx сейчас у меня выглядит так:

                                                Если он у вас типичный, то в чем проблема выделить его в отдельную пайп-функцию и горя не знать?
                                                • 0

                                                  mayorovp я всего лишь решаю проблемы, которые давно решены в $mol_atom, MobX и куче других библиотек, но услужливо рабросаны по всем Rx-у.


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

                                                  Да там человек топит за redux. Мол, Засунем весь стейт в глобальную переменную, обложимся экшенами и на каждый чих будем синхронно пушить состояние во все компоненты. А я только впендюрил ChangeDetection.OnPush везде, чтобы этого не происходило, ибо всё страшно тупило.


                                                  shareReplay(n) эмитит последние n значений при подписке. Вы либо просто не прочитали описание функции, либо что-то выдумываете. Либо под дурочка косите.

                                                  Очень легко строить из себя умного, когда вам расписали пробелему и почему она происходит. Вот инвестигировать её не так-то просто, когда после рефакторинга тесты начинают валиться со странными сообщениями. И проблема тут не в 3 тупых головах, а в инструменте, который вместо того, чтобы гарантировать инварианты над состояниями, заставляет играть в railroad tycoon прыгая между файлами и засекая кто когда подписывается и кто когда отправляет данные.


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

                                                  Типичный, да с вариациями. Да и цена этих костылей — снижение производительности. Я пока удерживаю себя от соблазна позаворачивать все 100500 операторов в подобные обёртки.

                                                  • 0
                                                    Проблема не в Rx. Проблема в том, что у Rx другая парадигма — но вы продолжаете думать в привычной.
                                                    • 0
                                                      > Очень легко строить из себя умного, когда вам расписали пробелему и почему она происходит.

                                                      Не надо строить из себя умного, надо просто читать документацию. Если используете ф-ю — ну прочитайте как она работает. Тогда не будет неожиданностей. Ведь то, о чем вы пишете, это не какие-то специфические особенности потрохов, на которые вы случайно наткнулись — это базовое поведение.
                                                      • 0
                                                        Ну, раз вы такой умный и начитанный, то наверняка знаете, почему вместо shareReplay нужно использовать комбинацию из publishReplay и refCount?
                                                        • 0
                                                          Это кто вам сказал такую глупость, что нужно?
                                                          • 0
                                                            Chrome Dev Tools, GitHub Issue, Source Code Review.
                                                            • 0
                                                              Так вот как раз по этим причинам, не нужно. Нужно использовать как раз shareReplay, если хотите консистентное поведение.
                                                              • 0
                                                                И чтобы память текла как дырявое корыто. By design. github.com/ReactiveX/rxjs/issues/3034
                                                                • 0
                                                                  При чем тут текущая память? ShareReplay так и должен работать. Это _до фикса_ он работал неправильно и отписывался, когда отписываться не надо. Чем вам удивительно текущее (корректное) поведение совершенно непонятно. Та подписка, от которой вы отписались — она отписана, все окей. Во втором случае — это отписка от сабжекта, созданного при помощи shareReplay(). Подписка же самого shareReplay на оригинальный барклик никуда не девается и не должна.
                                                                  Вы выдумываете какие-то мокрые мечты о том, как что-то должно работать (причем мечты неконсистентные), и потом обвиняете кого-то в том, что ф-я ведет себя корректно вместо того, чтобы быть сломанной. Ну это что-то совсем странное, согласитесь.
                                                                  • 0
                                                                    Вы бы хоть документацию почитали: github.com/Reactive-Extensions/RxJS/blob/master/doc/api/core/operators/sharereplay.md
                                                                    Или хотя бы подумали, в каких случаях может потребоваться, чтобы память текла по умолчанию.
                                                                    • 0
                                                                      Зачем вы читаете документацию 4 версии, если речь о фиксе из пятой? Признайтесь честно, вы троллируете?

                                                                      > Или хотя бы подумали, в каких случаях может потребоваться, чтобы память текла по умолчанию.

                                                                      Никакая память не течет, не выдумывайте. Все подписки освобождаются, как и должно быть.
                                                                      • 0
                                                                        Затем, что вы утверждаете, что в 4 поведение было ошибочным и в 5 его «починили».

                                                                        А, ну раз вы так сказали, то никаких утечек нет, ага.

                                                                        До чего фронтендеры докатились. Уже утечки памяти считают нормальным явлением. И правда, кому могут потребоваться приложения, которые могут работать дольше полу часа без перезагрузки?
                                                                        • 0
                                                                          > Уже утечки памяти считают нормальным явлением.

                                                                          Зачем вы врете? Там нет никаких утечек. Если бы использование методы действительно давало утечки, уже бы был сделан фикс.

                                                                          Утечка — это когда у вас есть ресурс, который висит и который нельзя освободить (например, память которую вы выделили, и на которую просрали указатель). В рассматриваемом случае вам никто не мешает все требуемые подписки закрыть. Если вы, конечно, САМИ этого не сделали — то будет утечка, но кто кроме вас виноват? Вы бы еще new назвали утечкой само по себе.

                                                                          > Затем, что вы утверждаете, что в 4 поведение было ошибочным и в 5 его «починили».

                                                                          Все так и есть. В 4 поведение было ошибочным. Метод не должен съедать буфер и не должен закрывать подписку сабжекта на оригинальный обсервабл, потому что это подписка вообще вне его юрисдикции. Было бы поведение правильным и ожидаемым — никто бы его не менял.
                                                                      • 0

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

                                                                        • 0
                                                                          Проблема в том, что там нет никаких утечек. Просто есть метод, который до фикса работал неправильно (и неконсинстентно с тем же share), а теперь работает правильно и консистентно. Так вышло, что теперь он не убирает подписки, которые изначально _и не должен_ был убирать (их надо было убирать самому). Сделали фикс, чем нарушили обратную совместимость, люди, которые закладывались на исправленное неконсистентное поведение закономерно недовольны. А vintage просто троллирует.
                                        • +16
                                          Уважаемый автор, рискну предположить что на вас здесь не поймут, ибо вы затрагиваете вопросы религии. :) Я поясню о чем я:

                                          Проблема в том что в мире современной разработки в среднем по индустрии возраст разработчика 25-29 лет, а стаж от 2-5 лет. Из личного опыта поиска сотрудников, в мире фронтэнда, в среднем можно сбросить пару лет стажа и возраста (https://insights.stackoverflow.com/survey/2016)

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

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

                                          На первую полосу у них почему то выходит не решение проблем людей и бизнеса, а fun, emoji в консоли и hover анимации. Кстати, не стоит забывать что тот же Фейсбук в большинстве своем разрабатывает поколение Фейсбук, а Цукерберг был обычным PHP-шником которых гнобят и хоронят за их «говнокод» уже второй десяток лет (Хотя проблема понятное дело не в языке, проблема в комьюнити и индустрии). Конечно как и везде, в Facebook есть хорошие инженеры. c'est la vie.

                                          Из личного же опыта Redux в итоге вызывает больше проблем, и не решает то о чем заявлено. Причем не у меня лично а у коллег фронтэндщиков, которые привели его в проект примерно так: "- Ура, мы начинаем новый проект, теперь мы сделаем все правильно, реактивно, и офигенно. У нас будет сложная админка, а значит нам срочно нужен React и Redux. Собирать все это будет webpack и мы еще прикрутим иммутабельность и реактивность". Как итог, изменение требований к более или менее средней форме или куску интерфейса занимает несколько недель и то и месяц разработки (в попытках понять, а как же это сделать на Redux + React + Saga + Thunk + Redux Form +… «правильно» и чтобы работало. Разработка превращается в забивание костылей, потому что «так не работает», вот так «нарушен источник правды», ну а так «в реакте не принятно»), а разобраться в том что происходит в приложении практически невозможно.

                                          Я в этом плане солидарен с Дэном Абрамовым который сказал, "- Если вы точно не знаете зачем вам Redux, скорее всего он вам не нужен. Не используйте Redux пока у вас нет проблем в React".

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

                                          PS И кстати о Redux приложениях, я до сих пор не смог найти примера более менее крупного приложения на нем содержащего хотя бы пару тройку ограниченных контекстов, что-нибудь типа CRM, CMS или ERP. Что-нибудь сложнее todo или типичного проекта из пары десятков файлов. Буду крайне признателен если кто-нибудь сбросит хотя бы парочку ссылок на среднего размера проекты на React/Redux.