January 28th, 2010

glider

DDD

Несмотря на свою дурную славу, разработка корпоративных прилаг (или, как их ласково называют, "оперденей") это весьма занятное дело. Редко какая программерская задача может похвастаться несколькими абсолютно непохожими друг на друга плоскостями (UI, business logic, persistence), которые необходимо подружить, а заодно еще и пронизать общими мотивами (security, validation, remoting и так далее).

Главный челлендж здесь, имхо, не в том, что это сделать трудно, а, наоборот – в том, что слишком много вариантов, причем каждый день этих вариантов становится все больше и больше. Кто-то старается угнаться за модой, старательно пробуя самые популярные в коммьюнити комбо. Кто-то выбирает самые простые доступные средства, опасаясь протечки абстракций. Бывает и такое, что вся разработка представляет собой GUI-driven development: берем модный UI фреймворк, учимся писать на нем формочки, а потом всю прилагу впихиваем в эти формочки (спасибо моему коллеге Диме за эту грустную метафору).

***

Самое главное в корпоративном приложении – его функциональность, то есть бизнес-логика. Но, конечно, бизнес-логике требуются данные, которые: 1) надо получить от пользователя из GUI, 2) надо отдать на сохранение компьютеру через persistence. Кроме того, бизнес-логику нужно как-то триггерить, поэтому опять-таки вылазит GUI.

Чего ради это я впариваю прописные истины? А ради того, чтобы сфокусироваться на том, что GUI и persistence, по сути, вторичны. Недавно в рассказе о своем видении программирования я писал, что главный критерий для меня – это выразительность кода. Соответственно, с моей точки зрения, метрика трушности разных фреймворков для разработки корпоративных прилаг заключается в том, насколько мало приходится писать кода дополнительно к, собственно, бизнес-логике. В этом контексте весьма радуют меня три буквы D, вынесенные в заголовок поста. Радуют тем, что в хаосе фреймворков представляют замечательный идеал, к которому стоит стремиться.

***

Рассмотрим на примере. Представим типичный сайтик электронного магазина и кусок бизнес-логики, который говорит о том, что 1) "если у кастомера поменялся адрес, то надо автоматом перенаправить его ордеры на новый адрес", 2) "при этом проверить, что на этот адрес нет никаких других ордеров от других кастомеров". Domain-driven development говорит о том, что эту логику надо реализовать вот так:

Пример кода, соответствующего идеологии DDD

«Ёмаё» – подумал бы человек, знакомый с основами программирования, но не отравленный еще мейнстримом – «Тоже мне открытие. А как еще написать? Берем лабаем классы домена (сорри, ФЯПщики, но для данной конкретной задачи ФП никак не катит): данные запихиваем в поля, бизнес-логику выражаем методами – все, как в книжке». И был бы прав – ведь смысл программы заключается в том, чтобы реализовывать бизнес-логику, причем самым простым способом.

***

Но всем известно, что случается в реальности.

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

Впрочем, что ж я ёрничаю – уже относительно давно есть хорошие O/RM-фреймворки. Но и у них есть неиллюзорные косяки. 1) Мало кто поддерживает обычные POCO-объекты – им подавай маппинги, диаграммки, кодогенерацию, что противоречит градиенту выразительности. 2) В мейнстриме дотнета все еще открытым является вопрос о том, как O/RM определяет то, какие именно данные изменились в результате действий, которые пользователь произвел «на клиенте», т.е. на другом физическом компьютере, поэтому чейндж-трекинг приходится писать руками. 3) Надо быть внимательным и учитывать количество и качество запросов к базе данных – например, если ничего не предпринять, то строчка номер #10 превратится в пачку маленьких запросиков, а строчка #9 вытянет из базы все ордеры и туеву хучу кастомеров.

Ахтунг творится и в GUI. Ладно, дизайнер слабал формочку в HTML (для десктопных приложений такая радость в дотнете появилась лишь недавно, не уверен, есть ли аналоги такого в других мейнстримных языках), но ведь в нее же еще надо вбить данные и получить из обратно, а еще же есть и валидация. Также не радует факт того, что GUI обычно находится на другой физической машине (привет, проблемы с тормозами и необходимостью учитывать семантику распределенности), а также пишется на непривычном и ограниченном языке программирования (какая уж тут доменная модель, хоть как бы слабать эту логику, а еще и AJAX поддерживать – ууу, facepalm).

Наконец, есть же еще и cross-cutting консерны – валидация, логгирование, секьюрити и в таком духе. К слову, по непонятной мне причине именно на эти темы разгораются самые жаркие дискашены в коммьюнити и на них обычно акцентируют неслабое внимание создатели очередного мегафреймворка типа CAB, SCSF, Prism и так далее.

В итоге имеем: кашу в голове, пачку фреймворков, кучу нелепых телодвижений для реализации изменений в бизнес-логике, ибо фреймворки надо дружить друг с другом и рассовывать по тирам (привет, выразительность!), недоумение кастомера (а что ж тут столько девелопить-то?) и неслабую фрустрацию разработчиков (а кому понравится целый день объяснять компьютеру три новых предложения в спецификации?).

***

В этом ералаше DDD мягко намекает программисту о том, что плясать надо от доменной модели.

Если хорошо подумать и сильно постараться, то можно сделать так, что: 1) маппинг на базу данных задается при помощи convention-over-configuration, 2) запросы к базе данных аля строчки ##9-10 примера маппятся на реальный SQL, 3) прилага сама определяет, что поменялось на клиенте, и не требует ручного обхода графа объектов и установки dirty флага, 4) понятие «миддл-тир» исчезает как класс – его код просто пишется на javascript или кросс-компилируется туда же, 5) роль сервера сводится лишь к тому, чтобы по REST отдать данные клиенту. В дотнете пункты с первого по третий вполне реализуются современными фреймворками (впрочем, я бы все равно писал специально для себя и не потому, что closed source, ибо они-то как раз-таки open source). Пункт четвертый надо реализовывать самому (впрочем, для джавы есть GWT, но я о нем мало знаю). Пункт пятый также уже реализован.

ВНЕЗАПНО получается, что: 1) не надо руками писать вообще никакие удаленные вызовы, 2) не надо руками писать вообще никакие удаленные вызовы, 3) не надо руками писать вообще никакие удаленные вызовы, 4) можно лабать богатый UI и не париться с всякими удаленными ивентами, постбэками, контроллерами и т.д. и т.п. ибо вот локальные данные, вот локальный DOM, вот локальные ивенты – делай все, что хочешь, 5) трафик весьма некисло кэшируется, ибо единственное, что гоняется по сети – это данные реляционной модели, которую, в отличие от general purpose RPC, можно автоматически проанализировать, 6) по той же причине почти за бесплатно становятся возможными partially-connected и disconnected сценарии. Конечно, это далеко не полное описание такого вижена – гораздо больше я законспектировал в своей knowledge base, но там и космических просторов хватает, а все, что описано выше, я вблизи видел реализованным на практике (правда, девелопил эту machinery не я).

Сюда еще много чего можно добавить, но это уже космос. Вот несколько вбросов: 1) генерить спеку прямо по коду, т.е. писать вначале код, а потом синкать спеку (например, выщемливать все ассерты и представлять их условия в читабельном виде, елико возможно), 2) определять потребности кода в бизнес-объектах и префетчить их , 3) выводить секьюрити из кода и сценариев использования приложения, 4) Naked Objects.

***

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