Основная суть MV-образных паттернов заключается в разделении бизнес-логики от интерфейса.
MVC
Что такое MVC
Представим такую ситуацию, что у нас есть приложение, которому нужно будет заменить интерфейс и сохранить как прошлое, так и новое отображение интерфейса. Но до этого мы реализовывали приложение в полной связности с бизнес-логикой. То есть BL находится прямо внутри UI и неразрывно с ним связана.
MVC же подразумевает то, что мы заранее выделили логику подсчёта всех значений (cos, sin, pow) в отедльной сущности (aka model), а уже будем вызывать эту логику из самого интерфейса (aca view) через связующее звено (aka controller)
Если приводить пример из реальной жизни, то когда мы взаимодействуем с банком, то обращение идёт не напрямую с деньгами, а через определённых посредников.
View - сотрудник банка или банкомат, через который мы делаем определённые запросы на операции в банке
Controller - посредник для банкомата или сотрудника банка, через которых они отправляют запросы на выполнение операций
банкомат вызывает заранее подгтовленные функции по выполнению операций над нашим счётом
работник обращается к корпоративному приложению для выполнения операции
Model - внутреннее устройство банка, которое выполняет все операции над счётом клиента
MVC подход используется у нас на любом этапе приложения. Начиная от клиент-серверного взаимодействия и бэкэнда, заканчивая архитектурой приложения на клиенте (web, мобильного, декстопного)
Пример MVC на NodeJS
Реализуем приложение на Express и шаблонизаторе HandleBars
Первое, что мы сделаем - это во view напишем логику отображения в шаблонизаторе.
view / user.hbs
view / user-error.hbs
Далее нам нужно будет описать модель пользователя, в которой находится интерфейс этого пользователя и прямые методы для работы с ним. Тут находится только логика работы с пользователем.
domain / user / model.js
Далее опишем контроллер для работы с моделью. Он будет давать нам контролируемую обёртку над моделью, в которой будут находиться все проверки и взаимодействие с внешним миром.
domain / user / controller.js
Ну и так же мы можем реализовать множество различных способов взаимодействия с нашей моделью. Модель просто является источником истины, которую можно провайдить различными способами через наш контроллер: с помощью REST API, SOAP или того же GraphQL
domain / user / rest-controller.js
domain / user / soap-controller.js
Ну и далее нам остаётся только поднять сервер
server.js
Пример клиентского приложения с MVC
Корневой HTML, в который подключаются скрипты
index.html
Описываем интерфейсы для View, Model, Controller
types / controller.ts
types / model.ts
types / view.ts
Общение через контроллер
Первый модуль у нас будет общаться по стандартной схеме, когда все потоки данных между View и Model контролирует Controller
Вот сама модель счётчика
modules / counterTwo / CounterTwoModel.ts
Тут уже находится контроллер, который дёргает запросы из модели
modules / counterTwo / CounterTwoController.ts
А тут уже во View мы вызываем только методы контроллера
modules / counterTwo / CounterTwoView.ts
Общение по кругу
В этом примере Controller отдаёт данные из Model во View, а View, в свою очередь, передаёт данные напрямую в Model
Это модель счётчика. Она уже заранее знает про существование View и уже работает непосредственно с данными, которые находятся внутри View.
Основной особенностью тут является то, что Model изменяет данные внутри себя и уведомляет View о том, что эти данные изменились.
modules / counter / CounterModel.ts
Это контроллер, который оборачивает в себе логику модели. Он принимает в себя модель и вызывает напрямую его методы
modules / counter / CounterController.ts
Это View, в котором мы передаём методы Model в Controller
modules / counter / CounterView.ts
Общение View и Controller с Model
Далее опишем модель, где и View, и Controller общаются с Model напрямую
Тут мы уже делаем максимально валидный пример, когда в Controller мы передаём Model, а во View Controller. По такой регрессии мы реализуем полностью заменяемые модули, которые должны соответствовать просто по интерфейсам для взаимного переиспользования.
Далее опишем модель пользователя, внутри которой будет логика работы с пользователем и фильтрации списка пользователей
modules / user / UserModel.ts
Контроллер же оборачивает логику модели и предоставляет её нашей вюшке
modules / user / UserController.ts
А тут уже находится логика View, которая в себе использует контроллер и методы из модели пользователя.
через контроллер мы получаем актуальные значения из модели
через методы модели мы заносим новые значения
modules / user / UserView.ts
Тут мы уже собираем наше приложение со всеми моделями и контроллерами. Основной метод mount() в каждом из них монтирует View на странице
mount.ts
Итог
MVC представляет из себя подход, в котором мы делим бизнес-логику и представление. Это деление позволяет нам легко заменять разные части приложения друг другом.
Модель - является единственным источником истины для приложения
Контроллер - является интерфейсом, которым пользуется представление, для обработки и передачи данных в модель
Представление - это непосредственно логика интерфейса пользователя, которая пользуется контроллером.
MVVM
Этот паттерн применим только к графическим интерфейсам
Тут у нас появляется сущность ViewModel вместо Controller
То есть такой подход говорит нам связывать данные напрямую из view с model с помощью двустороннего связывания (как v-bind во Vue или банан в коробке [()] Angular)
И последовательность в таком приложении достаточно короткая - связываем данные из View с ViewModel, где VM общается по обновлению и уведомлению с Model, чтобы связанные данные обновили интерфейс View
MVP
MVP - это паттерн в котором мы полностью убираем логику из View и вызываем обновления изнутри Presenter
Презентер полностью управляет взаимодействием модели и представления. Представление отправляет события (например, нажатие кнопки) в презентер, который затем взаимодействует с моделью и обновляет представление.