Комментарии 392
В ней «Модель» — это данные в ОЗУ, объект, скорее всего составной, возможно содержащий в себе множество объектов (список, коллекцию).
«Вид» — умеет отображать на экране «Модель», берёт некое «стандартное представление» и отрисовывает его на экране.
«Данные» — занимаются вводом/выводом из/в файлов, умеет преобразовывать файл в «стандартное представление модели» и наоборот,
При этом общение все трёх компонентов идёт через интерфейсы, которые, собственно, и оказываются наиболее «неподвижной» частью приложения.
При этом все три указанные части являются пассивными — а единственной активной частью программы становится Контроллер.
Контроллер сам не имеет внутренней памяти, не имеет состояний, за исключением «системных» — например, если приложение может работать в нескольких режимах — и занимается только вызывает соотв. методы интерфейсов прочих компонентов.
В силу того, что сказано в посте, надо бы вам определения M и D поменять местами :)
(Т.е. программы я привык выстраивать вокруг данных в памяти + контроллер, пристраивая остальное «вокруг» этой сладкой парочки. вплоть до того, что контроллер мог сливаться с моделью — в ООП это будет означать, что «Модель» — это поля корневого объекта данных, а «Контроллер» — это его методы. ))
Некропостинг для будущих читателей. Документация Qt в части MVC, я считаю, могла бы указать читателям, что в Qt по сути ничего не говорится про M (которая доменная модель), а скорее про VM (которая есть адаптер доменной M к формату, перевариваемому всеми V из Qt). Поэтому пользователи Qt могут сделать ошибку, используя Qtшные QAbstractItemModel или QStandardItemModel в качестве доменной модели - это будет очень сильное натягивание совы на глобус!
С точки зрения серверной части, AJAX — это такой же http-запрос.
А потом мы делаем вебсокеты…
а пользователь открывает это в IE 6 )))
Веб-сокет — это еще и API, которое должен поддерживать браузер. IE поддерживает веб-сокеты только с 11й версии.
… она (технология веб-сокетов, прим. моё) ещё надохится в процессе развития и не имеет хорошего уровня браузерной поддержки. Изначально технология была добавлена в браузеры Firefox 4 и Opera 11, но была удалена через несколько месяцев в связи с проблемой потенциальных нарушений безопасности.©
Ну и как вы будете подключаться к веб-сокету из шестого IE?
Раз вы пытаетесь цепляться к словам — вот вам:
Стандарт HTML5:
4.11 Scripting
6 Web application APIs
Конкретно про веб-сокеты — HTML Living Standard:
9.3 Web sockets
всего лишь один из возможных API для использования WebSocket и мне совершенно непонятно, какое отношение он имеет к HTML.
https://developer.mozilla.org/en/docs/Web/Guide/HTML/HTML5
что Веб-сокеты появились в HTML5
Есть такая штука как HTML5 API. Это просто группа стандартов. Стандарт самого HTML включен в него же. Как и API для работы с DOM например.
Я бы и про "поверх tcp" поспорил, т.к. ничего не мешает вебсокетам работать поверх udp или unix socket, т.е. "Веб-сокет — это протокол передачи данных."
давайте подумаем что это меняет.
- простая модель — запрос/ответ.
Ну то есть мы посылаем на сервер какой-то ивент и получаем ответ. Собственно все. Просто другой транспорт но модель выполнения абсолютно такая же как и в случае с http.
- Подписка на изменения данных.
Вот тут уже интереснее. Мы получаем какое-то событие о том что мы хотим подписаться на изменения, ну например баланса. Сервер при операциях с балансом будет кидать ивент о том что "воу у нас стэйт поменялся" а какой-то объект будет отслеживать эти события и формировать представление для клиента. Тут уже похоже на MVC c вью которое постоянно обновляет представление стэйта модели.
Но опять же, это вопрос не транспорта а того что нам нужно сделать.
Поэтой причине на бэкэнде хорошо себя показывает такой вариант как Model-View-Adapter. То есть есть некий адаптер между HTTP и приложением, который занимается обработкой "событи" и конвертирует их в какое-то действие в приложении. Так же результат работы приложения конвертируется в HTTP ответ. Ну и т.д.
https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93adapter
То есть идея такая. У вас есть View и есть Model. И они друг о друге ничего не знают. То есть одно и то же View может работать с разными моделями просто за счет смены адаптеров. Ну и наоборот. Одна и та же модель может иметь несколько видов представлений в зависимости от адаптера между ними.
Так же интересная особенность это то, что адаптеров может быть много и они формируют между собой своего рода цепь (мидлвары например хороший пример таких адаптеров).
Перечеркнутой линией обозначена необязательная связь Контроллера с Видом.
Это как?
Может V и M?
Вы картинку-то смотрели? Вариант "без связи между V и M" — он на отдельной схеме расположен. Слева же связь между V и M есть, а между контроллером и видом — опциональна.
В вебе контроллер практически всегда вызывает рендеринг вида явно (я не говорю, что это правильно). :)
А вот вид практически никогда не лезет в модель сам.
А если интерисует «физический смысл», то это когда во время работы приложения одни виды сменяют другие (открываются, закрываются) и считается что всем этим ансамблем управляет контроллер
А если интерисует «физический смысл», то это когда во время работы приложения одни виды сменяют другие (открываются, закрываются) и считается что всем этим ансамблем управляет контроллер
Но обычно, под MVC все таки подразумевают вариант с Активной Моделью.
Хм, а в вебе обычно пассивные модели. :) Страничка отдалась и все. Ничего в ней больше не меняется. :)
Независимость Модели является главным в MVC.
Независимость приветствуется везде… :)
Вся бизнес логика приложения, то есть большая часть кода, сосредотачивается в Контроллере и это при том что как раз Контроллер является самой зависимой частью в MVC – в общем случае он зависит и от Модели и от Вида.
Почему же?
Есть API — используйте его.
Хотите, поместите некоторую логику в модель — и так само используйте API.
Зависимости от M нету, так как M — пустая.
Как вообще может быть зависимость от V?
Отсюда и появился термин ТТУК — толстый тупой уродливый контроллер
Другой вариант — толстая тупая уродливая модель. :)
в Контроллер помимо всей бизнес-логики приложения помещается также еще и логика управления пользовательским интерфейсом
Что это вообще такое? :)
Дело в том, что в объектно-ориентированном приложении нет данных, а есть множество объектов и каждый из них содержит какие-то данные и методы работы с ними.
Прекращайте дрочить на ООП.
MVC возможно не только на ООП головного мозга.
Данных нету. Вокруг сферические объекты. Буквы — это тоже объекты.
Второй подход гораздо ближе к первоисточникам.
Та плевать, что было в первоисточниках.
Иногда появляются уникумы, которые говорят, что ООП — на самом деле это не то, что называют ООП сейчас, а обмен сообщениями.
Мартин Фаулер абсолютно прав, когда говорит что MVC это не паттерн
Та это вам все адекватные люди говорят. Но вы же кастрюли на голову оденете и как носороги упираетесь.
«1» Отделение модели предметной области (бизнес логики) приложения от пользовательского интерфейса
В называемом вами классическом это тоже есть.
«2» Независимость Модели и синхронизация пользовательских интерфейсов за счет шаблона Наблюдатель
К вебу имеет слабое отношение…
Очень часто (особенно когда дело касается простых виджетов) оно вообще не делается и используется «упрощенный MVC»
И выходит черти что. Каша из php и html.
То, что Модель реализует шаблон Наблюдатель явно указывает на то, что Модель это именно один объект.
В один класс пихать методы для работы с БД и бизнес-логику? Перемога.
И логика вывихнутая.
Использование наблюдателя никак не говорит, что один объект.
если внесем какие угодно изменения в бизнес логику приложения, но при этом оставим неизменным интерфейс-фасад, то ни Вид, ни Контроллер это никак не затронет.
Хм.
Предположим, что у нас нет фасада.
Глупо же было из-за изменения движка базы менять шаблоны? :)
Причем шлюз этот «вместо того чтобы обеспечивать общий единый для всех API, предоставляет различные API для каждого клиента
Это же так классно поддерживать несколько API :)
Автор подчеркивает, что Модели это не данные, а исключительно интерфейсы/объекты-посредники/фильтры
Вообще-то да.
обеспечивающие удобный доступ к данным, которые могут находится где угодно – на разных машинах, в разных форматах
Программисты на фреймворках понимают это так, что модели остаются ходилками в БД, поверх которых навешали мусора. :)
Типичные ошибки: копирование доменных данных в модели GUI-компонент
Хм.
Но если мы имеем дело с объектами, то они передаются по ссылке.
А как же как быть, когда нам нужны данные из 2 моделей?
А нагружать этой логикой V не хочется. Проще эти данные объединить в С.
Но в контексте шаблонов и схем Модель это прежде всего интерфейс и реализующий его объект-посредник (фасад, адаптер, прокси) обеспечивающие удобный и безопасный доступ к доменным данным, которые могут находится где угодно.
+100500
Но пропагандисты фреймворков ведут людей по быстрому пути :)
Поэтому, если есть те, кому интересно и «не надоело», то пишите и я выложу вторую часть полностью посвященную именно этой теме.
Конечно же, выкладывайте.
В общем первая часть статьи (первая часть того, что выложено, а не вся статья) — ничего нового. Такие самые заблуждение, как и везде, в частности и на хабре.
А вторая часть — грамотная.
MVC — это всего лишь разделение кода на уровни.
Разделять нужно с умом. А не делать так, как пишут идиоты в интернете.
Есть необходимость — вынесли что-то в отдельный класс, нету — не вынесли.
Дополню:
Если логика примитивная и нужна только одному контроллеру, то ее все же можно оставить и в контроллере.
Но не в виде ни под каким предлогом.
Программисты на фреймворках понимают это так, что модели остаются ходилками в БД, поверх которых навешали мусора. :)
Бред. Мои модели ничего о БД вообще не знают. Ходилка в БД работает поверх них.
Но пропагандисты фреймворков ведут людей по быстрому пути :)
К фреймворкам это не имеет никакого отношения. Можно с фреймворком преобразовывать доменный объект в какой-то DTO или прятать его прокси/адаптером/фасадом, а можно без него не преобразовывать и вообще из шаблона в базу писать.
Но не в виде ни под каким предлогом.
А если логика нужна виду? Банальное: список каких-то значений пустой — выдать параграф «нет данніх», не пустой — відать список.
Бред. Мои модели ничего о БД вообще не знают.
Вы молодец. :)
Если бы все были такими, то и статьи не было бы. :)
К фреймворкам это не имеет никакого отношения.
Самое непосредственное.
Почти вся разработка сейчас или на фреймворках, или на CMS.
А если логика нужна виду? Банальное: список каких-то значений пустой — выдать параграф «нет данніх», не пустой — відать список.
Та пишите.
Получите то, от чего начали:
Мешанина php и html. :)
Но спасибо хоть ответили, а не как некоторые :)
Самое непосредственное.
Почти вся разработка сейчас или на фреймворках, или на CMS.
Да без разницы на чём она. не зависит от использования фреймворка отдавать во вью или контроллер объект модели или прятать его за прокси/адаптерами/фасадами или, как вариант, давать дто.
Та пишите.
Получите то, от чего начали:
Мешанина php и html. :)
Есть шаблонизаторы вообще. Так или иначе логика того что и как показывать (например одни сообщения красным, другие зеленым) должна быть в виде, а не в контроллере или модели.
Да без разницы на чём она
Да, без разницы на чем.
Только пропагандисты фреймворков предлагают быстрый путь вместо правильного. :)
Есть шаблонизаторы вообще.
Ну тогда в шаблонизаторе будете в базу лазить. :)
Это уже будет не совсем шаблонизатор. :)
Хотя я не против вызова из шаблона других виджетов / контроллеров / шаблонов.
Только это только вызов, без логики обработки данных.
Так или иначе логика того что и как показывать (например одни сообщения красным, другие зеленым) должна быть в виде, а не в контроллере или модели.
Это ж другое.
Такая логика уместна в шаблоне. :)
Вы зря обобщаете «пропагандистов фреймворков». И вообще: кто они, єти пропагандисты?
Логика присутствует на всех уровнях. Просто она разная. А вы передёргиваете.
К тому же логика должна присутствовать и в наших рассуждениях. Так, например, когда нужны данные из двух моделей, все обращения к другим моделям происходят из основной модели, соответствующей текущему контроллеру. Иначе опять происходит переползание логики предметной области в контроллеры. ИМХО.
Если логика примитивная и нужна только одному контроллеру, то ее все же можно оставить и в контроллере.
Но не в виде ни под каким предлогом.
Тут ещё вот какой момент есть, сейчас логика нужна только в одном месте. А завтра она, как всегда внезапно понадобится ещё в пяти местах.
Типичные ошибки: обращение к доменным объектам напрямую
Часто использую в виде передачи в вид через контроллер доменных объектов (сущностей и объектов-значений), но не считаю это ошибкой в общем случае, а просто оптимизацией своего труда — в большинстве случаев создание отдельных фасадов будет заключаться в написании кучи геттеров типа FacadeObject::getSomeValue() {return $this->domainObject->getSomeValue();}. Да, оно изолирует доменный объект от вида и контроллера, не допускает даже в теории (без хаков типа рефлексии) изменение доменных объектов, но в большинстве случаев, имхо, эта изоляция не стоит потраченного времени и усложнения кода, если соблюдать дисциплину — не дергать в виде и контроллере методов объектов, изменяющих их состояние и только в контроллере дергать сервисы, изменяющие состояние домена. В каких-то языках, возможно, помог бы её соблюдать модификатор доступа типа friendly, но в PHP его нет, а в JS вообще модификаторов нет толком.
Ну и фронт-контроллер отвечает у меня за инициализацию приложения (в PHP) и роутинг пользовательских событий в отдельные контроллеры, которые инициализируют доменную модель для реакции на событие UI, дергают сервисы, передают, если нужно часть данных доменной модели в вид для отображения (в PHP, в JS вид слушает события модели) и сохраняют изменения доменной модели. То есть контроллеры обеспечивают не только UI, но и инфраструктуру для доменной модели, в частности обеспечивают персистентность её данных.
Сама же доменная модель зависит только от объявленных в ней же абстракций, которые контроллеры заполняют конкретными инфраструктурными реализациями. Иногда, конечно, абстракции текут (прежде всего из-за трудоемкости полной абстракции от инфраструктуры хранения данных), но в целом считаю, что у меня удачный компромисс между стремлением всё сделать по науке и практическими требованиям заказчика прежде всего к скорости разработки. Главное, соблюдать дисциплину и внимательно следить когда действительно пора вводить дополнительный слой абстракций и делить существующую на несколько для изоляции и уменьшения связанности.
Есть приложение, не веб, десктопное, которое общается с базой данных. Вся бизнес-логика вынесена в хранимые процедуры (или функции в терминах Postgre). Насколько я понимаю, это толстая модель данные+бизнес-логика. В приложении нет ни одного прямого select, insert, update, delete. Соответственно на каждое действие есть соответствующая процедура, с правами для каких-то групп пользователей. Вроде бы всё логично.
Но попытка узнать, как части этого приложения перенести в веб интерфейс (интранет), натыкается на какое-то фундаментальное непонимание меня веб-программистами. Начиная с того, что авторизацию в таком веб-приложении нельзя делать на основе авторизации в базе данных (пустили в БД, пользователь вошёл, не пустили, не вошёл). Заканчивая тем, что у меня «неправильно» организованы данные, функциями никто не пользуется и права так раздавать нельзя. А разделением прав будет заниматься некая прокладка фреймворка, а все пользователи приложения, в БД будут логиниться под одним пользователей (условным web_user). Так как знаний у меня не достаточно, оценить полный спектр технологий/фреймворков и самое главное структуру такого веб интерфейса я не могу.
Читая статью, мне показалось, что моя идея имеет права на существование. Я не прав?
Вся логика в приложении это круто. Поменять условия выборки — поменяй код. Поменялись проверки — поменяй код. При этом надо заставить всех пользователей перезапустить приложение. Громоздкие SQL запросы в коде. Зачем? Если сейчас я чуть меняю хранимую процедуру (параметры передаются те же) и у всех всё работает по-новому. Не говоря уже о фантазиях сделать приложение под другую платформу (веб, macOS).
Веб-приложения не надо перезапускать. И код обычно меняется проще, чем схема БД.
Вот декстоп — там да, с доставкой новых версий целая эпопея. Да и небезопасен неограниченный доступ к базе с рабочих мест. Поэтому десктопные приложения делают клиент-серверными.
А про неограниченный доступ к БД с рабочих мест — а какая альтернатива в вебе? Если мы говорим про приложение, которое работает только с БД, и только после авторизации. Зачем какой-то дополнительный компонент авторизации, если уже всё есть – можно/нельзя войти проверяет сервер БД.
Клиентскую часть перезапускать не надо, если не менялся API.
Зачем какой-то дополнительный компонент авторизации, если уже всё есть – можно/нельзя войти проверяет сервер БД.
Потому что так проще (процедурные расширения SQL — не самые простые языки программирования). Возможно, для вас это не так. Но не стоит обобщать свой опыт на весь мир.
Потому что веб-сервер может держать больше соединений чем сервер БД.
Сервер БД не умеет идентифицировать пользователя через WS-Federation или OAuth/OpenID.
Если доступ пользователя к столбцам в БД настраивается довольно просто — то доступом к строкам управлять вам придется вручную, и никакие фреймворки тут не помогут.
- Вам придется городить знатные велосипеды, чтобы передать из БД уведомление об изменении данных.
А про неограниченный доступ к БД с рабочих мест — а какая альтернатива в вебе?
Доступ к БД только от веб-сервера, конечно же. Пароль никому не сообщать, порт закрыть файерволом или биндить на loopback.
2. Мне не актуально, правда.
3. Аргумент, но наверное не для внутреннего использования.
Я бы с удовольствием обменялся опытом, проектировал бы БД, за
«Проще», это вот опять, сложный вопрос. Я до сих пор не понимаю какой комплект технологий (на сервере и клиенте) мне нужен, чтобы просто вытащить в веб-интерфейс справочник из двух полей. Так чтобы легко добавлять/изменять/удалять данные прямо в гриде.
На клиенте: HTML + CSS + jquery или какая-нибудь библиотека для двусторонней привязки данных (KnockoutJs или AngularLight).
На сервере: PHP или другой язык, пригодный для веб-программирования (C#, Java, Ruby, Python).
Самым сложным тут будет — избежать отправки на сервер справочника целиком (что чревато конфликтами при одновременном редактировании). Если справочник редактируется монопольно — то задача вообще простейшая, иначе надо будет отслеживать какие строки пользователь менял, а какие — нет.
Я бы с удовольствием обменялся опытом, проектировал бы БД, за еду обучение веб-подходам в очень узкой области)
Да я и "за так" вам про веб-подходы расскажу. Но лучше все-таки это делать на http://ru.stackoverflow.com/, а не в комментариях к случайному посту на хабре.
Аргумент, но наверное не для внутреннего использования.
Для внутреннего использования неплохо заходит авторизация через Active Directory (которая на самом деле Kerberos, не путать с авторизацией через LDAP!). И она, насколько я знаю, из всех СУБД работает только с MS SQL Server.
Ну так и с логикой в коде так же. Один раз назначили пользователю роль и все. С этой стороны разница только в том, что они хранятся в таблицах типа user и role, а не в системных.
С коннектом напрямую в БД проблема в том, что подключенный пользователь имеет доступ ко всем средствам SQL, поэтому надо по умолчанию ограничивать права на всё.
С доступом через веб-приложение немного попроще — если приложение не предоставляет возможность для отображения таблицы транзакций, то никто их и не прочитает, без всякой проверки прав. А если предоставляет, то можно например id пользователя добавлять в каждый запрос, чтобы он только свои видел.
Если сейчас я чуть меняю хранимую процедуру (параметры передаются те же) и у всех всё работает по-новому. Не говоря уже о фантазиях сделать приложение под другую платформу (веб, macOS).
Вот с доступом через промежуточное веб-приложение все то же самое. Поменяли код — все запросы обрабатываются по-новому. В десктопной разработке это кажется называется сервер приложений и тонкий клиент.
Другое приложение тоже подключить не проблема, для этого делается API, и все клиенты используют его.
И да, я не знаю ни одного фреймворка, который бы авторизовал юзеров посредством бд.
Symfony c Doctrine позволяют это делать, причём на уровне конфигов.
Эм… это как? Ну то есть, там аутентификация на стороне php происходит, с хэшированием пароля через password_hash и т.д. Да и Doctrine тут не причем, просто есть дефолтный entity user provider.
Неужели в мире вэба свет окончательно сошелся на фреймворках, и на чистом php уже никто не хочет даже просто «пообщаться с БД»?
В целом да. Более того, это считается дурным тоном для большинства задач. Уж как минимум, соединение должен настроить фреймворк по конфигам. Справедливости ради, нормальные фреймворки позволяют и соединение устанавливать с динамическими параметрами, но большинство разработчиков на них, наверное, даже не знают как это делать, из голого ПХП знают как, а в воркфлоу фреймворка как заинжектить динамику не знают, привыкли к «магии».
Неужели в мире вэба свет окончательно сошелся на фреймворках
Не везде, но фреймворки доминируют.
Разработчики то ли обленились, то ли отупели, то ли тупыми и были, то ли не хотят брать на себя ответственность. :) Бла-бла-бла.
Много свежей крови, которая кроме фреймворков ничего не умеет.
Поколение программистов, не умеющих программировать на языке, на котором написан фреймворк :)
а в воркфлоу фреймворка как заинжектить динамику не знают, привыкли к «магии»
Но фреймворки тоже толком не умеют. :)
Ну такое.
Давайте, вахтеры, минусуйте. :)
Разработчики то ли обленились, то ли отупели, то ли тупыми и были, то ли не хотят брать на себя ответственность. :)
Обленились, да. Хороший разработчик — ленивый разработчик. Он не будет писать сотни раз один и тот же код в разных проектах. И даже один раз писать и потом копипастить или подключать не будет, если знает, что есть готовое решение. Это только люди с синдром NIH пишут всё сами, начиная чуть ли не с BIOS.
Хороший разработчик — ленивый разработчик
Согласен.
если знает, что есть готовое решение
Вот только пользоваться фреймворками люди не умеют.
Вы же сами это говорили. :)
Да и MVC унылый на фреймворках, как показала статья и комменты. :)
Это только люди с синдром NIH пишут всё сами, начиная чуть ли не с BIOS.
Есть понятие unix-way.
Фреймворк — это швейцарский нож, которым никто не умеет пользоваться :)
Это разве нормально, что программист на фреймворке языка не знает языка и не умеет программировать? :)
Хотя да, разные программисты нужны. :)
«Не хочет писать SQL-запросы вручную» не означает «не умеет программировать».
Карл, а где я такое говорил?.. :)
Дело в том, что в объектно-ориентированном приложении нет данных, а есть множество объектов и каждый из них содержит какие-то данные и методы работы с ними.
Прекращайте дрочить на ООП.
Если вы предлагаете не использовать объекты, по которым запросы к базе данных строятся автоматически, значит предлагаете использовать запросы вручную и массивы.
Вот только пользоваться фреймворками люди не умеют.
Я вам больше скажу. Если вы дадите разработчику голый PHP выйдет скорее всего хуже. Ну то есть тут проблема не в том что люди не умеют пользоваться фреймворками, а в целом не умеют или не хотят думать что они делают. Культ карго, эффект Даннинга-Крюгера и т.п.
Да и MVC унылый на фреймворках, как показала статья и комменты. :)
MVC это buzz-word. В целом например фэйсбук со своим flux подошел очень грамотно. Они просто переименовали вещи и отгородились от общей концепции. Что бы меньше конфликтов вызывало в понимании.
Фреймворк — это швейцарский нож, которым никто не умеет пользоваться :)
Посмотрите на современные фреймворки. Как правило они состоят из набора готовых самодостаточных компонентов. Обычно есть стандартная сборка этих компонентов которая и именуется фреймворком. Но никто не мешает вам брать и использовать другие компоненты.
Есть правда чуть другой вид фреймворков. Они ориентированы на то чтобы дать разработчику не конструктор, из которого он может собрать что ему надо и заменять детали, а скорее платформу. Тот же RoR, Yii и подобные монолитные фреймворки идут именно по этому пути. У этого подхода есть как свои минусы так и плюсы.
Есть понятие unix-way.
Он обязывает иметь самодостаточные модули которые можно комбинировать. Каждый модуль со своей узкой зоной ответственности. Делай что-то одно и делай это хорошо. Но желательно использовать готовые модули а не писать велосипеды. Очень часто когда люди кричат "фреймворки не нужны" потом проскакивает фраза в духе "вот у меня свое ядро есть". И это обычно означает что есть не пачка модулей независимых а каша в которой просто очень хорошо ориентируется ее автор. Так что… тут такое, вопрос восприятия.
Это разве нормально, что программист на фреймворке языка не знает языка и не умеет программировать? :)
Нет, не нормально. Бизнес логика приложения по хорошему не должна быть завязана на язык. Так же есть такая проблема среди разработчиков что если они выбирают один конкретный инструмент, то пытаются потом задачи подгонять под него же. Например ORM. Это хороший инструмент когда нам надо сделать что-то с каким-то небольшим набором данных. Провести бизнес транзакцию. Но многие начинают впадать в крайности и пытаться тем же ORM решать вопросы репортов, сложных выборок и т.д. Это не означает что "ORM не нужны", это означает что люди любят зацикливаться и не особо хотят думать.
Если вы дадите разработчику голый PHP выйдет скорее всего хуже. Ну то есть тут проблема не в том что люди не умеют пользоваться фреймворками, а в целом не умеют или не хотят думать что они делают.
Выходит парадокс :)
Для того, чтобы нормально пользоваться фреймворком, нужно быть хорошим программистом.
Но хороший программист может обойтись и без фреймворка.
А плохие программисты не могут нормально пользоваться фреймворком.
Но без фреймворка будет еще хуже. :)
ЦА фреймворков — плохие программисты? :)
Культ карго, эффект Даннинга-Крюгера и т.п.
А, ну это да. Странно только, что он так сильно распространен среди программистов или «программистов» :)
Они просто переименовали вещи и отгородились от общей концепции.
Ерунда какая-то :)
Как правило они состоят из набора готовых самодостаточных компонентов. Обычно есть стандартная сборка этих компонентов которая и именуется фреймворком.
1. Только не все умеют эту сборку готовить :)
2. А что должно быть во фреймворке, если выбросить все компоненты?
И это обычно означает что есть не пачка модулей независимых а каша в которой просто очень хорошо ориентируется ее автор.
Хм, но общение может быть организовано посредством какой-то шины :)
Очень часто когда люди кричат «фреймворки не нужны»
Правильней «фреймворки не всем нужны» :)
Но многие начинают впадать в крайности
Об этом и речь, что не нужно впадаться в крайности. :)
Что еще скажу: если человек понимает, что он делает, и знает где что лежит, то плевать, что кто-то считает, что он делает неправильно. :)
Хотя в крайности тут тоже впадать не нужно.
Я вот раньше писал плохо отформатированный код :) Но мне он был понятен :) А сейчас самому противно смотреть на такой код. :)
Выходит парадокс :)
Для того, чтобы нормально пользоваться фреймворком, нужно быть хорошим программистом.
Но хороший программист может обойтись и без фреймворка.
А плохие программисты не могут нормально пользоваться фреймворком.
Но без фреймворка будет еще хуже. :)
Нет парадокса.
Хороший программист может обойтись без фреймворка, но хорошо подходящий под задачу фреймворк (обычно значительно) ускорит её решение (а плохо подходящий он не выберет).
А плохому программисту фреймворк поможет (или, скорее, заставит) создавать не такой уж плохой код, с которым, в частности, хорошему будет проще разобраться или
ЦА.
В общем ЦА фреймворков — программисты вообще.
Для того, чтобы нормально пользоваться фреймворком, нужно быть хорошим программистом.
Но хороший программист может обойтись и без фреймворка.
А плохие программисты не могут нормально пользоваться фреймворком.
Но без фреймворка будет еще хуже. :)
Никакого парадокса. Есть такая штука — сюхари.
- не опытный программист если ему дать просто язык программирования напишет вам небезопасное и кривое решение. Причем оно будет плохим во всем.
- Чтобы уменьшить риск ему можно одеть смирительную рубашку в виде фреймворка, который декларирует "правильные" подходы. Далеко не факт что программист будет эти подходы правильно понимать. И да есть проблема что поскольку подходы принимаются на веру программисты становятся религиозны.
- Со временем и при нормальной команде разработчик начинает понимать подходы и использовать инструменты с умом.
- Сильный разработчик прошел все эти этапы, прекрасно ориентируется почему фреймворки такие какие они есть, понимает подходы и т.д. А зная разные подходы и трезво оценивая плюсы и минусы каждого, уже не важно на каком фреймворке (даже если вы решитесь делать все без них у вас выйдет свой фреймворк) будут делаться дела.
Странно только, что он так сильно распространен среди программистов или «программистов» :)
Это общечеловеческий феномен.
- Только не все умеют эту сборку готовить :)
- А что должно быть во фреймворке, если выбросить все компоненты?
- Да, смотрите про "три стадии обучения".
- Клей, мосты между компонентами, адаптеры, загрузка конфигураций и базовая структура. Это не много кода обычно.
Хм, но общение может быть организовано посредством какой-то шины :)
если у вас кучу компонентов связывает одна шина, вы получаете систему с высокой связанностью. Ну то есть все та же каша но с иллюзией порядка.
Правильней «фреймворки не всем нужны» :)
Фреймворки нужны всем. Это каркас. Хотите вы или нет но он у вас появится. Другое дело что есть те 0.1% разработчиков чьи задачи требуют создание своего уникального каркаса.
Что еще скажу: если человек понимает, что он делает, и знает где что лежит, то плевать, что кто-то считает, что он делает неправильно. :)
И тут мы приходим к эффекту Даннинга-Крюгера. Как понять что человек на самом деле понимает что делает а не игнорирует советы других? Проблема то в том что программисты частенько самоучки. И частенько они тусуются в кругу таких же вот самоучек. И невежество начинает укрепляться и распространяться. И в итоге люди исповедующие нормальные подходы остаются в меньшинстве. А поскольку они в меньшинстве то все надеятся на мудрость толпы и т.д.
Я вот раньше писал плохо отформатированный код :) Но мне он был понятен :) А сейчас самому противно смотреть на такой код. :)
Я раньше геттеры у классов делал и думал что это нормально. Как же я был не прав.
Никакого парадокса. Есть такая штука — сюхари.
Беда в том, что программист может застрять на первом уровне и иметь культ карго. :)
Ибо никакого обучения и осмысления нету. Обычное кодирование, как макака. :)
На предложение подумать своей головой, в ответ агрессия. :)
Процитирую:
До того, как вы начнете на практике применять паттерны ООП, и не поймете их минусы и плюсы, я советую не читать книг по паттернам, и не использовать фреймворки, иначе случится паттерн головного мозга, и паттерны будут применяться не по назначению, а просто потому, что вы их знаете, вы будете лепить их к месту и не к месту.
Вам не кажется, что те, кто начинал на голом языке, имеют больший профессионализм?
если у вас кучу компонентов связывает одна шина, вы получаете систему с высокой связанностью.
А на фреймворках компоненты как взаимодействуют друг с другом? :)
Service Container — это та самая единая шина. :)
У меня единая шина событий.
А компоненты одного пространства имен свободно вызывают зависимости, но их не много.
Фреймворки нужны всем.
Тогда так:
«Мейстримовые фреймворки не всем нужны» :)
Клей, мосты между компонентами, адаптеры, загрузка конфигураций и базовая структура. Это не много кода обычно.
Так я и говорю, что это не много. :)
Это можно и самому реализовать, как нужно. :)
И в итоге люди исповедующие нормальные подходы остаются в меньшинстве. А поскольку они в меньшинстве то все надеятся на мудрость толпы и т.д.
Но большинство пишет на фреймворках… :)
Беда в том, что программист может застрять на первом уровне и иметь культ карго. :)
Это происходит вне зависимости от инструментов. Это лишь говорит о том что разработчик не особо хочет вникать что он делает и зачем. И да, таких много. Но это не проблема инструментов и таким людям обычно ничего не помогает. Обычно они занимаются всякими вордпрессами и подобным.
На предложение подумать своей головой, в ответ агрессия. :)
У человеческого мозга есть такой интересный механизм защиты. Если мы встречаемся с чем-то новым, чем-то что противоречит нашим убеждениям, обычно это воспринимается в штыки. Если у человека развито критическое мышление, но при достаточном количестве аргументов человек начинает менять и адаптировать свою точку зрения. Но чаще всего если это что-то что является основной системы координат (что-то вроде "фреймворки это плохо" или "мой бог круче твоего") мы получим холивар и высокий уровень агрессии.
Если смотреть на это с точки зрения эволюции, скорее всего работает это для того, чтобы встретившись с чем-то новым мы не забывали старый опыт. Например вы знаете что огонь горячий. И когда будете видеть что-то похожее на огонь и вам кто-то скажем что "он холодный" вы врядли поверите и вряд ли без колебаний засунете туда руку. Более того,
Процитирую:
Кого?
Паттерны лишь словарь. Вы говорите кому-то "используй адаптер" и разработчик понимает что ему надо завернуть объект с одним интерфейсом в другой объект, с другим интерфейсом чтобы сделать совместимость (слово адаптер не просто так выбрано). То есть возможно в документации к фреймворку будут использоваться подобные слова, но в целом я не припомню ни одного где подобные вещи диктовались бы. Обычно вы будете натыкаться на предложение "заюзать паттерн" далеко не в начале документации.
Ну и опять же. Перед тем как учить паттерны стоит разобраться с такими понятиями как связанность, управление зависимостями, инкапсуляция, декомпозиция задачи и т.д. Структурное программирование можно вместо ООП поизучать. Но это поднимает уже вопрос процесса обучения — его нет. Я часто видел когда на вопрос "с чего начать" кидают книжку по паттернам, хотя человек еще не умеет алгоритмы строить.
Вам не кажется, что те, кто начинал на голом языке, имеют больший профессионализм?
тут вопрос в том что есть "начинал". Вот к примеру я начинал писать на PHP не потому что мне надо было заказ на фрилансе закрыть, а потому что нужен был простой консольный скриптик который бы сконвертил 3000 документов из одного формата в другой и я не хотел это делать руками. Пришлось разобраться с основами программирования, регулярками и подобным.
А многие под "начинать" подразумевают "ну я на выходных почитал про php и сейчас взял пару заказов на фрилансе и времени разбираться нат надо просто проблему решить".
Перед тем как брать фреймворк в руки разработчик уже должен:
- знать как информация представлена в памяти хотя бы на базовом уровне
- знать что есть алгоритмы и как их писать. Банально понимать как написать свой пузырек или алгоритм бинарного поиска.
- знать хотя бы основную часть синтаксиса языка.
- представлять как происходит процесс разработки ПО.
Если вы посмотрите на какой-нибудь Ruby-on-rails и какие-нибудь толковые курсы в духе "RoR за 3 месяца", то там еще до знакомства с самим фреймворком вас сначала научат на ruby писать, потом научат тесты писать и только потом фреймворк.
То есть знать язык надо, но писать свой фреймворк — плохая идея.
А на фреймворках компоненты как взаимодействуют друг с другом? :)
Service Container — это та самая единая шина. :)
давайте разберемся с терминологией что бы лучше друг-друга понимать. "шина" это что-то что выступает как транспорт между компонентами. То есть для того что бы компоненты общались между собой они должны знать о шине. Контейнер зависимостей же это такая штука, которая хранит в себе все зависимости и умеет их выдавать по просьбе. Но компоненты ничего о контейнере не знают. Они общаются напрямую а контейнер лишь "сводит их вместе". Короче говоря это не шина.
У меня единая шина событий.
а вот это уже шина. И это та самая глобальная зависимость в ваших проектах. Если мы возьмем любой компонент вашей системы ему нужна будет эта самая шина.
В целом по поводу событий для снижения связанности… Это очень хороший способ снизить связанность между компонентами. Но помимо связанности есть еще такое понятие как "зацепление" (coheasion) и если связанность должна быть низкой, то зацепление должно быть высоким. Что это значит? Это значит что код который относится к одной области ответственности (все что связано с юзерами например) должно лежать рядом и общаться напрямую без всяких там шин.
«Мейстримовые фреймворки не всем нужны» :)
Они нужны 99.9% проектов. Да, это не всем, но подовляющему большинству. А вот "не мэйнстрим" фреймворки не нужны никому. По очень простой причине. Если вы берете фреймворк написанный васей пупкиным у которого 10 звезд на гитхабе, то все расходы на поддержку этого фреймворка ложатся на вас. Фреймворки комьюнити которых насчитывает десятки тысяч разработчиков использовать как-то проще. Тупая экономика.
Это можно и самому реализовать, как нужно. :)
на готовых компонентах — да. Но только опять же зачем? в чем смысл? берем symfony/framework-standard-edition и он покрывает 90% всех проектов. А вот если мы хотим делать продукт свой например, и нам очень хочется что бы структура проекта отражало то как мы планируем работать с проектом, и мы хотим точно представлять что мы используем — то тут да. компонентики, контейнер зависимостей какой, и все будет хорошо. Вот только для этого на проекте должен быть синьер разработчик который будет следить за всем этим. Джунам — только стандартные сборки.
Но большинство пишет на фреймворках… :)
мне больше нравится интерпретация "большинство используют фреймворки". Если для вас "фреймворк" это структура проекта — ну ок. Для меня "фреймворк" это все что составляет инфраструктуру проекта. То есть если у меня нет фреймворка, он у меня будет.
Кого?
Чувака одного с форума :)
тут вопрос в том что есть «начинал»
Я тоже начинал по маленькому.
Захотел прикрутить динамику к своему статичному сайту. :)
Контейнер зависимостей же это такая штука, которая хранит в себе все зависимости и умеет их выдавать по просьбе. Но компоненты ничего о контейнере не знают.
Но они же знают, что они ищут. :)
Допустим кеш.
Или кеш на мемкеше наследует базовый кеш.
еще такое понятие как «зацепление» (coheasion) и если связанность должна быть низкой, то зацепление должно быть высоким.
Зацепление высокое :)
Они нужны 99.9% проектов.
Я обязан упомянуть статью
то все расходы на поддержку этого фреймворка ложатся на вас
Какие ж там расходы.
Там же не так и много кода по сравнению с самим приложением.
Но они же знают, что они ищут. :)
они явно объявляют свои зависимости. Например в такой штуке кака конструктор. И им без разницы есть там какой-то конструктор или клиентский код сам все разрулит.
Зацепление высокое :)
Опять же, когда зацепление высокое но связанность недостаточно низкая то это приводит к вещам вроде god object. Важен баланс. И я не склонен верить высказываниям что у вас все хорошо (только если в скажете что у вас 90+ покрытие юнит тестами кода, и это не 100 тест кейсов, и это настоящие юнит тесты не выходящие за пределы одного процесса).
Я обязан упомянуть статью
Я обязан дать ссылку на докладик: Greg Young — Stop over engineering. Там очень хорошо и про написание своих фреймворков, и про ложный нон конформизм, или за попытки обобщить специализированный функционал....
Там же не так и много кода по сравнению с самим приложением.
любой код который вы используете надо поддерживать. Ваш он, или это внешняя зависимость… Просто если вы используете что-то популярное шанс того что баги за вас будут вылавливать другие тысячи людей повыше. И расходы на поддержку будут равномерно распределяться на большое количество людей.
Я тоже не приветствую оверинжиниринг. :)
собирать свой фреймворк даже из готовых компонентов в 99% случаев оверинженеринг. Например у меня специализация — апишки писать. Я взял стандартный симфони, накрутил туда пару своих решений, пару чужих, тупо своя сборка, и просто использую из проекта в проект. Большая часть "клея" — стандартная. Это значит что можно дать этот проект на суппорт и дальнешнее развитие любому разработчику который знаком с symfony. А особенности и отличия прописать в README.
Делать же сборку под один проект можно только если вы прекрасно понимаете что делаете. А это уже не новички.
Другое дело что есть те 0.1% разработчиков чьи задачи требуют создание своего уникального каркаса.
Искал комменты, где говорится что только фреймворки наше все.
Нашел этот.
Ну ок, я тогда в 0,1% :)
Спасибо.
Да и MVC унылый на фреймворках, как показала статья и комменты. :)
Нормальный MVC. Используют его многие ненормально. Например, руководствуются туториалами как пользоваться фреймворками
Есть понятие unix-way.
Фреймворк — это швейцарский нож, которым никто не умеет пользоваться :)
Хороший фреймворк состоит из изолированных, слабосвязанных, заменяемых на сторонние реализации или просто исключаемых модулей
Открою для вас секрет: фреймворки для того и изобретают, чтобы написание программ было доступнее. Программисты не обленились, нас просто стало больше.
Если бы вы писали БД для веба с нуля — то авторизация средствами БД была бы ужасной архитектурной ошибкой, так же как и бизнес-логика в хранимках. Просто из-за скорости разработки.
Но если БД уже есть и готовая, то веб-фронтэнд к ней сделать намного проще. Фактически, веб-разработчику не нужно вообще думать об авторизации, только об аутентификации. Да и вся серверная часть будет очень простой — вызвать хранимку, передать ей параметры, получить результаты, отобразить их. Фреймворки тут тоже не нужны, разве что что-нибудь для шаблонизации. И для защиты от CSRF, но ее и самому сделать можно.
Возможно, веб-программисты просто не понимали зачем вам вообще они. По-хорошему, тут верстальщика достаточно, а PHP в требуемом объеме вы и сами изучить можете.
Самой большой проблемой будет хранение паролей. Скорее всего, вам придется сохранять пароль пользователя в сессии в открытом виде. Это связано с тем, что базе данных пароль требуется при каждом соединении, а веб-сайт запрашивает его у пользователя один раз. Длительное хранение паролей в открытом виде считается является небезопасным — но само по себе, без других дыр, к проблемам привести не должно. А дыр в подобной крайне простой серверной части быть не должно.
Если использовать языки программирования, отличные от PHP, то там можно хранить в сессии не пароль к БД, а непосредственно открытое соединение. Да и на PHP так можно делать, если написать свой http-сервер или fastcgi-обработчик. Но это будет уже намного сложнее.
Основное ограничение подобного подхода — принципиальная невозможность нормально передать из БД наружу событие об изменении данных. Это значит, что никаких веб-сокетов в подобном веб-сайте не появится никогда, да и AJAX при таком подходе будет не лучшим решением. С другой стороны, в интранете большего и не надо.
Т.е. вы хотите сказать, что весь вопрос только в готовности БД?
Да, весь вопрос в готовности БД. Веб-приложение с логикой внутри можно сделать быстрее чем БД с той же самой логикой внутри.
Вот именно "говно со сложными процентами" и делается намного проще на нормальных языках программирования, нежели на SQL.
SQL — это прежде всего язык написания запросов, а не программирования. Это уже накладывает некоторые ограничения.
А поведение оптимизаторов в некоторых СУБД и вовсе заставляет заниматься какой-то ерундой, когда план желаемый выполнения очевиден любому школьнику — но оптимизатор запросов никак не желает его строить.
Мне нравится проектировать БД, стараюсь делать, чтобы результатом можно было пользоваться откуда угодно.
Попробую привести пример. Есть прайс-лист, видеть его может почти кто угодно. Например в нём пять колонок. И изменения в одних колонках вызывает каскадные изменения в других, плюс запись лога изменений, плюс что-нибудь ещё.
Изменять отдельную колонку может менять только определённая группа пользователей. Я бы написал 5 хранимых процедур, раздал права на каждую соответствующей группе и всё. Приложение понятия не имеет о том, что и кому можно, оно зовёт одну процедуру в зависимости от того, какую колонку меняют. И можно или нельзя менять проверяет сервер БД. Это же просто и быстро пишется что со стороны приложения, что со стороны сервера БД. Неужели перенеся эту логику в приложение станет проще? (Я осознаю, что почти всё, что я написал можно вообще сделать в триггере).
У меня только хранимых процедур 2к, если представить, что это переехало в код приложения, да ещё и с обёртками, становится страшно
А мне вот становится страшно, что 2к функций из потенциального кода уже переехало в базу :) Предлагаю отказаться от понятия "страшно" и прочих эмоций и обсудить в чем именно вы видите проблему.
Лично я вижу проблему 2к хранимок в том, что там наверняка есть куча повторяемого кода, который нельзя куда-то вынести потому что язык не позволяет. Та же обработка ошибок...
Еще проблема может быть с доработками: добавление колонки в часто используемую таблицу приведет к тому, что надо вручную исправлять все хранимки, поскольку инструменты рефакторинга отсутствуют.
По поводу вашего примера с прайсом. В вашем варианте разницы действительно нет (кроме проблемы изменений при доработках).
Но представьте себе, что назначать права доступа нужно не на колонки, а на строки. Например, есть несколько отделов — и менять в каждом из них цены может только тот, что за этот отдел отвечает. Получается, возможность доступа к строке зависит от значения внешнего ключа (номера отдела).
В веб-приложении при хорошем фреймворке практически ничего не изменится. А в БД?
Лично я вижу проблему 2к хранимок в том, что там наверняка есть куча повторяемого кода, который нельзя куда-то вынести потому что язык не позволяет. Та же обработка ошибок...
Нет, повторяемого кода нет. Есть, только если нужны выборки с разным доступом, с разным набором полей, что достаточно редкий случай.
Еще проблема может быть с доработками: добавление колонки в часто используемую таблицу приведет к тому, что надо вручную исправлять все хранимки, поскольку инструменты рефакторинга отсутствуют.
Вручную надо исправить ровно одну процедуру – выборку, где это поле нужно добавить. И написать ещё одну, для изменения этого поля (если нужно).
Но представьте себе, что назначать права доступа нужно не на колонки, а на строки.
Да, это действительно проблема, но она обходится, например вспомогательными таблицами с признаками доступа по пользователю/группе, дальше можно просто присоединить к таблице с доступом данные.
Вручную надо исправить ровно одну процедуру – выборку, где это поле нужно добавить. И написать ещё одну, для изменения этого поля (если нужно).
А если поле не добавляется, а выносится в отдельную таблицу? Тогда надо исправлять каждую выборку, где это поле присутствует.
Если у этого поля еще и "популярное" имя — то поиск такого поля по всем хранимкам станет тем еще приключением.
А если схема БД еще и не в виде файла хранится...
так не делают потому что:
1. pl\pgSQL — _очень_ плохой язык, который мало кто знает
2. очень редко вся бизнес логика должна выполняться в транзакции, поэтому для масштабируемости её целесообразно выполнять на отдельном сервере
3. авторизация и прочие дополнительные функции в БД слабо кастомизируются
4. выше упомянутые проблемы с отслеживанием версий и параллельной разработкой
5. БД коннект на пользователя это обычно очень расточительно
6. не уверен насколько гибкие настройки пермишенов в pgsql, но по идее любой кулхацкер сможет заддосить вашу БД просто написав пару строк в консоли браузера
даже если все эти проблемы не актуальны для тупого круда в интранете, вы надеюсь понимаете что никому не интересно применять абсолютно не применимое в 99% случаев решение
из жизни пример — у нас на проекте в начале было очень много хранимых процедур, в итоге почти все выкосили потому что писать их никто не умел а те что были написаны — жутко тормозили
PS это не про вас случайно http://thedailywtf.com/articles/table-driven-software?
PPS просто интересно, а чем вы предлагаете дергать postgres из жаваскрипта?
При много большом количестве пользователей неизбежны коллизии. На сервере я всегда могу точно сказать в каком состоянии данные, а транзакции помогают эту целостность поддерживать. Или вы про что-то другое?
И это самый простой пример. Могут быть разные версии одной процедуры, например. Если не изменилась, то трогать ничего не надо в базе, если изменилась надо изменить и в базе.
Меня больше беспокоит прямой доступ к данным, когда все кому не лень при наличии инструмента и некоторых знаний может выбрать что угодно. И такие случаи были, пытались запросы писать из excel.
Нет, это довольно частая ситуация. Есть подходы разработки, которые плодят довольно много веток.
Тот же git flow.
И с логикой в БД эти подходы не дружат.
Ну не умеет GIT переключать разные версии БД. И отслеживать обновления схемы БД не умеет! И нет инструментов, которые бы во всех случаях эти самые обновления умели отследить и объединить, в отличии от файлов.
Разумеется, миграции — это те же скрипты, которые можно положить в ветку (и они там лежат!).
Проблемы возникают в случае нелинейной истории, при объединении веток или при переключении между ними.
Господи, это же для тривиальные случаев.
Для таких случаев отличный инструмент.
А когда идет обновление сложнее, с взаимосвязанными элементами, с правильной последовательностью обновления, с обновлением справочников и данных, тогда ценность этой штуки резко уменьшается.
Именно! В том-то и проблема, что нормальных инструментов для нелинейного версионирования схемы БД — не существует.
Существовали бы — писать хранимки было бы намного проще.
А пока — используется подход "схему БД меняем только после согласования со всеми коллегами"
https://habrahabr.ru/post/321050/#comment_10056898
Зря вы в разных ветках отвечаете, раз сами жалуетесь, что вам это трудно.
Хранимки не равны схеме БД.
Да, не равны. Они часть часть схемы.
И что такое
нелинейного версионирования схемы БД
вообще не понятно.
Когда база меняется не пошагово в одном потоке и одном направлении, максимум иногда откатывается, а шаг иди два, а идёт развитие параллельно в нескольких направлениях, возможно разными разработчиками, и эти направления потом сливаются.
У вас в один момент времени на сервере живет много версий БД?
Одновременно одна, но за день может менятся несколько раз и это не только «вперёд» и «назад», но и «влево-вправо».
Простой пример:
в продакшене база с таблицей contract c полем типа enum status. В рамках одной задачи нужно расширить количество значений, разбив существующее на два, в рамках другой перенести поле в отдельную таблицу для хранения истории изменений. Задачи разработчики делают в разных ветках гита/мерка/свн/… О задачах друг друга до поры до времени вообще не знают. Их лид должен сначала проверить изменения одного, потом другого, а потом слить их. Разработчики написали скрипты миграции (накатывания и отката) для своих задач. Сейчас у него копия продакшен базы (версия A). Сначала он должен быстро проверить задачу разбиения значения на два (версия B), потом создания отдельной таблицы (версия C). Для этого система версионирования должна позволять ему быстро (читай автоматически) переключаться с версии A на B (разбить значение), с B на A (объединить значения), с A на C (создать таблицу, скоипровать значения, удалить поле) с C на A (создать поле, скопировать последнее значение, удалить поле). Эта задача, в принципе решена существующими инструментами в виде скриптов миграции созданными разработчиками или даже автоматически (на пустой базе, без сохранения данных кроме самых тривиальных случаев типа переименования столбца/таблицы). Но вот задача переключения с B на C и обратно решена уже хуже. Система версионирования должна понять, что общий предок у них версия A, откатить изменения версии B, накатить изменения версии C и наоборот. Задача объединения версий B и С практически не решена, максимум по таймстампам пытается определить какой скрипт миграции ставит первым. И это лишь по схеме собственно данных, когда есть формальное описание схемы, как правило не на SQL. С хранимыми процедурами, функциями, триггерами всё ещё хуже. Собственно из рассмотренных мною решений никто и не пытается автоматически что-то делать.
Схема БД, записанная в 1 файл, хорошо смотрится только пока БД разворачивается с нуля.
Но вот у вас в БД уже полно данных, и вам надо выкатить новую версию. Что будете делать?
Мне странно ваше непонимание.
Я говорил, конечно же, про продакшен или приближенные к нему окружения. Локальную базу разработчика пересоздать — ни разу не проблема.
Развертывать нужно новую версию приложения, куда входит новая схема БД.
У нас было специальное приложение, которое накатывало скрипты и файлы обновления и следило за ошибками.
… и это приложение было написано для линейной истории версий. То есть для одной ветки. У меня в каждом проекте такое же есть, кстати.
А вот при разработке в разных ветках проблемы и начинаются.
- Александр добавил в таблицу колонку X
- Борис добавил в таблицу колонку X
- Виктор добавил в таблицу колонку Y
- Изменения Александра (1) были развернуты на сервере
- Виктор смерджил себе изменения Бориса (2)
- Александр смерджил себе изменения Виктора (5) и обнаружил конфликт
- Александр переименовал свою колонку X, два ей имя Z.
- Борис переименовал свою колонку X, два ей имя T.
Теперь все эти изменения надо развернуть на сервере. Не потеряв данные, которые уже накопились в колонке X.
Вы представляете себе, с какой стороны вообще подступать к этой задаче?
А ведь есть и более сложные случаи, когда колонка была удалена, потом удаление отменили — и надо получившуюся ветку слить с основной так, чтобы не потерять данные на продуктиве. Но при этом скрыть факт удаления колонки из истории нельзя — потому что на тестовом сервере ее уже удалили и надо восстанавливать, пусть и без данных.
Разумеется, обычные СУБД не предоставляют соответствующих инструментов. Но для промышленных СУБД такой инструментарий за хорошие деньги стоит приобретать.
И как работа через процедуру позволит не запутаться в нелинейных миграциях?
И ваш инструмент автоматически такую ситуацию разруливает?
Такие — никак. Я намеренно привел ситуацию, которую не умеет разруливать ни один известный мне инструмент.
Но заметьте: когда такое творится с базой — это "вакханалия". А когда такое творится с кодом — это нормальный рабочий процесс, разруливаемый гитом без всяких проблем.
Причина различий — в том, что в коде нет накопленных данных, которые нельзя терять. Каждый билд кода — все равно что создание базы с нуля. Именно поэтому код мне нравится больше базы.
Глаза разуйте уже.
Но заметьте: когда такое творится с базой — это "вакханалия". А когда такое творится с кодом — это нормальный рабочий процесс, разруливаемый гитом без всяких проблем.
Логика в хранимых процедура это весьма удобно и часто используется.
А здесь все зависит от типа приложения и характера сопровождения: у нас, например, 4 типа СУБД поддерживаются, а решение сопровождается самим клиентом. Поэтому все в хранимках делать — это та еще задачка.
Но если рассматривать конкретных случае — решение вполне себе приемлимое.
Вы слышали что-нибудь про преждевременные оптимизации?
То, что вам пришлось консультировать коллег — это совершенно нормальный рабочий процесс. Поймите: у них уже два месяца как есть приложение, которое работает! У них не было проблем ни с занесением данных в БД, ни с отображением их для пользователя.
И через два месяца все что случилось — сломался один отчет! Который они, после ваших консультаций, починили. А все остальное у них продолжает работать.
Если бы у них еще и было больше опыта — они бы знали про проблему с отчетом заранее — и за те два месяца, пока их приложение работало, успели бы десять раз его исправить.
Следующих шаг, зачем делать обработку данных в разных местах?
Чтобы упростить код. Потому что когда логика в разных местах, код получается проще.
Да. На нормальных языках программирования доступны всевозможные инструменты борьбы со сложностью, придуманные поколениями программистов.
Все паттерны, библиотеки, фреймворки и технологии — это не просто страшные или модные слова, это средства, которые делают код проще. Они были созданы для этой цели.
В PHP комьюнити есть такой ORM как Doctrine. И авторы оного когда выступают на конференциях с докладами вроде "Doctrine best practice" очень много говорят про то что ORM созданы для OLTP и для таких вещей как репорты придуман замечательный инструмент — SQL. Так что проблема не в инструментах а в головах.
Что до "абстракций". Абстракции разные бывают. DAO тоже абстракция и она не мешает вам использовать вашу СУБД на полную катушку. Но вот с точки зрения приложения вся работа с СУБД изолирована и детали работы с оной так же. В этом собственно и заключается суть абстракции.
Но кроме сложности манипулированя данными вы добавляете обертку сверху в виде API, что еще усложняет.
Нет. Обертка над SELECT/INSERT/UPDATE/DELETE в виде API с бизнес-логикой добавляется вместо обертки над теми же SELECT/INSERT/UPDATE/DELETE в виде SQL-процедур.
Сталкивался с приложениями обоих видов. Поддерживал одно время программу на Delphi+Firebird с логином через БД, последнее время занимаюсь веб-программированием. Поэтому могу сказать по опыту — серверные приложения гораздо проще в поддержке. В свою очередь могу вам посоветовать познакомиться с такими приложениями. Откроете для себя много нового)
Это вы свое решение рассказали. А теперь расскажите условие задачи.
Вы упускаете из виду что тут не все разбираются в вашей предметной области.
Кроме того, эта задача работает явно в контексте клиента
Далеко не факт. Очень многое зависит от административных регламентов и политик.
Если задача именно в таком виде, то я бы так и сделал, с временными таблицами и процедурами. Пусть 3% логики будут в базе, но хотя бы остальными 97% будет проще управлять.
Но вообще есть вопросы. Зачем на время операции генерирования блокировать таблицы? Почему нельзя пару тысяч записей вытянуть в оперативную память? Курсор и оконные функции это типичные вычисления в цикле с накоплением результата. Почему нельзя использовать код вида
while ($row = $query->fetchRow()) { do something }
? Джойн двух временных таблиц, подозреваю, можно заменить на группировку вида $orders[$client_id][] = $order
.Вот тут хороший пример был. Куча SQL с временными таблицами заменяется на 3 вложенных цикла.
Зачем на время операции генерирования блокировать таблицы?
Чтобы во время начисления не прошла операция выдачи или погашения. Нужно, как минимум, блокировать вставку новых платежей по конкретному счёту по которому в данный момент производится начисление. Знаете способ как блокировать вставку записей в таблицу с конкретным значением поля?
Способ есть, через блокировки. Блокируем строку счета на запись, и во всех операциях на изменение счета сначала читаем строку с этим счетом.
Если во время расчёта прошли какие-то операции — да и пусть их, они будут учтены при следующем расчёте.
Грозит проблемами не только с конкретным клиентом, но и с регулятором. Банально, у клиента 100 000 тела кредита и 10 000 процентов, нужно начислить ещё 100 процентов в полночь. Начинаем считать. Тут же приходит платеж от клиента на 20 000. Если мы его пишем без блокировок, не дожидаясь окончания расчёта, то, с одной стороны, мы распределим платеж как 10 000 на тело и 10 000 на проценты и рассчитаем проценты на 100 000, хотя должны бы по идее либо 10 100 на проценты и 9 900 на тело, либо 10000 на тело и 10000 на проценты, но тогда расчёт процентов должен быть не на 100000, а на 90000, грубо не 100, а 90. Клиент заметит лишних начисленных 10 рублей или не погашенных 100 рублей процентов и побежит с жалобой в ЦБ РФ.
А проценты на тело и проценты на проценты как-то по-разному считаются?.. Надо просто записать платеж на 20 000 целиком, куда он там достался — можно определить позже, при следующем расчете процентов.
Мы его пишем без блокировок, но отнюдь никуда не распределяем. Мы просто создаём запись в таблице транзакций, проставляя время (00:01) и сумму (20к). Так как отметка времени позже полуночи, то в расчёт процентов предыдущего дня этот платёж не попадает. Полночь — это момент отсечки, ровно так же, как если бы мы заблокировали БД.
Как раскидать сумму платежа по телу и по процентам — это должно рассчитываться совершенно отдельно, где-то на этапе составления отчётов и расчёта процентов при закрытии следующего дня.
проставляя время (00:01)
Грязный хак. Особенно быстро выплывет, если платеж принимается не самостоятельно, а через внешних посредников типа платежных систем — у клиента в чеке 00:00:00, а у вас 00.00.01 в выписке по счёту.
это должно рассчитываться совершенно отдельно
Это может рассчитываться отдельно, если у регуляторов нет требований выдавать клиенту в момент оплаты чек, квитанцию, корешок к ПКО с чётким разбиением уплаченной суммы на тело, проценты, пеню и т. п.
Если регуляторы требуют выдать квитанцию с немедленной раскладкой, то надо принять платёж без блокирования таблиц, но временно приостановить выдачу квитанции. Клиенту так и так пришлось бы сидеть и ждать окончания расчёта, ну так пусть это будет сделано lock-free.
Точно так же все будут ждать результатов платежа, если заблокировать таблицу. При этом конца расчёта будет ждать не один клиент, а несколько. Если делать без блокировки, то ждать будет только тот, у кого проценты за день ещё не подсчитаны. Если пересчёт делать индивидуально, маленькими кусками, а не одной большой задачей по всей базе, то время ожидания будет миллисекунды.
Это не хак, это упрощение. Надо просто милисекунды хранить. Их и показывать-то необязательно.
Хуже с неодинаковостью времени на разных серверах.
Вот вам и предлагают не использовать циклы в бд. После отдачи сырых данных на сервер приложения, БД свободна (за исключением блокировок по клиенту), а циклы будут бегать на сервере приложений, где это не зло а великое благо.
Миллион записей — как раз ерунда. Если, конечно же, это раз в день делать, а не раз в секунду.
Вот если записей десяток миллиардов — это уже проблема...
по миллиону? да хоть на мобильном телефоне, даже не подлагнет. Самое медленное что сейчас у меня есть в проектах это база банных (UPDATE\INSERT\SELECT -в порядке скорости). Горизонтальный шардинг пока решает проблемы.
В отличии от комментаторов выше хранимки мы используем, но мало, и там где от них вреда нет. В текущем проекте они обновляют ровно одну таблицу и не требовательны к производительности
И в чем же проблема посмотреть остаток по счету в реплике пока его меняют в мастере?
Деньги на этом конкретном счёту перемещаются каждую секунду, и отчёт по остатку должен быть актуален с точностью до секунды? Или всё же допускается увидеть в отчёте «Ваш баланс по состоянию на DD/MM/YYYY hh:mm:ss»?
У логики в базе просто есть явные и довольно сильные ограничения, связанные с интеграцией вовне. Я их уже в другом комменте подробно излагал, и не вижу, чтобы это вызвало сильные возражения, так что не стану повторяться.
Ну т.е. для меня лично слова про боль и страдания — они как правило верны именно в этих случаях, когда вы не можете остаться внутри базы, и вынуждены интегрироваться.
Когда правила в АПП, для этих двух операций используют обертки, орээмы, еще х.з что
Орээмы делаются не для выполнения операций, а для организации кода. Чтобы можно было работать с 10 объектами, в которых по 10 связанных с ними методов, а не с 10 массивами и 100 процедурами.
Когда это в процедурах БД, это все делается сразу нативными запросами в БД.
Когда логика в приложении, в базу идут только простые запросы вида SELECT/INSERT/UPDATE/DELETE, которые можно сгенерировать автоматически.
Когда логика в базе, в базу идут сложные запросы вида "SELECT some_function(...)", где some_function имеет произвольное имя и произвольное количество параметров, и все это надо прописывать вручную, и которые помимо этого сами посылают простые запросы вида SELECT/INSERT/UPDATE/DELETE. И семантика не очень, когда для внесения изменений надо делать "SELECT".
В 100% случаев реализация БД в приложениях (для наших задач, примеры я приводил тут в комментариях) по скорости разработки и скорости внесения изменений проигрывала.
Естественно, если рассматривать толстый десктопный клиент с логином в БД, то вносить изменения будет проще в базу. А вот тонкий десктопный клиент с подключением к серверному приложению, тут уже не так все однозначно.
говоришь ему, хорошо, не вопрос, вот вам машина, реализуйте некую простую вещь, покажите, как это классно. Затраты времени в разы больше, а порой на порядки.
А вот скажите и нам тоже. Только не в общих словах, а конкретную задачу с конкретными требованиями, по которой можно построить прототип. Вот и оценим затраты.
Орээмы делаются не для выполнения операций, а для организации кода. Чтобы можно было работать с 10 объектами, в которых по 10 связанных с ними методов, а не с 10 массивами и 100 процедурами.
Орээмы делаются для изоляции объектной модели от базы данных. Чтобы не писать вручную скул запросы, а потом не пеобразовывать из результаты в обхекты и наоброт.
Я даже верю, что на PL/SQL можно написать аналог Business Rules Engine — но дело в том, что как правило это выйдет настолько непрактично, что этим будет невозможно пользоваться.
А бизнес логика в виде «если — то», какая хрен разница на каком языке напечатаны эти операторы?
В теории — никакой. На практике для тех или иных видов логики одни языки гораздо удобнее других.
Все. Без оберток, ореэмов и прочих методов.
Вот только логика почти вся на реляционной модели построена. Как показывает практика, обычно бизнес ставит задачи, оперируя терминами которые гораздо лучше ложатся на объектную модель, которая если в РСУБД и есть, то сбоку к реляционной пришита. Не будет бизнес ставить задачу в терминах реляций и кортежей, таблиц и строк, а вот в терминах сущностей и операций над ними — вполне.
Но говорить, что бизнес-логика в БД, это боль для всех случаев, очевидная глупость.
Обычно, когда так говорят, имеют в виду, что боль — это процесс разработки, особенно итеративной, ну и поддержки. Ну нет ещё для SQL РСУБД инструментов разработки сравнимых по удобству с современными инструментами для классических ЯП. Каждый раз матерюсь, когда нужно менять логику, которую был вынужден помещать в БД по соображениям быстродействия или большой сложности реализации другими путями. Более-менее нормально только добавлять что-то новое линейно. Изменения, особенно на нижних уровнях зависимостей — боль, проще для новой задачи повторить с небольшими модификациями нужные зависимости старой, чем немного их и старую задачу поправить. Банально переименовать процедуру в одной схеме, используемую в десятке мест, когда имеется такая же в другой, но используемая в сотне мест — куча ручной работы, автоматических инструментов либо нет, либо за ними вычищать дольше, чем вручную сделать.
В 100% случаев реализация БД в приложениях (для наших задач, примеры я приводил тут в комментариях) по скорости разработки и скорости внесения изменений проигрывала.
Ну, реализовывать БД в приложениях, конечно, не очень разумная идея. Данные в базах, логика в приложении. Если данные из базы попадают в приложение в адаптированном для приложении виде, теряя по дороге свою реляционную природу (да, я про ОРМ), а не программист либо адаптирует вручную, либо так и работает с реляцонными в непредусмотренной для этого среде, то непонятно в чём затык.
Сколько запросов вам придется оформить и отправить, для получения данных?
Вообще-то столько же сколько и вам. Просто появляется намного больше пространства для маневра в плане архитектуры приложения. Так же если логика реально сложная мы снижаем стоимость введение нового разработчика и в целом увеличиваем поддерживаемость системы.
И каждый запрос SQL сервер будет компилировать и строить план запроса,
в то время, как хранимая процедура уже откомпилирована и план запроса сохранен для нее.
план запросов хэшируется вне зависимости от того был ли это просто запрос или он выполнен их хранимки (во всяком случае в postgresql) и это логично. Что до разбора запроса — это занимает настолько мало времени что в целом им можно пренебречь.
А время тикает.
и вот тут в дело вступает возможност делать запросы паралельно. Например такие параметры как:
- стоимость маршрута
- весогабаритные характеристики груза
- объем перевезенных грузов с агрегацией по календарным месяцам
- проверяем статус других заказов которые ожидают отправки
мы можем произвести все эти операции одновременно так как они не зависят между собой. Далее мы просто в скриптике за миллисекунду что-то вычисляем и вперед.
А еще есть кэширование на стороне приложения и другие веселые радости. Ну или может нам для расчета маршрута надо по графу пройтись и тут проще вынести это добро в отдельную базу.
Если БД занимается только данными (для чего она и предназначена)
Глядя на средства, предоставляемые современными SQL СУБД, я бы уже не говорил так уверенно для чего они предназначены. Мощные языки описания процедур, триггеров и т. д., как встроенные, так и с возможностью подключать языки общего назначения типа Python или C#
Увы, но возможность подключения языков общего назначения зачастую преувеличена. То нельзя, это нельзя, сюда не ходи...
Например, в MS SQL Server внешнюю функцию нельзя использовать в качестве первичного ключа в индексированном представлении.
И чтобы контейнер этот управлял ресурсами, которые может потребить хранимая процедура aka приложение. А иначе криво написанная процедура на .Net выжрет всю память, займет все ядра процессора ненужной фигней, залочит какие-то нужные всем прочие ресурсы, и все к черту сломает.
Вот такого внутри баз данных пока нет. И пока этого нет — они могут и дальше продолжать над этим работать, но приложения все равно будет проще разворачивать снаружи.
Поздравляю вас с выходом из криосна!
Если я что-то опустил, подскажите: что такого делает первичный индекс, что не может «обычный»?
Первичный ключ (точнее — кластерный индекс, но обычно это одно и то же) делает представление, собственно, индексированным. Пока в представления не создан первичный ключ — на него нельзя вешать другие индексы.
Вы привели достаточно редкую ситуацию. Если сделать запрос create table (id primary key)
— то индекс по первичному ключу будет кластерным. Это — поведение по умолчанию.
Пожалуйста, не придирайтесь к словам. Кластерный индекс — специфичное для MS SQL понятие, вот я его и заменил на более понятное тем, кто работает с другими СУБД, рассчитывая что специалисты по MS SQL поймут что я имел в виду.
ох уж эти извечные холивары между DBA и бэкэндщиками...
но не привели ни одного примера бизнес привил, которые можно реализовать в АПП и нигде больше.
категоричность это не очень хорошо, согласен. Для хранимых процедур есть вполне валидные сценарии.
Но все сводится к банальной экономике. С точки зрения скорости разработки, стоимости поддержки и расширяемости решений процедурки выходят дороже. Вот и весь спор. Не говоря уже о таких вещах когда простой расчет стоимости или каких-то скидок не выйдет сделать на CPU за необходимое время (CUDA). Например у меня недавно приходил реквест на разработку системы, которая на основе координат точек рассчитывает необходимое количество материала. Материал и все параметры хранятся в базе но считать все в базе будет слишком сложно.
Мой опыт, говорит обратное.
А мой опыт говорит что хранимки это скорее исключение из правил. Когда есть специфические требования например связанные с безопасностью данных.
Когда подобные вещи лежат в хранимках вы полностью завязываетесь на инфраструктуре и это лишает вас гибкости. Да, в подавляющем большинстве случаев это ок, но и плюсы от хранимок в подавляющем большинстве случаев не столь значительны.
Ну и в целом из моего опыт найти разработчиков с вашим опытом работы с базами данных весьма и весьма сложно. Не говоря про стоимость разработчиков которые смогут это нормально организовать.
В целом спор выходит пустым. Мое мнение — впадать в крайности не очень хорошо. Хранимые процедуры лишь один из вариантов как делать дела и брать этот вариант по умолчанию так себе идея. Точно так же как никогда не допускать использования хранимок.
Вроде бы логично использовать для манипуляциями с данными, язык, специально для этого предназначенный.
Что мешает горизонтально масштабировать сервера БД?
И с обновлением процедур примерно так же, если есть репликация, например master-slave.
Допустим, у нас есть один сервер БД, нужно подключить slave и асинхронно реплицировать. Как это сделать, не выключая master?
Не знаю, таких задач у меня пока не было. Только по-моему, сервер приложений гораздо быстрее положит сервер БД, и вам всё равно его придётся масштабировать.
Вообще глядя на две длинные ветки обсуждения, я для себя сделал вывод, что хороши оба подхода, вместе, а не по отдельности. Потому что то, что генерят некоторые ORM не лезет ни в какие ворота.
Потому что то, что генерят некоторые ORM не лезет ни в какие ворота.
Открою вам еще один секрет :)
ORM генерируют запросы не из вредности — а потому что им на вход запрос пришел. И то, что подается ORM на вход, обычно точно так же поддается оптимизации.
Хотя, конечно же, ее пределы более ограничены нежели пределы оптимизации SQL-запросов.
А если реально — то когда метод delete что-то достает из базы, прежде чем это удалить — это тривиально баг. Абстракции тут не при чем, ни дырявые, ни какие либо другие.
Я согласен, что ORM может такие абстракции содержать, по разным причинам — но данный пример кажется неудачным.
Не совсем так. Тут не баг реализации — а принципиальное ограничение архитектуры, и интерфейс его хорошо иллюстрирует.
Посмотрите что метод принимает — он принимает последовательность объектов. Именно поэтому и говорится, что в ORM перед удалением записи ее надо сначала вытянуть из базы. Если не вытянуть — то и передавать в delete
будет нечего.
Обходные пути, конечно же, существуют. Если известные первичные ключи объектов — можно создать "пустышки", присоединить их к контексту ORM — а потом удалить.
И еще всегда можно сделать "сырой" SQL-запрос к базе.
void delete(ID id) вот вам метод удаления по id, он на пол-страницы выше.
а то о чем вы толкуете — это метод для удаления сущностей, которые вы уже ранее загрузили.
Зачем вы их загрузили — это ваше дело, например чтобы показать пользователю, который нажмет кнопку Del.
При чем тут архитектура, если вы невнимательно изучили список методов?
void delete(ID id)
пользуйтесь этим, если у вас есть id, и вам не нужно смотреть на данные, прежде чем их удалять. Делать какие-то выводы из того, что есть еще и другой метод, довольно странно.
Блин, приведите уже ссылку на исходники! Нам не интересно гуглить всякую фигню.
Если у вас есть id, и вы знаете маппинг полей объекта на колонки таблицы — то вы можете сгенерировать просто DELETE. Тут нет никаких текущих абстракций в этом месте.
Если же вы о чем-то другом — то давайте уточним, о чем конкретно?
С приложением на сервере будет точно так же. Изменили код расчета на сервере, а клиенты, которые к нему коннектятся, менять не надо. Только можно использовать ООП, внешние библиотеки, систему контроля версий, и другие полезные вещи типа пошаговой отладки.
Итак, для начала — есть случаи, когда разных типов баз много. В этом случае вариант логики в хранимках воообще практически отпадает, потому что написать что-то одинаковое не на чем, а написать три-четыре разных варианта для оракла, ms sql и postgresql — неподъемная задача.
Во-вторых, берем скажем MS SQL для определенности. Натыкаемся на то, что T-SQL как язык разработки достаточно примитивен, если не сказать прямо убог.
Ну например, из функций вообще ничего делать нельзя, из процедур можно — но скажем набор возвращаемых процедурой типов данных весьма ограничен (по сути — сводится к таблицам). Если мне нужно что-то более сложное — скажем дерево, то все, капец.
Вернуть из процедуры что-либо нетривиальное — боль и страдания. Передать в нее что-то нетривиальное — боль и страдания. Посмотрите для примера, во что выливается разбор JSON в T-SQL — для этого просто нет приличных слов.
Дальше — если из приложения я могу развернуть наружу скажем веб-сервисы, как RESTJson, так и например SOAP, или слушать сокеты и работать по TCP/IP, или webcocket, например, или поднять полноценный web сервер и отдавать html, и сделать web GUI, или если очень нужно, отдавать наружу какую-нибудь Corba — то попробуйте сделать это внутри T-SQL. У вас либо просто нифига не выйдет, либо вы будете вынуждены деплоить процедуры на .Net, что само по себе боль и страдания.
В случае оракла все чуть-чуть легче, потому что внутри базы живет JVM, и можно что-то написать на Java — но опять же, это боль и страдания — потому что преимущества дает весьма сомнительные, и сделать все тоже самое вне базы обычно намного проще.
Ну и последнее, уже озвученное — как правило, языки разработки вне базы намного более продвинутые и удобные, нежели те, что встроены в СУБД. Это не факт — это больше чем факт, так оно и было на самом деле (с)
Не, ну это я надеюсь мы все понимаем одинаково. Моя мысль заключалась скорее в том, если мы уже делаем что-то (обычно эта штука называется сервер приложений) для взаимодействия и пр., то сделать там же и бизнес логику обычно является логичным решением. И там как правило удобнее.
Что же до данных — то вы попробуйте тоже абстрагироваться от своего видения, и представить, что данные изначально бывают и не в базе, и возможно никогда туда не попадают вообще. А живут в виде сообщений где-нибудь в шине, где и логичнее их обрабатывать. Не говоря уже о том, что бывают базы и других видов, отличных от оракла, где просто не на чем писать бизнес логику вообще.
И опять же, главная мысль была в том, что вне базы для обработки обычно намного больше всяких хороших вариантов.
Вы можете важные данные (например вашего счета в банке) донести на бумажке, написанные ручкой или фото бумажное показать.
Без всякой интернет лабуды.
Данные, основа бизнес приложений.
Противоречите сами себе. Основа бизнес-приложений — это логика обработки данных. Данные могут быть в базе, в обычных файлах, на веб-сервисах, на бумажках и т. п. Бизнес-логика их обработки не должна зависеть от формы их хранения. То же начисление процентов — вполне можно представить приложение, в котором ночью люди сначала инсталлируют софт на машину, потом достают карточки счетов, вводят в формы всех операции по счёту, получают результат начисления, записывают его в карточку, а потом форматируют машину. Зачем мне размещать логику начисления в базе в таком случае? Я размещаю её в приложении и пишу адаптеры для всяких разных источников данных, включая базы. Завтра придёт директор и скажет, что хранить в своей базе слишком дорого (трехкратное резервирование, пять стратегий резервного копирования, 24/7 дежурство персонала) и он нашел облако, которое все дешево хранит и отдает по рест-лайк интерфейсу. Я просто напишу новый адаптер. Перехожу с MySQL на PostgreSQL — даже писать не надо, просто заменю один чужой адаптер на другой чужой. Вот, кстати, уже больше чем на год у нас затянулся переход с MySQL на PostgreSQL — бизнес-логика от базы независима, но вот отчёты писались на raw SQL и их логику никто объяснить не может, а единственный кто может её разобрать — я. Ни того, кто ставил задачу нет уже в нашей фирме, ни того, кто реализовывал, но отчётами все пользуются. Но у меня в работе куча прямых бизнес-задач — новая функциональность, адаптация под оптимизированные бизнес-процессы и изменяющееся законодательство и т. д. и просто нет времени разбираться в логике, реализованной на MySQL и переносить её на PostgreSQL. И это просто SELECT запросы по сути, а не процедуры. Но в MySQL нет оконных функций, но есть пользовательские переменные в SQL, а в Postgres наоборот.
1. Переключатся по десять раз на дню, в разные (по времени) версии ПО и БД и что то там дописывать, потом сливать, странно. Приходит в голову, что вы поддерживаете десятки различных версий одной программы, возможно для различных клиентов. Ваша программа разрослась десятком версий, при таком варианте очень сложно поддерживать архитектуру.
Нет, программа одна для одного заказчика. Задач в работе может быть несколько. Ну и принимаю задачи от всех остальных разработчиков. Сначала проверить задачу в его ветке, потом слить её в общую ветку, потом проверить что общая осталась рабатоспособной.
Необходимость постоянно переименовывать процедуры в БД. Там, где я работал, это крайне редкая операция. Если вы постоянно вынужденны заниматься таким, что то не так с проектированием.
Мелкий рефакторинг неотъемлемая часть разработки у меня. А переименование одна из частых процедур в рефакторинге.
Если проект затянулся на год, он не проект, а хотелка без ресурсов. Значит у него нет заказчика, готового за него платить. Закройте такой проект и не отвлекайтесь на него.
Проекту 6 лет, переход на Постгрес — лишь одна из задач в нём.
Кроме того, есть некоторая противоречивость в примерах.
Нет противоречивости. Там, где логика расчётов осталась в приложении, там переход не составляет практически никаких проблем, просто переключить адаптер и несколько строк подправить, там, где использовали специфичные для мускуля фичи типа CAST AS UNSIGNED в сортировке, а в Postgres надо AS INTEGER. Там, где логика перенесена на сторону СУБД — начинаются проблемы.
И, замечу, не просто видится, а есть успешно реализованные проекты, поддерживаемые замечу не мной одним, а командами программистов независимых от меня, что подтверждает мое мнение.
Если вам удобно и команде которая поддерживает ваши решения тоже удобно — значит все хорошо. Но опять же интересна специфика проектов. Предметная область. Нагрузки и количество данных. В целом ваш подход весьма интересен и я бы хотел хоть одним глазком глянуть на подобный проект, для расширения кругозора скажем. Но вот вам мои мысли исходя из моего опыта.
Есть данные, хранящиеся в БД.
На самом деле, это ценность для бизнеса.
…
Данные, основа бизнес приложений.
Ключ ко всему данные.
Несомненно данные важны, но они бесполезны без некой логики обработки этих самых данных. И вот это уже представляет основную ценность для бизнеса. А как вы это реализуете — бизнесу плевать. Гдавное что бы это работало, работало стабильно, и реализация позволяла относительно быстро вносить изменения. И вот тут самое интересное.
В БД лежат данные и правила работы с данными (процедуры).
В моем представлении "процедуры" имеют доступ ко всем данным, то есть данные как бы лежат в глобальной области видимости. Глобальное состояние. Меня интересует как вы подходите к изоляции данных для процедур и изоляции сайд эффектов. К примеру.
У нас есть данные и процедуры для работы с этими данными. Данные лежат отдельно, процедуры — отдельно. То есть старое доброе процедурное программирование. Данные для процедур доступны глобально, так что любая процедура может в любой момент времени что-то сделать. Есть ли у вас тут какая-то изоляция? Ибо подавляющее большинство разработчиков на проекте больше среднего уже скорее всего превратят все в кашу и отследить что кто меняет уже будет мягко скажем затруднительно.
И еще момент. У нас есть какие-то данные и есть инварианты этих данных. Далеко не все инварианты можно покрыть средствами которые дает SQL (хотя тут возможно отсутствие опыта играет свою роль). Можете чуть объяснить как вы проверяете например такие вещи как "пользователь не может купить продуктов более чем на 120% от покупок за предыдущий календарный месяц"? Ну то есть… вы делаете для этого отдельную процедуру? или проверку на тригеры вешаете? Как вы поступаете?
в нем на самом деле перемешивалась бизнес логика и логика визуализации данных.
То есть вырожденными были и представления, не только модель?
Отдельное спасибо за качество следопыта в Вас, когда разбираешься, пока истина не покажет свое лицо. Люблю таких людей
Жду вторую часть
Целью статьи вовсе не являлось сказать что «original MVC» единственно правильный. Именно поэтому вначале дан обзор и приведены почти все основные схемы и варианты. Каждый может выбрать что ему больше подходит…
Лично для меня было очень полезно узнать с чего «все начиналось». Мне хотелось показать что исходный MVC с одной стороны был намного сложнее и богаче чем большинство упрощенных схем, которые нам обычно преподносятся в качестве MVC, а с другой — он довольно прост, логичен и буквально «выводится» из архитектурных принципов. Поэтому понимая суть можно его граммотно варьировать и адаптировать под свои нужды. Например у нас тоже не используется шаблон Наблюдатель просто потому что синхронизация приложения идет по таймеру. Но от этого MVC не перестает быть MVC.
Вопросы связанные с проектированием базы данных и логики приложения… Дело в том что тут двоякая ситуация. По большому счету база+логика это та самая доменная модель. И MVC не дает и не может дать вам ответ как лучше проектировать доменную модель. Он лишь говорит как организовать взаимодействие этой самой модели с пользовательским интерфейсом (лучше через фасад). Поэтому когда вам говорят что вы не так спроектировали потому что MVC… то это конечно же не правда. Но!!! к мнению и советам коллег стоит очень внимательно прислушаться потому как именно Вебщики постоянно сталкиваются с проектированием базы и сервера, знают что там за засады и какие решения лучше применять. Так что предложение вынести веб в в отдельную тему оно очень даже разумно.
А вот по поводу проектирования пользовательского интерфейса MVC есть что сказать. Конечно же интерфейс вовсе не обязан быть тупым, и современный веб наглядное этому подверждение. Главный вопрос — как же сделать его умным и при этом не смешивать логику GUI с графическими компонентами? Собственно об этом и будет вторая часть
ЗЫ на самом деле всем спасибо за терпимость. мне казалось что в столь «религиозной» теме за нетрадиционность подхода меня сразу закидают камнями
По своему опыту я всегда не мог согласиться с MVC, которые диктует тот или иной фреймворк. В первую очередь, не потому что фреймворк плох (смотри ниже второе), а потому что его пользователи, мои коллеги, отталкивались в своих спорах от этого фреймворка. Поясню. У меня всегда было стойкое ощущение, что споры об MVC не от того правильно или неправильно кто-то использует конкретный фреймворк. А от непонимания, что данный конкретный фреймворк разработан со своим видением MVC. Да, все банально, но часто происходит так: Что такое MVC? А вот он в этом фреймворке. И далее гадание на кофейной гуще и ориентация на мнение авторитетов или собственные эксперименты. Т.е. нужно разделять: «Ты работаешь не так как принято с этим фреймворком» и «Ты не понимаешь принципы MVC».
Второе, а так ли я готов согласиться с моделью конкретного фреймворка, нужно обсуждать только после того как понял и принял первое. И уже здесь приходит понимание, что нужны четкие формализованные правила, с помощью которых можно рассмотреть все основные реализации MVC на предмет «удобно или не удобно» работать. И в статье сразу же бросается в глаза:
- Модель с точки зрения реализации — это интерфейс к бизнес логике. Просто в точку.
- Разделение на контроллер лежит на следующей уровне иерархии после разделение на «Модель предметной области» и «Взаимодействие с пользователем».
String[] getFIO();
клиент должен получить готовый список из 10 строк с Ф.И.О. людей…
А формироваться этот список должен внутри доменной модели, котороя знает как наиболее оптимальным образом вытащить эти данные из базы или еще откуда. В крайнем случае этот список может формировать фасад… но вообще это опасная дорожка по направлению к тому что фасад начнет реализовывать бизнесс-логику. Просто в вашем примере только база и фасад и мне пока не очень понятно где там находится бизнесс-логика
как-то нелогично тянуть из БД 50 объектов, чтоьы отобразить такой простой список.
Потому часто разделяют модель чтения и записи. То есть что бы отобразить данные 10-ти человек вы грузите не 10 агрегатов (это как-то противоестественно в терминах DDD так как вы должны работать с одним агрегатом в рамках бизнес-транзакции), а делаете один SQL запрос который выбирает нужные данные. Эта выборка будет не в репозитории (поскольку он возвращает корень агрегата).
SQL-запрос к базе, сделанный из фасада или, упаси боже, из контроллера, противоречит всем принципам разбиения приложения на слои!
Да, так делать можно. Лишь бы только это не было преждевременной оптимизацией или трудным в поддержке костылем.
Лишь бы только это не было преждевременной оптимизацией или трудным в поддержке костылем.
Если проще. Вместо одного класса который бы занимался и записью и чтением у вас будет два класса. Один отвечает за запись данных, а другой за выборки которые мы используем для формирования UI. С точки зрения single responsibility это лучше поскольку причины для изменений у этих классов будут несколько разные. Но количество кода остается одинаковым. Иногда даже существенно уменьшается. И существенно снижается сложность самой системы.
Зато появляется неявная зависимость между классами, к тому же невидимая для инструментов рефакторинга.
Если будем менять схему БД — надо будет переписывать не только репозитории, но и все отчеты, которые для изменяемых таблиц насоздавали.
Но при этом больше шансов углядеть необходимость переделывания запросов еще до принятия решения о переделывании схемы БД :)
больше шансов углядеть
шанс проглядеть остается а этого хватает. Но вообще тесты надо писать.
Тесты сработают только когда схема БД будет уже изменена. А надо бы увидеть сложные выборки еще до того как новую схему начнут делать.
А надо бы увидеть сложные выборки еще до того как новую схему начнут делать.
зачем?
Чтобы не сделать такую структуру БД, на которой нужные выборки станут невозможными.
вы говорили что "тесты сработают только когда схема БД будет уже изменена" и это ровно то что нужно от тестов. Убедиться (не на 100% конечно) что нет регрессий. Обычно все же если мы изменили схему базы то даже на позитивных тест кейсах только можно узнать что все плохо.
Речь же шла о том как минимизировать человеческий фактор.
противоречит всем принципам разбиения приложения на слои!
Вы любите лазанью?
А формироваться этот список должен внутри доменной модели, котороя знает как наиболее оптимальным образом вытащить эти данные из базы или еще откуда.
Не согласен, потому что доменной модели должно быть все равно на отображение, и оптимальности на этом уровне никакой нет.
В крайнем случае этот список может формировать фасад…
Именно так я и понял из вашей статьи, но фасад скрывает доменную модель, которая содержит данные и бизнес-логику. Если вы знакомы с DDD, то понимаете, что репозиторий PersonRepository, возвращает АГРЕГАТ — Person, а он состоит из 5-ти объектов, следовательно 5 * 10 = 50!
Грубый пример — поисковый робот который бегает по сети и индексирует где и что находится, чтобы, когда вы обратитесь к поисковику, вам информация была выдана быстро и удобно. Роботу, который в этом случае и будет приложением «все равно на отображение» но тем не менее его работа заключается именно в том чтобы сделать доступ к информации быстрой и удобной.
Мне кажется что в вашем случае (и вообще при работе с базами и не базами) все тоже самое только в предельно упрощенном виде — должно быть некое приложение или модуль в приложении (и этот модуль будет относится именно к доменной модели в терминологии MVC) который знает где находится информация и как ее оптимальным образом извлечь и в удобной форме вернуть для дальнейшего использования. Он может ее просто извлекать, или частично кэшировать для быстрого дальнейшего доступа… не суть важно, важно чтобы именно в доменной модели за эту задачу кто-то отвечал.
И если удобная форма это просто набор строк… То значит этот модуль не должен тащить объекты а должен с помощью SQL запроса извлечь из базы лишь нужные вам строки и вернуть (как и написал Volch). А уж что вы с этими строками будете делать дальше это уже ваше дело — можете отображать, можете для какой-нибудь аналитики или отчетов использовать.
Когда (и если) возникла необходимость в оптимизации можно перейти к чему-то подобному (2)
Поскольку интерфейс фасада в обоих случаях будет одинаков то на клиенте переход от 1 к 2 никак не отразится. Именно в этом и заключается смысл фасада — можно менять конкретную реализацию бизнесс модели а клиент этого даже не заметит

