Летом 2004 года компанией Info Industries Group в процессе разработки прикладной информационной системы был создан инструментарий разработки под кодовым наименованием IIG ORM 1.0 (Object-Relationship Mapper version 1.0), который в настоящее время эксплуатируется в компании при создании различных прикладных корпоративных ИС, функционирующих под управлением среды исполнения Microsoft.Net Framework 1.1, и развивается силами специалистов IIG.
Планируемая к вводу в эксплуатацию весной 2008 года версия ESA является дальнейшим развитием идей, реализованных в версии 1.0, но в своей основе и принципах будет полностью переработанным продуктом.
Для понимания проблем, возникающих в процессе разработки, развития и поддержки ИС и путей их решения (для чего, собственно, и предназначен ORM), рассмотрим архитектуру ИС.
Архитектура информационных систем (ИС)
Основой современных ИС является выборка, обработка и сохранение больших потоков информации с учетом множества конкурентных запросов. Большинство ИС строятся по многослойной/многозвенной архитектуре, где каждый слой призван решать определенный круг задач, взаимосвязь между которыми строго определена. Если определить, что долговременное хранилище сущностей ИС является самым «глубоким» слоем, а слой презентации — самым «поверхностным», то правило взаимосвязи слоев можно определить следующим образом: внешний слой, «знает» и использует нижележащий слой, однако тот ничего «не знает» о внешних слоях.
В большинстве ИС в качестве долговременного хранилища, т. е. самого глубокого слоя, используются реляционные базы данных.
Проблемы, возникающие в процессе разработки, развития и поддержки информационных систем
В реляционной БД взаимоотношения между родственными данными описываются посредством реляционных связей. Однако, на уровне современных языков программирования такие связи принято выражать в парадигме ООП, с использованием наследования, агрегации и т. д.
Процесс отображения объектных связей на реляционные и обратно, для которого и принят термин ORM, является неоднозначным, т. е., объектные связи не содержат всей необходимой информации для отображения их на реляционные (справедливо и обратное). Это приводит к необходимости описания, хранения и использования информации процесса ORM, что в разных продуктах реализовано по-разному. Например, в популярном open-source продукте Hibernate (и его реализации под Microsoft.Net Framework — NHibernate) используются внешние xml-файлы. В нашем продукте это сделано через конструкции SQL и SQL-параметры, которые обеспечивают сохранение/восстановление состояний объектов из реояционной БД. Однако, это не решает проблем, возникающих при описании подобного рода информации. Вне зависимости от того, в каком конкретно формате ведется и поддерживается такого рода описание, оно полностью разрывает «цепочку» ООП (наследование, агрегацию) и «прерывает» строгую проверку типов. Неизбежно такие «разрывы» приводят к несогласованности взаимодействующих слоев при рефакторинге ИС , что приводит к возникновению ошибок, наличие которых проявляется в момент запуска и функционирования ИС, а выявление ошибок времени исполнения (run-time) связано с лишними расходами временных ресурсов.
Аналогию вышеописанной проблеме можно найти в механизме позднего связывания в вызовах COM, где имена методов COM-сервера и типы параметров разрешаются в момент вызова.
Аналогичная проблема встает в рассинхронизации цепочки взаимодействующих слоев при рефакторинге ИС. Однако, т. к. обычно остальные слои ИС, в отличие от слоя персистентности, полностью решаются в парадигме ООП, большинство проявлений рассинхронизации слоев выражается в виде ошибок времени компиляции, что позволяет сэкономить ресурсы.
Для решения вышеперечисленных проблем IIG развивает линейку продуктов под условным названием ORM.
Решение IIG ORM 1.0
Главной задачей, которая была поставлена перед ORM 1.0, было «преобразование» ошибок времени выполнения в ошибки времени компиляции, возникающие в процессе ORM.
ORM 1.0 представляет собой приложение, которое позволяет на основе существующей базы данных спроектировать модель, состоящую из так называемых ORM-классов с выбором проектировщиком отражения реляционной связи «один-к-одному» в наследование, а затем на основании модели сгенерить исходные классы (на языке C#), содержащие код восстановления/сохранения состояний объектов, массовой выборки с критериями отбора и пэйджингом, с учетом разделения доступа по принципу ACL и т. п.
В качестве средства (engine) генерации используется хостирование ASP.NET. Темплейты генерации пишутся в стиле ASP.
За основу генерируемых классов принят класс System.Data.DataTable, путем типизации и наращивания генерирумых классов.
В ходе эксплуатации ORM 1.0 был выявлен ряд следующих недостатков:
• ORM-классы не позволяют наращивать поведение на уровне моделирования, что не позволяет в генерирумом коде «навязать» разработчику имплементацию методов поведения, например, за счет генерации интерфейса для конкретного ORM-класса и указания его в цепочке наследования кода класса;
• ORM 1.0 «привязан» к единственному серверу БД (Microsoft SQL Server 2000), что не позволяет использовать его в разработке ИС на серверах других производителей;
• Разработка и поддержка генераторов, по сути производящих трансформацию модели в код с одновременным отображением «кодовой» модели в тест кода затруднено, т. к. различные части генераторов становятся сильно-связанными и должны работать согласованно;
• Сложность отладки генераторов, для запуска которых используется хостирование ASP.NET. (Тем более следует принять во внимание, что в Microsoft.Net Framework 2.0 принцип хостирования изменился.)
Решение ESA
ESA будет представлять собой приложение и, возможно, VSIP решение, модель проектирования которого будет намного шире существующей (ORM 1.0), а главное, будет расширяемой, что позволит описывать или сделать более гибким не только процесс ORM, но и позволит описывать слои, взаимодествие между слоями и трансформацию между моделями. Также будет в корне изменен процесс генерации.
Базовые понятия, позволяющие описывать состояние моделей:
• Контракт (Contract) – описывает поведение, в том числе данные и формат данных (например – таблица БД) описывается через контракт. Однако, контракт не существует сам по себе, а существует на границе юнита (см. ниже);
• Юнит (Unit) – контейнер вложенных юнитов. Юнит содержит от 0 до N юнитов (где N > 0);
• Граница юнита (Unit’s bound) – контейнер для принадлежащих юниту контрактов. Граница неотемлема от юнита;
• Делегация контрактов (Delegation of contracts) – позволяет описывать делегацию методов одного контракта на методы других контрактов, т. е. отношение делегируемого контракта к делегированным – «один-ко-многим»;
• Трансформация (Transformation) – однонаправленный или двунаправленный процесс преобразования модели из одного представления в другое. Двунаправленный процесс поддерживает согласованное состояние моделей на уровне проектирования.
• Кодовая модель (Code model) – представление модели самого нижнего уровня, используемая как цель трансформации более высоких представлений.
Описанные базовые понятия по своей сути являются абстрактными. Для выражения модели соответствующего представления необходимо реализовать конкретные понятия, которые описывают такое представление. Например, для описания в модели схемы БД таблицы БД необходимо реализовать контракт, содержащий в поведении только описание свойств (поля и типы), контракт, содержащий в поведении только описание индексов, юнит, который содержит только такие контракты, и, строго по одному, а так же трансформер уровня преобразования в кодовую модель (см. ниже). Кстати, описанный процесс является стандартным процессом расширения дополнительными представлениями модели.
Предполагается, что через двунаправленный процесс трансформации будет реализован механизм независимости от конкретного сервера БД. При привязке к конкретному серверу БД из «независимой» (выраженной через типы System.Data.SqlTypes) модели БД путем трансформации будет создана модель под конкретный сервер БД. При этом не исключается допроектирование полученной модели. Реинжиниринг существующей БД предполагается осуществлять подобным путем – с использованием трансформации.
Процесс генерации кода по модели, в отличии от ORM 1.0, разделен на две части. Первая часть этого процесса сводится к трансформации представления модели к представлению кодовой модели, которая в свою очередь, во-второй части процесса генерации используется для генерации исходного кода.
Возможность расширения представлений моделей, по своей сути, вводит возможность на одном наборе базовых понятий, путем расширения, вводить разные DSL (Domain Specific Language), расширением двухсторонней (согласованной) трансформации описывать процесс двунаправленного изменения DSL и единообразным способом, через трансформацию до представления в кодовую модель, осуществлять генерацию исходного кода.
|