Тонкие и толстые модели
4 апреля 2011
- Толстые модели можно охватить модульными тестами. С тонкими придётся писать ещё и функциональные.
- Реализацию метода толстой модели легко подменить. Для меня это основной аргумент (хотя часто и я ленюсь). Например, есть у нас модели в системе, использующие Active Record:
class MyThinModel extends CActiveRecord { … }
В большинстве случаев вызывают их в контроллерах как-то так:
$criteria = new CDbCriteria(); $criteria->compare('used', 1); $models = MyThinModel::model()->findAll($criteria);
А теперь представим, что проект у нас вырос до приличных нагрузок и кэш с оптимизацией запросов не помогают. Что теперь делать? Приходится перекраивать хранилище на какой-нибудь быстрый key-value, например, тот же Redis. Вот только проблема есть… проект очень завязан на SQL-ный Active Record и программист, посмотрев на всё это дело, принимает популярное решение «переделать с нуля». А ведь можно было этого избежать (ну или хотя-бы сгладить эффект), если бы изначально использовались толстые модели:
class MyThinModel extends CActiveRecord { public function getAllUsed() { $criteria = new CDbCriteria(); $criteria->compare('used', 1); return self::model()->findAll($criteria); } }
Ну и в контроллере:
$models = MyThinModel::model()->getAllUsed();
В этом случае переписать необходимо будет только getAllUsed
.
Комментарии RSS по email OK
С тех пор как перешел на Yii тоже стараюсь делать толстые модели и тонкие контроллеры =) В принципе MVC и подразумевает такой подход по моему, ибо работа с данными должна быть в модели. Контроллер это лишь извлечение и подготовка к выводу)
Примерно так и делаю, это помогает избежать дублирование кода.
Как в таком случае быть со scopes ? Стараться как можно меньше их использовать?
Для этого в ZendFramework модели пишутся используя Data Mapper шаблон. Очень удобно получается, что все операции с базой хранятся в файлах Имя_моделиMapper. И тестировать очень просто. Планируется ли в Yii внедрение данного шаблона в Yii? Ибо очень удобно.
porter, внедрять ничего не надо, берите и используйте. Достаточно в Gii модели назвать как
mapper
.Вот честно, не знал как это все называется. Но делал именно модели толстыми, изначально казалось правильным )
В Django так же принято делать. Опять - же, совсем не обязательно делать все функции для манипуляции с данными методами класса, можно закинуть простыми функциями в файл с моделями, если они не завязаны жестко на данные экземпляра класса. Скажем ваш пример ничто не мешает записать a-la:
Правда в PHP с этим сложнее ибо глобальное пространства имен и все такое...
я когда-то в комментариях писал об этом, нельзя использовать query builder в контроллере. но тонкая и толстая модель не сводится к её использованию в контроллере. в контроллере просто нельзя их писать. тонкая - это когда в каждом модуле используется своя модель унаследованная от model_base, толстая - это когда для всех модулей общая и в ней куча методов.
непонятно тогда что длать со всякими CPagination и CActiveDataProvider; В каком месте устанавливать текущую страницу и количество страниц?
Я использую некую связку принципов толстой и тонкой модели. В контролере:
В модели
Таким образом я не завязан на имена полей в базе, и API от ORM единственное условие это сохранить функции find() и find_all() дабы не пришлось переписывать контролеры.
Согласен с Zloy. Не совсем ясно тогда как utilize CPagination для передачи данных в толстую модель.
Сможете подсобить примером такой реализации кто делал?
2 Евген, Я делаю так:
Модель:
Контроллер:
Гда validated - вспомогательный метод из поведения прицепленного к модели, что-то типа(в очень упрощеном виде):
ну как бы у меня на этот счет идея такая:
В контроллере
С одной стороны с идеологической точки зрения все верно все поведение настривается в контроллере, но setPagination и getPaginaiton мне как то не очень нравится.А толстые модели это определенно хорошо обязательно к использованию, повторное использование кода возрастает.
Также использую толстые модели, где находится вся логика работы с БД. При этом большая часть методов модели - статические, а вместо AR использую непосредственно PDO. AR в основном остается только для относительно редких и рутинных операций (всяческие формы регистрации, добавления ч-л), где очень кстати приходятся правила валидации. Вот так вот отказываюсь от удобств ORM в пользу производительности и экономии памяти, ибо хорошая, но прожорливая штука.
Подпишусь на комментарии... ;)
У меня все с точностью до наоборот.
Толстые модели сильно снижают гибкость системы.
Представьте, если потребуется выбирать список каких-то объектов (например счетов), но по какому-то статусу?
Получится:
либо
А теперь, нам требуется вывести все счета, выставленные в каком-то диапазоне дат
Далее возникает логичное желание выбрать новые счета за какой-то диапазон:
А теперь представьте, что таких моделей десятки, и у каждой вырастет по сотне таких комбинаций...
Слабо будет запомнить интерфейсы всех моделей?
Вот мне слабо...
А потом переписывать всё это еще и под другую базу... Ну уж нет...
скобочки лишние...
Keltanas, может я что-то не пониаю, но вы пробовали использовать scopes?
Keltanas, перечитай мой комментарий выше. http://rmcreative.ru/blog/post/tonkie-i-tolstye-modeli#c4295 представь, что у тебя просто запрос: function findAccounts($status = null, $from = null, $to = null) теперь он с лёгкость подходит ко всем описанным потребностям. да и запрос findAccounts(1) в ходе модификаций может потребовать не просто делать выборку where status = 1, но и заключать бизнес логику модели, например soft delete where status = 1 and deleted = 0
2Алексей scopes - хорошая идея. Но она же относится к тонким моделям? А речь же в посте не об этом...
2AmdY то что ты написал:
кажется мне ужасным
Вообще я сейчас пытаюсь в своих проектах наоборот из толстых моделей делать тонкие. Они хоть и увеличивают время выполнения кода, но позволяют более наглядно формировать условия выборок.
Под бизнес-логикой все же понимаю что-то более сложное, чем where status = 1 and deleted = 0. Например расчет маржи с учетом себестоимости, доставки, радости и пр.. для всех товаров в счете.. Только надо оговориться, что у меня оптовая компания и в одном счете могут быть сотни товаров...
Раньше сделал толстую модель для этих целей, а сейчас понял, на сколько она сложна и неповоротлива... в результате лишний refactoring..
PS: Единственное, что не нравится в Yii - это статические вызовы в моделях... Я бы предпочел использовать фабричные методы...
Keltanas, а что кроме
::model()
вызывается статично и мешает?А вот допустим у меня есть задача добавление коммента к посту,но при это у меня должно оправляться уведомление автору поста о новом посте и должна быть запись в лог файл. Т.е. есть модель Post и метод у нее addComment(Comment $comment). Тогда возникает вопрос, где должен находиться весь код по отсылке уведомления автора поста и записи в лог? В модели Post или в контроллере поста?
Serge, да, в общем, где угодно.
2Sam: Все таки по моему толстая модель отличается в основном тем что она содержит всю бизнес логику, включая в моем случае и логику добавления коммента (отсылку уведомления и запись в лог). Т.е. контроллер не содержит ни капли кода записи в лог и уведомления. В контроллере только должнен находиться код создания / валидации формы и все. А тонкая модель наоборот представляет собой только хранилище данных (ее основная задача хранить значения св-в и работа данными из БД) а весь код (логика) сосредоточен в контроллере.
кстати, а разве не будет более правильно метод getAllUsed объявить статическим? Ведь он к объекту не относится
rak, т.к. в AR есть метод
model()
, который возвращает экземпляр в состоянии fidner. Это позволяет получить большую гибкость за счёт нормального наследования.Уважаемый Sam: Мне нужно реализовать data mapper на yii. Можно поподробнее об этом? rmcreative.ru/blog/post/tonkie-i-tolstye-modeli#c4292 Я не понял как это сделать(
mixartemev, что именно хочется получить от data mapper-а? Насколько он должен быть честным?
2Sam: что именно хочется получить от data mapper-а? получить то, что обычно от него и требуется - убрать из объекта модели информацию о бд, а оставить только бизнес логику. Насколько он должен быть честным? Насколько возможно. Буду рад любому примеру реализации.
mixartemev, Ну если я правильно понял, то оберните AR своей моделью. Наружу давайте только методы модели, работа с AR только внутри модели.
Очень поверхностно, как то так.