Сам не вытерпел и решил посмотреть, «как же было в оригинале». Нашёл статью некоего S. Burbeck: http://www.math.sfedu.ru/smalltalk/gui/mvc.pdf 1992 года.
По результату прочтения несколько моментов.
В вашей статье есть какой-то акцент на применении Facade для реализации Модели. Burbeck же пишет, что Моделью может быть любой объект Smalltalk, просто при использовании «тупых объектов» — т.н. «пассивной Модели» мы не получаем функционал оповещения Вида при изменениях.
То есть по факту, разделение объектов на Модель-Вид-Контроллер в Smalltalk довольно тонко заточено именно под тамошнюю оконно-графическую модель. И Модель в отрыве от виджета (который состоит из пары Вид-Контроллер) не несёт никакого особого смысла — это просто объект Smalltalk (возможно, составной).
Соответственно, получается, что разделение UI на Вид и Контроллер в отрыве от соответствующей графической (точнее, UI-) библиотеки бессмысленно. А в рамках конкретной библиотеки может существовать другое деление, в т.ч. на бОльшее количество компонентов. Насколько я понял из вышеприведённой, в контексте Smalltalk это разделение позволяло повторно использовать одинаковые Контроллеры разными Видами, причём основной функционал Контроллера, кроме примитивной маршрутизации событий в стандартизованные сообщения, заключался в трансляции экранных координат мыши в локальные оконные — путём опроса соответствующих Видов. Этот функционал похож у многих виджетов, поэтому логично было выделить его из кода, описывающего конкретный UI.
Ещё интересная тема — иерархический MVC (HMVC — он же PAC), где контроллер выделяется как маршрутизатор всех событий компонента, а не только UI`шных.
Burbeck же пишет, что Моделью может быть любой объект Smalltalk
Не для всех доменных моделей нужен фасад. Фасад применяют обычно когда нужно обеспечить единую высокоуровневую точку доступа к сложным графам объектом. Если такового графа нет или он не сложный для внешнего потребителя (например, вся модель — это агрегат из DDD), то и фасад не нужен.
Necessarily, any object can be a model.
Отчасти в силу того, что на момент написания не было подходящей устоявшейся терминологии и идеологии. Эта фраза из середины объяснения Бурбека, что такое вообще модель. Заканчивает объяснение он как раз «фасадом»:
Because any object can play the role of model, the basic
behavior required for models to participate in the MVC paradigm is inherited from class
Object which is the class that is a superclass of all possible models.
в 1992 году еще не придумали паттерн «фасад».
Не придумали названия, сам паттерн существовал задолго до этого. Не забывайте что GoF вышедший в 94-ом году содержал лишь названия и обобщения для решений которые существовали уже давно. Ну это я так, брюзжу.
То есть по факту, разделение объектов на Модель-Вид-Контроллер в Smalltalk довольно тонко заточено именно под тамошнюю оконно-графическую модель.
Рекомендую почитать: Thing-Model-View-Editor — это ма й 79-ого года. Первое упоминание MVC было вроде как в декабре. Словом можете глянуть что Тругве думал по этому поводу. Там особое внимание уделяется UI. У меня валяется кусок перевода этой статьи, если посчитаете этот материал полезным могу опубликовать перевод.
Только читается очень тяжело.
У меня была безумная идея сделать что-то типа музея, мол старые публикации с пояснениями и обновленной графикой, вроде этой публикации. В частности хотел запихнуть туда "goto considered harmful" (ибо это письмо очень хорошо отвечает на вопрос почему глобальные переменные это не очень удобно) и т.п. Что-то типа "почему в программировании все так как сейчас и как все начиналось". Но на это надо времени убить кучу...
По поводу Модели… Вы правы, если читать лишь описания то и у Барбека и у Краснера действительно написано что модель это доменный объект (любой объект Smalltalk) как вы верно и заметили. А вот на практике всегда был «фасад» между ними. И специальный объект Model от которого наследовались все модели был выделен особо, а это значит что опять таки на практике не любой объект Смоллтолк мог быть моделью. Это то и есть самое интересное. Предполаю что связано это с тем что тогда действительно не было шаблонов, они были «первопроходцами», поэтому практика опережала «теорию». Кстати у Реенскауга в описании фасад тоже появился позже…
А вот насчет бессмысленности разделения UI на вид и контроллер в отрыве от библиотеке… Тут с вами не соглашусь пока. Во второй части попробую показать что это штука довольно универсальная и существующая не только в программах а вообще почти в любом интерфейсе
По поводу Модели-Фасада: понятно, что любой более-менее нетривиальный UI-компонент имеет достаточно сложный «бэкэнд», что бы его можно было реализовать одним пассивным объектом.
Тут ещё другой момент, который я имел в виду:
Методы Модели-Фасада для того или иного компонента могут возвращать сложные объекты. Так вот, архитектурная проблема: имеют ли право методы Фасада возвращать ссылки на объекты, доступные из других фасадов, который этот Фасад скрывает (и в конце концов, ссылки на конкретные пассивные объекты, которые содержат данные)? Или Фасад должен возвращать только объекты-Прокси для доступа к реальным объектам? Или ещё как-то?
Если мы берем сферический вакуум, то по хорошему каждый метод фасада должен возвращать только DTO которое нужно в этом конкретном случае. Есть конечно ситуации при которых вы хотите что-то реюзать, но тут надо больше про связанность и зацепление думать нежели о MVC. Ну то есть банальные принципы разбиения на модули и слои.
А хороший фасад является ещё и адаптером (вообще сложно бывает провести между ними разницу), не тупо возвращает копии или прокси (прокси, кстати, копируют интерфейс проксируемого объекта, включая его мутаторы, что не является изоляцией по сути), а адаптирует их к нуждам клиента. MVC-приложению часто для большинства операций не нужно полное представление доменного объекта, запрошенного у модели и достаточно передать урезанную версию, зачастую даже в структуре отличной от доменной, например, приложение интересует только ФИО клиента и остаток на его счету, ему не нужны все данные клиента и список всех операций по счёту. Модель в этом случае может отдать контроллеру или представлению плоский DTO (а то и массив) из четырех полей, не просто пряча данные модели от остального приложения, но пряча и структуру модели. Что хорошо как с точки зрения защиты данных от НСД, так и с точки зрения защиты VC от изменений в M, а внутренностей M (доменной модели, инфраструктуры и т. д. ) от изменений требований VC, если они реализуемы без изменения модели, например потребовалось выводить не только итоговый баланс, но и разбивать его на доходную и расходную часть. С одной стороны, вроде, это функция вью по полному списку операций составить их баланс, а, с другой, для простого отображения итогов вью потребуется слишко много знаний о внутренней структуре доменной модели и связь вью-модель станет слишком хрупкой,
Должен или не должен определяется требованиями и выделенными ресурсами. Введение дополнительных уровней изоляции увеличивает как трудоемкость разработки приложения, так и ресурсы, потребляемые ею в рантайме.
Один из ключевых моментов, которые делают микросервисную архитектуру реально полезной и работающей — как раз эта изоляция. Именно ради этой изоляции выдвинуто требование, что у каждого микросервиса должна быть собственная БД, куда нет доступа другим микросервисам. А для передачи данных между двумя частями приложения приходится их сериализовывать и передавать по сети. Что, безусловно, заметно жрёт ресурсы и увеличивает трудоёмкость. Но, тем не менее, это окупается тем, что нарушить слой изоляции между микросервисами невозможно при всём желании.
Теоретически, безусловно, можно обеспечить ровно такую же изоляцию между двумя модулями/пакетами/объектами в рамках одного физического приложения, не разделяя его на несколько независимых микросервисов. Но ключевое слово здесь — теоретически. А на практике абсолютное большинство разработчиков не в состоянии удерживаться от нарушения этих "виртуальных" границ, более того, зачастую они даже не дают себе труд понять где эти границы проходят и почему их категорически нельзя нарушать. Ведь всегда кажется, что если нарушить их совсем чуть-чуть — то это как-бы и не нарушение вовсе… зато можно намного быстрее и не включая голову сделать нужную фичу.
Мне очень хочется найти какой-то подход, доступный большинству разработчиков, который бы решил эту проблему, и сделал реальной разработку больших монолитных приложений с жёстким соблюдением границ между их внутренними модулями/пакетами/объектами.
Но реально и на практике я такое видел только в одном месте — в языке Limbo, который работает только под OS Inferno. Там сама OS обеспечивает необходимый уровень изоляции — по сути, там можно отдельные библиотеки или части приложения (функции, по сути) запускать в отдельном лёгком потоке работающем в собственном уникальном окружении (а-ля продвинутый chroot), при этом доступ в память есть только к тем объектам, которые этому потоку передали параметрами при запуске или позднее прислали по типизированному каналу. В результате получается, что у каждого такого модуля (или даже функции) программист описывает интерфейс, к чему него есть доступ, и после запуска ни этот модуль не может получить доступ к чему-либо выходящему за рамки этого интерфейса, ни кто-то другой не может получить доступ к внутренностям этого модуля если он заранее не предоставил к этим внутренностям соответствующий интерфейс.
Язык Limbo вы сюда зря привели. Он ну никак не помешает "нарушать границы" неаккуратному программисту — ведь это делается без всяких хитрых трюков с памятью.
Один из ключевых моментов, которые делают микросервисную архитектуру реально полезной и работающей — как раз эта изоляция.
В контексте этого топика, говоря об MVC и фасадах к доменной модели в качестве модели приложения, мы говорим об изоляции в рамках одного микросервиса, построенного по MVC паттерну. Контроллер принимает от клиентов микросервиса запросы, обращается к фасаду и сериализует ответ фасада в ответе клиенту. От клиентов микросервиса доменная модель и так изолирована как минимум сериализацией (сериализатор — вью по сути), особо большого смысла изолировать сущности домена от контроллера часто нет. Фасадом сложность доменной модели просто прячется от контроллера, но формировать специально для контроллера отдельный DTO часто не имеет смысла на практике, особенно когда «тупые» контроллеры (запрос к фасаду, сериализация ответа) пишет тот же человек, который пишет фасад и всё это в рамках одной задачи. Да, это красиво, правильно, но трудоемко при добавлении нового свойства в модель повторять его не дважды (в самой модели и в сериализаторе), а трижды (между моделью и сериализатором).
Именно ради этой изоляции выдвинуто требование, что у каждого микросервиса должна быть собственная БД, куда нет доступа другим микросервисам.
Это не требование, а часто используемая практика. В конце-концов, СУБД тоже своего рода микросервис, которому никто не запрещает иметь несколько клиентов в рамках системы.
В контексте этого топика, говоря об MVC и фасадах к доменной модели в качестве модели приложения, мы говорим об изоляции в рамках одного микросервиса, построенного по MVC паттерну.
Э… почему это? В рамках этого топика мы говорим о разработке приложения, например веб-сайта, бэкенд которого вполне может быть построен по микросервисной архитектуре, и один (или несколько) из микросервисов вполне могут реализовывать этот самый интерфейс фасада, избавляя веб-интерфейс или мобильные клиенты от необходимости делать запросы напрямую в микросервисы реализующие доменную модель.
В самих микросервисах внутри тоже может быть MVC, явно или неявно (как Вы описали), но зачастую в микросервисах полезного кода строк 50-100, шансов что он будет активно усложняться и развиваться практически нет (а если и будет — проще сделать новый микросервис), и заморачиваться там какой-либо архитектурой вообще нет смысла.
MVC это архитектурный подход. Его можно применять внутри какого-то модуля/библиотеки, строить по нему отдельное приложение (процесс ОС), и ровно так же строить по нему распределённое приложение состоящее из десятков микросервисов.
Мой изначальный коммент был про то, что когда у нас MVC в рамках одного процесса ОС, то соблюдать границы (не лазить в модель мимо фасада, не пропускать через фасад наружу исходные объекты доменной модели через которые опять же можно узнать лишние детали реализации модели и даже изменять модель мимо фасада, etc.) становится очень сложным т.к. язык/ОС не помогают соблюдать границы и это становится вопросом внутренней дисциплины разработчиков.
но зачастую в микросервисах полезного кода строк 50-100
микро в микросервисах не выражается в количестве кода. Микросервис на 10К строк — норм покуда он отвечает за одну область ответственности бизнеса. А не микро он становится когда начинает работать в пределах двух областей ответственности и более.
Конечно, но мы же здесь не микросервисы обсуждаем, а MVC. Если в микросервисе 10К строк — без какой-то архитектуры (может и MVC) внутри этого микросервиса, в большинстве случаев, будет очень неуютно. А если в нём 50-100 строк, то часто можно архитектурой не заморачиваться.
MVC не архитектура всего приложения, это лишь способ отделения UI логики и логики обработки данных. На этом собственно можно и закончить.
А если в нём 50-100 строк, то часто можно архитектурой не заморачиваться.
Собственно тогда можно чуть по другому сказать, без привязки к микросервисам. Если вы можете разделить систему на изолированные модули адекватно, то не важно как и что там внутри этого изолированного модуля. Но чтобы так сделать придется сильно много думать об архитектуре. А микросервисы лишь налагают физические ограничения на эти модули. Так что если не думать об этой самой архитектуре боль будет точно и ее будет много.
Это не требование, а часто используемая практика. В конце-концов, СУБД тоже своего рода микросервис, которому никто не запрещает иметь несколько клиентов в рамках системы.
Это именно что требование. Как только у микросервисов появляется общая БД и начинаются философские рассуждения о том, что формально СУБД это тоже сервис (на микро он, вообще-то, не тянет) — то "Хьюстон, у нас проблема".
Общая СУБД — это, по сути, аналог громадной и сложной глобальной переменной, с которой одновременно работает куча разных кусков кода. Обложившись блокировками, транзакциями и версионированием схемы БД чтобы всё это хоть как-то работало.
Суть микросервисного подхода в том, что пока стабильно работают API сторонних микросервисов и стабильно работает API этого микросервиса — мы можем делать что угодно и как угодно в самом микросервисе, и это гарантированно ничего не сломает в других местах. Поэтому несовместимых изменений API стараются избегать из всех сил, потому что это единственное, что может поломать что угодно и где угодно. Хотя СУБД это безусловно сервис, но у этого сервиса нет стабильно работающего API — я имею в виду достаточно высокоуровневого API, которое бы гарантировало что сделав вот такой запрос в базу я получу нужные мне данные в корректном формате — потому что изменение схемы БД или ошибка в коде изменяющем данные всё это ломают.
На практике, безусловно, общие БД у сервисов встречаются. Но чтобы это более-менее нормально работало приходится относится к схеме БД как к публичному API — документировать его, стараться не изменять несовместимым образом, добавлять в саму БД функции контролирующие корректность записываемых значений и целостность данных… и работает это довольно паршиво. Потому что БД — это, всё-таки, не фасад а модель, возвращаясь к теме топика. И, по хорошему, перед такой общей БД надо вешать отдельный микросервис, который даст API к этой БД в стиле фасада и терминах бизнес-логики приложения, и скроет саму БД от всех остальных микросервисов.
ViewModel?
Спасибо, отличная статья! Пожалуй, это лучшая статья по MVC, которая мне встречалась. Большинство других источников пишут (сами того не подозревая) про "MVC-в-веб", "MVC-в-java", "MVC-в-yii". Ваша статья выходит уровнем выше.
Практически нигде и никогда пользователь не оперирует напрямую моделью. Однако можно консоль с доступом к объектной модели считать прямой связью пользователя и модели (но если неприятно, можно консоль считать другим контроллером), мы пишем gui на python, нам это актуально.
Иногда некоторые части нашего кода, в которые меня заносит, выглядят как карго-культ MVC. Прямо чётко видно, что их начинал писать кто-то, кто понимал суть MVC, а потом он ушёл, а продолжатели взяли внешние признаки, шелуху, а идею не поняли. И получается именно что карго-культ MVC вместо самого MVC.
И ведь люди не со зла это делали, а просто не понимали, как правильно, а как неправильно. Причём важнее понимать, как неправильно, - потому что тогда можно пойти к коллеге и посоветоваться, как правильно. Нужен какой-то очень простой критерий, который позволил бы любому человеку, который работает с GUI, быстро понять, что он делает что-то не так. И у меня есть идея.
Надо поставить ВСЕМ разработчикам конкретную задачу: вся функциональность, которая пишется, должна работать БЕЗ View (в нащем случае gui - без создания виджетов). То есть прямо должен быть какой-то метод (мы его потом в тестах можем вызывать), который выполняет 100% возможных действий пользователя, вызывая какой-то код без создания виджета. И этот метод (раз View нет, то нет и работающих обработчиков gui-событий) - это точно не метод контроллера. По-моему, соблюдение этого критерия достаточно просто проверить. Да, это может привести к тому, что код попадёт в контроллер вместо модели, но это и обнаружить легче, и исправить потом.
Но при этом Модель в «MVC схеме» вовсе не тождественна доменной модели (которая может быть сколь угодно сложной и состоять из множества объектов), а является всего лишь ее интерфейсом и фасадом.
Любопытно, ведь это по факту то, что принято называть ViewModel? Получается MVVM ближе к исходному понимаю создателей MVC?
Вместо того чтобы нужным образом всего лишь интерпретировать и адаптировать имеющиеся доменные данные с помощью моделей-посредников их начинают копировать в эти модели-посредники
Я бы сказал, что это вопрос двух разных ролей, которые в общепринятом (не значит "правильном" или "исходном"!) понимании назначаются модели. Модель - как хранитель данных и обеспечение всей бизнес-логики этих данных. И модель - как источник данных для Представления и всех прочих. Естественным образом кажется, что первое - это настоящая Model, а второе - ViewModel, и именно вторая должна стремиться к тому, чтобы по возможности хранить только ссылки, а не копии. Собственно, об этой путанице вы дальше и пишете.
ListView отображает список переменных (слева), а TextView показывает значение выбранной переменной (справа)… Моделью для этих видов служит экземпляр класса «Inspector»… Отдельный класс «Inspector» является посредником или фильтром для того чтобы обеспечивать доступ к любому свойству любого объекта. Использование промежуточных объектов между View и "actual" models является типичным способом изолировать поведение отображения от модели приложения
Вот тут у меня есть такой частный вопрос. Где-то есть какая-то переменная-индекс, которая хранит текущий выбранный элемент в этом ListView. Вы как считаете, она где? Только внутри самого ListView? Внутри упомянутого посредника Inspector? Внутри той domain-модели, на которую ссылается Inspector? Это может стать важным для организации master-detail связей с использованием подобных ListView.
Тут можно сказать лишь одно: архитектура, в которой один модуль (Вид или Контроллер), должен «лезть» внутрь другого модуля (доменной модели) и искать там для себя данные или объекты для изменения очень нехорошо «пахнет». Получается что Вид и Контроллер зависят от деталей реализации доменной модели, и если структура этой самой модели изменится, то придется переделывать весь пользовательский интерфейс.
В порядке размышлений. А может быть в этом ничего плохого нет? В моих задачах Вид всё-таки является отражением в интерфейсе именно доменной Модели, которая, в свою очередь, должна отражать реальность. Поэтому если мы решили переделать доменную Модель, чаще всего и Вид изменяется тоже.
В этом случае кажется допустимым, чтобы какой-то Вид непосредственно привязывался к какой-то структурной части Модели, а не требовал обязательно вынести интерфейс к этой части в Фасад.
Ещё пара замечаний "на полях" после (очередного) прочтения этой шикарной статьи:
Модули должны взаимодействовать друг с другом лишь на уровне абстрактных интерфейсов (Dependency Inversion Principle).
Формально говоря, это, конечно, "по классике" входит в DIP, но только потому, что лишней буквы в SOLID не нашлось. Взаимодействие на уровне абстрактных интерфейсов, а не реализаций - в чём тут инверсия то? На мой взгляд, это самая большая проблема такой идеи, как SOLID.
Вместо Предоставляемого Интерфейса (Provided Interface) используются Требуемые Интерфейсы (RequiredInterface).
А вот именно это - как раз и есть главная часть Dependency Inversion Principle, именно тут и инверсия направления стрелочек появляется на диаграммах.
Охота на мифический MVC. Обзор, возвращение к первоисточникам и про то, как анализировать и выводить шаблоны самому