|
Навигация
|
Главная » Тестирование
Парадигмы программирования. Data Driven vs Domain Driven
Источник: habrahabr primepix Информационные технологии развиваются семимильными шагами, появляются новые устройства, платформы, операционные системы, и вместе с этим растет спектр задач, который приходится решать разработчикам. Но, не все так плохо - на помощь программистам спешат новые средства разработки, ide"шки, новые языки программирования, методологии и т.д. Один только список парадигм программирования впечатляет, а с учетом современных мультипарадигменных ЯП (например, C#) резонно встает вопрос: "Как с этим всем быть? Что выбрать?". Попробуем немного разобраться. Откуда взялось так много парадигм? На самом деле ответ уже прозвучал в этом посте - разные типы задач легче и быстрее решать, используя подходящие парадигмы. Соответственно, с развитием ИТ появлялись новые виды задач (или старые становились актуальными), а решать их используя старые подходы было неудобно, что влекло за собой переосмысление и появление новых методик. Что выбрать? Все зависит от того, что требуется сделать. Стоит отметить, что все средства разработки отличаются, одни поддерживают одно, другие - другое. К примеру, PHP со "стандартным" набором модулей не поддерживает аспектно-ориентированное программирование. Поэтому выбор методологии довольно тесно связан с платформой разработки. Ну, и не стоит забывать, что можно комбинировать разные подходы, что приводит нас к выбору стека парадигм. Для категоризации парадигм я использую 4 измерения, которые присущи практически любой задаче: - Данные
Любая программа, так или иначе, работает с данными: хранит их, обрабатывает, анализирует, передает
- Действия .
Любая программа должна что-то делать, обычно действия связанны с данными.
- Логика .
Логика или бизнес-логика определяет правила, которым подчиняются данные и действия. Без логики программа лишена смысла - Интерфейс .
То, как программа взаимодействует с внешним миром
Можно пойти дальше и углубиться в эту идею: придумать качественные характеристики для данных четырех мер, добавить строгие правила и немного математики, но это, пожалуй, тема для отдельного поста. Я думаю, большинство системных архитекторов определяют данные характеристики для конкретной задачи на основе своих знаний и опыта. После того, как вы проанализируете свою задачу по этим 4 измерениям, то, скорее всего, увидите, что определенное измерение выражено сильнее остальных. А это в свою очередь позволит определиться с парадигмой программирования, так как обычно они нацелены на какое-то одно измерение. Рассмотрим примеры: - Ориентация на данные (Data Driven Design).Нам важны в первую очередь данные, а не то, как они между собой связанны.
Типы подходящих приложений: - грабберы (собираем данные из разных источников, сохраняем куда-нибудь)
- различные админки, интерфейсы к базам данным, все, где много простых CRUD операций
- случаи, когда работа с данными уже определена, например, требуется разработать программу, а база данных уже существует и схему данных не изменить. В таком случае возможно проще ориентироваться на то, что уже есть, чем создавать дополнительные обертки над данными и уровнями доступа к данным
- часто ориентация на данные появляется при использовании ORM, но нельзя заранее сказать хорошо это, или плохо (об этом ниже)
- Ориентация на действия - императивные подходы к разработке. Думаю, что сюда можно отнести такие парадигмы как Event Driven Programming, Aspect Oriented Programming.
- Ориентация на логику - Domain Driven Design (DDD) и все, что с этим связанно. Здесь нам важна предметная область задачи, мы уделяем внимание моделированию объектов, анализу связей и зависимостей. Применяется преимущественно в бизнес приложениях.
Так же, сюда относится декларативный подход и отчасти функциональное программирование (решение задач, которые хорошо описываются математическими формулами) - Ориентация на интерфейс. Используется, когда в первую очередь важно как программа взаимодействует с внешним миром.
Разработка приложения с ориентацией только на интерфейс - ситуация довольно редкая. Хотя в некоторых книгах я встречал упоминание о том, что такой подход рассматривался в серьез, причем базируясь на пользовательском интерфейсе - брали, что непосредственно видит пользователь и, исходя из этого, проектировали структуры данных и все остальное. Ориентация на пользовательский интерфейс в бизнес приложениях часто проявляется косвенно: к примеру, пользователю требуется видеть определенные данные, которые получить сложно, за счет чего архитектура обрастает дополнительными конструкциями (например, вынужденной избыточностью данных). Формально сюда можно отнести Event Driven Programming А, что на практике? В нашей компании мы занимаемся разработкой бизнес приложений (стартапы, веб-сервисы, сайты, прикладные программы и т.п.) поэтому далее поговорим о парадигмах программирования, которые встречаются как в нашей практике, так и у других команд работающих в этой области. На основе моего опыта могу сказать, что в данной сфере превалируют два подхода: ориентация на данные (Data Driven) и ориентация на логику (Domain Driven). По сути, они являются конкурирующими методологиями, но на практике могут объединяться в симбиозы, которые часто являются известными анти-паттернами. Одним из преимуществ Data Driven в сравнении с Domain Driven является простота использования и внедрения. Поэтому Data Driven начинают использовать там, где надо применить Domain Driven (причем часто это происходит бессознательно). Проблемы же возникают с тем, что Data Driven плохо совместим с концепциями объектно-ориентированного программирования (конечно, если вы вообще используете ООП). На небольших приложениях эти проблемы почти незаметны. На средних по размеру приложениях эти проблемы уже заметны и начинают приводить к анти-паттернам, ну, а на крупных проектах проблемы становятся серьезными и требуют соответствующих мер. В свою очередь Domain Driven выигрышен на крупных проектах, а на небольших - приводит к усложнению решения и требует больше ресурсов для разработки, что часто бывает критичным с точки зрения бизнес требований (вывести проект на рынок "asap", за небольшой бюджет). Для того, чтобы понять разницу в подходах рассмотрим более конкретный пример. Допустим, нам требуется разработать систему учета ордеров. У нас есть такие сущности как: - Продукт
- Клиент
- Квота (отправляется клиенту как предложение)
- Ордер (заказ)
- Инвойс (платежка)
- Ордер поставщику
- Билл (по сути, платежка от поставщика)
Решив, что контекстная область у нас как на ладони, мы начинаем проектировать базу данных. Создаем соответствующие таблицы, запускаем ORM-ку, генерируем сущностные классы (ну, или в случае "умной" orm-ки прописываем схему где-то отдельно, например, в хml, и уже по ней генерируем и базу и сущностные классы). В итоге получаем на каждую сущность отдельный, независимый класс. Радуемся жизни, работать с объектами легко и просто. Проходит время, и нам требуется добавить дополнительную логику в программу - например, находить у ордера товар с самой высокой ценой. Здесь уже могут возникнуть проблемы, если ваша orm не поддерживает внешние связи (т.е. сущностные классы ничего не знаю о контексте данных), в этом случае, придется создавать сервис, в котором будет метод - по ордеру вернуть нужный продукт. Но, наша orm хорошая, умеет работать с внешними связями, и мы просто добавляем метод в класс ордера. Снова радуемся жизни, цель достигнута, в класс добавлен метод, у нас почти настоящее ООП. Проходит время, и нам надо добавить такой же метод и для квоты, и для инвойса и для других аналогичных сущностей. Что делать? Мы можем просто прописать во все классы этот метод, но это будет, по сути, дублирование кода и аукнется при поддержке и тестировании. Мы не хотим усложнять и просто копируем метод во все классы. Потом появляются аналогичные методы, сущностные классы начинают распухать одинаковым кодом. Проходит время, и появляется логика, которую не описать внешними связами в бд, и поэтому разместить ее в сущностных классах нет возможности. Мы начинаем создавать сервисы, которые выполняют эти функции. В итоге получаем, что бизнес логика разбросана по сущностным классам и сервисам, понять, где искать нужный метод становится все сложнее. Решаем провести рефакторинг и вынести, к примеру, повторяющейся код в сервисы - выделяем общий функционал в интерфейс (например, делаем интерфейс - IProductable, т.е. нечто, что содержит продукты), сервисы могут работать с этими интерфейсами, за счет чего немного выигрываем в абстракции. Но кардинально это не решает проблему, получаем больше методов в сервисах и решаем для единства картины перенести все методы из сущностных классов в сервисы. Теперь мы знаем, где искать методы, но наши сущностные классы лишись всякой логики, и мы получили так называемую "Анемичную модель" (Anemic Model). На этом этапе мы полностью ушли от концепции ООП - объекты хранят только данные, вся логика находится в отдельных классах, ни инкапсуляции, ни наследования. Стоит отметить, что это не так плохо, как может показаться - ничего не мешает внедрять юнит тестирование, да и вообще разработку через тестирование, внедрять паттерны управления зависимостями и т.п., в общем, с этим можно жить. Проблемы возникнут, когда приложение перерастет в большое, когда сущностей станет так много, что их нереально удержать в голове, а о взаимодействии и говорить не приходится. В этом случае поддержка и развитие такого приложения станет проблемой. Как вы наверно догадались, этот сценарий описывает использование Data Driven подхода и его проблемы. В случае с Domain Driven мы бы поступили следующим образом. Во-первых, ни о каком проектировании бд на первом этапе речи бы не шло. Нам потребовалось бы тщательно проанализировали контекстную область задачи, смоделировать ее и перенести на язык ООП. Например, мы можем создать модель абстрактного документа, у которого был бы набор базовых свойств. От него наследовать документ, у которого есть продукты, от него наследовать "платежный" документ, с ценой и биллинг адресом и так далее. При таком подходе добавить метод, получающий самый дорогой продукт, не составляет труда - мы просто добавим его в соответствующий базовый класс. В итоге, контекстная область задачи будет описана используя ООП на полную катушку. Но появляются очевидные проблемы: как сохранять данные в бд? Собственно, для этого потребуется создавать функционал для меппирования данных из наших моделей на поля в бд. Такие мепперы могут быть довольно сложными, и при изменении моделей придется изменять и мепперы. Более того, вы не застрахованы от ошибки при моделировании, что может привести к сложному рефакторингу. Итак, подведем итог Data Driven vs Domain Driven: Data Driven: - Плюсы
- Позволяет быстро разработать приложение или прототип
- Удобно проектировать (кодогенерация по схеме и тп)
- На небольших или средних по размеру проектах может быть вполне себе решением
- Минусы
- Может приводить к анти-паттернам и уходу от ООП
- На больших проектах приводит к хаосу, сложной поддержке и т.п.
Domain Driven: - Плюсы
- Использует всю мощь ООП
- Позволяет контролировать сложность контекстной области (домена)
- Есть еще ряд преимуществ, не описанных в статье, например, создание доменного языка и внедрение BDD.
- Дает мощный инструмент для разработки сложных и больших решений
- Минусы
- Требует значительно больше ресурсов при разработке, что приводит к удорожанию решения
- Определенные части становятся сложнее в поддержке (мепперы данных и т.п.)
Так, что же, черт возьми, мне выбрать? К сожалению, однозначного ответа нет. Анализируйте вашу проблему, ваши ресурсы, перспективы развития, цели и задачи. Правильный выбор - это всегда компромисс.
Как повысить эффективность процесса тестирования в компании.
Проектирование информационных систем. Часть 5. Этапы разработки проекта: реализация, тестирование, эксплуатация и сопровождение.
Здоровье и излучение компьютера.
Жизненый цикл информационной системы.
Менеджмент качества: реинжиниринг и BSC.
Главная » Тестирование
|