В предыдущих главах книги мы изучали структуру базы данных, т.е. структуру так называемой внутренней системы (back end). В части 4 мы рассматриваем структуру программных единиц, называемых внешней системой (front end). Внешняя система охватывает экранные формы и отчеты, которые поддерживает база данных. В традиционном приложении базы данных эти две системы четко определены: внутренняя включает таблицы, индексы и представления, а внешняя — форматирующую и прикладную логику.
В этой главе проводится исследование факторов, требующих, чтобы проектировщики четко разграничивали управление пользовательским интерфейсом и выполнение операций обработки. Эти факторы являются основой многих предположений, которые мы делаем в главах о конкретных архитектурах, в частности в разговоре об архитектуре клиент/сервер в главе 11. В этой главе освещаются проблемы, возникающие в случае, когда пользовательский интерфейс и база данных разделены сетью.
Правила для данных, процессов и интерфейса
Недостатки многих прикладных систем вызваны тем, что в них не определены различия между правилами для данных, правилами для процессов и правилами для интерфейса. В этом разделе мы попробуем их разграничить.
Группы правил
Назначение каждой из упомянутых выше групп правил можно описать так.
• В правилах для данных формулируются условия, которым должны удовлетворять данные. Эти правила действуют для каждого экземпляра данных, к которым они относятся, и обычно выводятся из модели данных.
• Правила для процессов определяют, что может (и что не может) делать приложение. Эти правила обычно выводятся из модели функций.
• Правила для интерфейса устанавливают, каким должен видеть приложение конечный пользователь. Такие правила влияют только на представление пользователя о приложении, а обработки не касаются. Они выводятся из спецификации пользовательского интерфейса.
Правила должны быть атомарными, т.е. сложные правила следует разбить на компоненты. Кроме того, необходимо исследовать все правила на предмет того, оправданы ли они.
Мы не будем уточнять и раскрывать приведенные выше определения, а рассмотрим ряд примеров. Сначала мы представим правила без каких-либо комментариев, и вы сможете сформировать собственное мнение о том, к какой группе относится то или иное правило. Затем мы приведем правила повторно — уже с комментариями.
Итак, вот набор правил, над которыми вам предстоит подумать.
• Пол человека должен быть либо "F" ("Ж"), либо "М" ("М").
• Каждый заказ должен быть предназначен для одного и только одного покупателя.
• Каждая строка должна быть частью одного и только одного заказа и относиться к одному и только одному коду товара.
• Пособие не должно выплачиваться лицу, сбережения которого превышают $16000.
• Только руководитель может санкционировать выплату денежного вознаграждения.
• Все торговые операции, проводимые в воскресенье, учитываются в бухгалтерских записях за следующий понедельник.
• Система инициирует поиск затребованного товара по введенному коду и обновляет экранную форму с описанием и ценой. Если обнаруживается более дешевая эквивалентная марка, то она выводится на экран вместо первоначальной.
• Если пользователь выбирает несанкционированный заказ, стоимость которого превышает установленный для него лимит, кнопка Authorize в экранной форме должна быть запрещена для выбора (окрашена в серый цвет).
• Каждый платеж имеет уникальный номер, выделенный системой.
• Все коды валют должны раскрываться, т.е. рядом с кодом при вводе должно указываться полное название валюты.
• Обновление записей о платежах не разрешается.
• При каждом изменении в записях о покупателях требуется создавать контрольную запись об изменении.
Интерфейсный процесс и архитектура клиент/сервер
Поскольку Oracle7 поддерживает и хранимые процедуры, и триггеры, то в среде клиент/сервер большая часть логики может (и должна) размещаться в базе данных на сервере. В техническом плане Oracle7 использует модель клиент/сервер даже в том случае, когда и клиент (прикладная программа), и сервер (база данных) располагаются на одной машине. Почему? Потому что два этих компонента взаимодействуют друг с другом с помощью сообщений, а не традиционных структур вызовов. Это позволяет двум компонентам — прикладной программе и СУБД — работать как отдельным процессам. Они могут находиться на одной машине (такую архитектуру обычно называют двухзадачной) или на разных машинах, связанных сетью. Именно последний случай обычно имеется в виду, когда говорят о системе клиент/сервер.
Надеемся, что хотя бы некоторые из вас попытались определить, к какой группе относится то или иное правило. Если вы лишь бегло взглянули на них, хотелось бы убедить вас вернуться и просмотреть правила еще раз, поскольку далее мы затронем ряд вопросов, знание которых, на наш взгляд, просто необходимо для эффективного проектирования. На примере этих правил продемонстрированы некоторые проблемы, которые, как мы часто видели, знаменуют собой разницу между успешным и недостаточно удачным проектами.
А теперь — независимо от того, пытались вы проанализировать эти примеры или нет, — перейдем к анализу каждого из сформулированных выше правил.
Пол человека должен быть либо "F" ("Ж"), либо "М" ("М").
Это правило для данных, которое можно ввести с помощью ограничения CHECK. Значение в столбце для пола человека должно присутствовать и равняться либо "Ж", либо "М". Конечно, может иметь место ошибка анализа или проектирования; возможно, нам потребуется допустить отсутствие этого значения.
Нам приходилось работать с Клиффом Лонгманом, который много лет отвечал в Oracle за архитектуру CASE-средств. На занятиях по стратегии бизнес-анализа он обычно говорил: "Правило — это правило, и оно безоговорочное. Оно может быть неверным, но в этом случае оно безоговорочно неверное". В этом — важная особенность любого правила: в нем категорически утверждается, как будет себя вести приложение, независимо от того, как правило сформулировано — верно или неверно. Вот почему мы должны быть очень внимательны, с тем чтобы понять его правильно.
Каждый заказ должен быть предназначен для одного и только одного покупателя.
Это правило для данных, которое можно ввести с помощью комбинации ограничений PRIMERY KEY и NOT NULL.
Каждая строка должна быть частью одного и только одного заказа и относиться к одному и только к одному коду товара.
Это не одно, а два правила для данных, каждое из которых можно ввести с помощью комбинации ограничений PRIMERY KEY и NOT NULL. Выполнить изменение структуры будет гораздо проще, если сформулировать два отдельных правила, у каждого из которых будут свои достоинства и недостатки.
Пособие не должно выплачиваться лицу, сбережения которого превышают $16000.
Это правило для процессов. В нем ничего не говорится о содержимом и определениях базы данных, а, скорее, выражается мысль о том, что может происходить, а что не может. Если это правило вообще подлежит автоматизации (а не делается, например, в виде канцелярской проверки), то его следует реализовать внутри приложения, а не интерфейса или базы данных.
Только руководитель может санкционировать выплату денежного вознаграждения.
Это правило также для процессов. В момент санкционирования платежа приложение должно проверить наличие у пользователя надлежащих прав, т.е. является ли он руководителем. Многие разработчики пытаются реализовать такие правила как правила для данных, но это неверно, потому что эта связь временная (зависит от времени), а не постоянная (как в первых трех примерах).
Все торговые операции, проводимые в воскресенье, учитываются в бухгалтерских книгах за следующий понедельник.
Здесь, фактически, два правила. Первое гласит, что проводки по торговым операциям нельзя делать в бухгалтерских записях за воскресенье. Это правило можно ввести ограничением CHECK. Второе правило — правило для процессов, которое разъясняет приложению, как откорректировать дату так, чтобы она стала приемлемой: оно должно выявить все даты, припадающие на воскресенье, и прибавить к ним единицу. Разбивая эти правила таким образом, мы избегаем ситуации, когда в приложении появляется оператор INSERT, выдающий значение для столбца лишь для того, чтобы обнаружить, что в строке базы данных появилось другое значение. Такая форма подрыва функциональности операторов была недопустима в бета-версиях Oracle версии 7.0, однако под давлением покупателей появилась в промышленной версии. Многие из нас считают, что это послабление стало неблагоразумной уступкой со стороны Oracle, позволяющей ее клиентам допускать ошибки при проектировании.
Более удачным решением этой проблемы может быть сопровождение двух дат, transaction_date и effective_date, и создание правила для данных, которое делает effective_date производным полем. В этом случае указанное правило можно трансформировать в правило для данных, а логику обработки освободить от требования указывать действительную дату.
Система инициирует поиск затребованного товара по введенному коду и обновляет экранную форму с описанием и ценой. Если обнаруживается более дешевая эквивалентная марка, то она выводится на экран вместо первоначальной.
Фактически, это правило для процессов. Оно гласит, что запрос на выборку подробных сведений о товаре (в некоторой части приложения) должен предоставлять сведения о самом дешевом варианте. Второе предложение совершенно неуместно, и его следует удалить из правила. Если наша система работает с приемлемым временем реакции, то пользователи никогда не смогут прочитать то, что было выведено на экран вначале, поскольку информация меняется очень быстро. Мы не приводили правил для процессов (и, вероятно, правил для данных), указывающих, как найти более дешевую эквивалентную марку, но предположим, что они есть где-то в спецификациях.
Если пользователь выбирает несанкционированный заказ, стоимость которого превышает установленный для него лимит, кнопка Authorize в экранной форме должна быть окрашена в серый цвет.
Здесь, вероятно, три правила: два общих и одно конкретное. Одно общее правило, скорее всего, устанавливает, что запрещенные для выбора возможности должны быть показаны на экране как таковые, а второе — что для кнопок это делается путем окрашивания их в серый цвет. Третье правило гласит, что средство Authorize должно при определенных обстоятельствах отключаться. Конечно, ему может соответствовать функциональная клавиша или пункт меню, но в этом случае они должны удовлетворять каким-то другим общим правилам.
Однако если мы оставим все как есть, то это будет означать, что мы рассчитываем на ввод уровней санкционирования со стороны интерфейсного кода, и поэтому придется также ввести правило для процессов, гласящее, что пользователь не может санкционировать заказы, стоимость которых превышает установленный лимит. Это правило будет вводиться в рамках менеджера данных как часть хранимой процедуры, а также в интерфейсе. Такое дублирование рассматривается в следующем разделе.
Каждый платеж имеет уникальный номер, выделенный системой.
Здесь тоже два правила, маскирующихся под одно. Первое (относительно уникальности) — это простое правило для данных, которое можно ввести с помощью ограничения PRIMERY KEY или UNIQUE. Второе правило предполагает работу по назначению номера. Мы предположили бы, что структура данных предоставляет механизм для выполнения этой задачи, который может быть последовательностью Oracle или, если нужен более жесткий контроль и бухгалтеры терпеть не могут разрывов в последовательности, однострочной таблицей, содержащей следующий свободный номер.
Каждый, кто хорошо знает Oracle, может, конечно, возразить, что логике обработки не нужно знать о механизме назначения номеров. Почему? Потому что мы можем написать триггер BEFOR INSERT уровня строки, применяющий выделенный системой номер. К сожалению, это означает, что процесс не может выбрать только что созданную запись или связать с ней другие записи, потому что не знает номер! Настойчивый сторонник SQL затем напишет триггер так, чтобы он заносил выделенный номер в глобальную переменную пакета, из которой его может выбрать приложение.
Если в коде триггеров и процедур встречаются структуры, подобные этим, то это означает, что разработчики перепутали правила для данных с правилами для процессов. Проектируя систему под распределенную архитектуру, мы должны уточнить это правило и выяснить, в каких масштабах необходимо обеспечить уникальность номеров — офиса или всей организации.
Все коды валют должны раскрываться, т.е. рядом с кодом при вводе должно указываться полное название.
Это чистое правило для интерфейса, взятое из спецификации пользовательского интерфейса реальной системы. На первый взгляд это требование кажется совершенно разумным и обоснованным, однако простое исследование показывает, что пользователи системы по привычке оперируют кодами валют, а не их названиям. Им не нужна система, которая сообщала бы при вводе кода USD, что это означает United States Dollars.
В большинстве организаций есть системы сокращений, уже ставшие частью их языка и культуры. Новые пользователи, еще не знакомые с этой лексикой, могут обращаться к оперативной справочной системе или всплывающему списку значений, если не знают смысл того или иного кода, но большинство пользователей просто хотят вводить знакомый код. Для организации поиска и вывода описательного текста необходимо экранное пространство и время, особенно там, где данные не кэшируются интерфейсом и требуется обращение к базе данных. (Вопросы кэширования рассматриваются в главе 11.)
Обновление записей о платежах не разрешается.
Это правило для данных, которое следует вводить при помощи простого триггера BEFOR UPDATE на уровне предложения. Этот триггер должен генерировать ошибку приложения.
Каждое изменение в записях о покупателях требует создания контрольной записи об изменении.
Это правило для данных, которое следует вводить при помощи триггера BEFOR UPDATE уровня строки в таблице покупателей.
Подготовка и утверждение документации
Хотя три группы правил, которые мы сформулировали в предыдущем разделе, тесно взаимосвязаны, важно разделить их в документации, представляемой пользователям, и, что еще более важно, в документации, на основе которой осуществляются утверждение и сдача в эксплуатацию. В результате на этапе проектирования мы должны стремиться получить три документа (или три набора документов):
• Структура интерфейса. Этот документ ориентирован главным образом на пользователей. Из него пользователи узнают, что именно они увидят на экране и как будут взаимодействовать с тем, что увидят. Документ не должен быть загроможден техническими деталями. Приемка на пользовательском уровне займет меньше времени, если документ будет сжатым и простым, а не потребует от пользователей знания основ технологии.
• Структура процессов относится к структуре интерфейса и определяет, как должна быть реализована эта структура и необходимые для нее сервисы.
• Структура данных задает основные объекты базы данных, с которыми должны работать процессы.
На этапе проектирования можно макетировать пользовательский интерфейс, если это обеспечит выполнение требований, поставленных пользователем. Однако при этом следует быть осторожным и не создавать макет интерфейса, предполагающий наличие сервисов, которые не могут быть реализованы существующими процессами.
Ограничивающим фактором в большинстве случаев является производительность. Можно, конечно, разработать структуру интерфейса, которая предполагает вывод на экран плана улицы и позволяет пользователю выбрать участок, а затем нажатием кнопки выполнить обновление для всех жителей этого участка. На макете эта операция будет выполняться мгновенно. В реальной же системе может потребоваться сложный процесс поиска жителей по почтовому индексу. Обновление также может занять довольно много времени, если речь идет о густонаселенном участке. Вследствие этого в "живой" системе подобный поиск, возможно, придется осуществлять в пакетном режиме. Если пользователи утвердили именно такую структуру интерфейса и требуют ее реализации, то это повлечет за собой серьезные проблемы.
Важным вопросом является размещение логики (кода или объявлений), которая реализует три группы правил, описанных в предыдущем разделе.
С точки зрения расположения логики правила должны быть реализованы следующим образом.
• Правила для интерфейса следует реализовать во внешней системе, независимо от того, что она собой представляет — программу на Visual Basic или Delphi, экранную форму, созданную в Oracle Forms, или генератор отчетов.
• Правила для процессов следует реализовать как процедуры, вызываемые из внешней системы, но они не обязательно должны входить в ее состав.
• Правила для данных следует реализовать в самом менеджере данных с помощью ограничений или триггеров.
Беглый анализ этих положений показывает, что месторасположение правил для интерфейса и правил для данных задано точно, тогда как вопрос о местонахождении правил для процессов остается несколько неопределенным (даже несмотря на четко сформулированное ограничение, гласящее, что любое правило для процессов, находящееся во внешней системе, не обязательно должно находиться там). Так, любая процедура, реализующая правило для процессов, должна быть отделена от кода, реализующего правило для интерфейса, и не должна зависеть от него.
Для случая, когда независимые процедуры для процессов написаны на PL/SQL, в Oracle Forms есть привлекательное средство, позволяющее "перетащить" эти процедуры из внешней системы во внутреннюю. Хотя эта возможность очень мощная и подчеркивает, что правила для процессов могут существовать либо в клиентском, либо в серверном пространстве, ее применение несколько ограничивается тем фактом, что Oracle Forms в настоящее время поддерживает лишь PL/SQL версии 1, а не более позднюю и значительно улучшенную версию 2.х, с которой работает процедурная опция Огас1е7.
Все идет очень хорошо, но некоторое время спустя наши пользователи начинают просить о более информативной обратной связи при обработке недопустимых значений данных. Возьмем, например, первое рассматриваемое нами правило для данных:
Пол человека должен быть либо "F" ("Ж"), либо "М" ("М")
В спецификации интерфейса будет полностью обоснованным требование о том, что это правило подлежит проверке, как только пользователь переходит на следующее поле. (В этом примере подразумевается, что значения вводятся непосредственно, а не выбираются из группы селекторных кнопок, что само по себе представляет форму локальной реализации правила в интерфейсе.)
Проектировщик может подумать, что он стоит перед выбором: реализовать эту проверку в интерфейсе как правило для интерфейса или реализовать ее на сервере данных как правило для данных. На самом деле этого выбора не существует — есть, по сути дела, два правила, работающих в разных средах, и оба они должны быть реализованы. Правило для интерфейса должно быть реализовано потому, что пользователи требуют моментальной обратной связи, а правило для данных потому, что в обеспечении целостности данных сервер не должен зависеть от интерфейса.
Когда мы представляем эти соображения группе программистов или проектировщиков, они обычно начинают беспокоиться о снижении производительности и возможности выхода двух этих правил из синхронного режима. Давайте рассмотрим эти проблемы по очереди. Избыточная проверка осуществляется в интерфейсе, и, как правило, во внешней системе (особенно в средах клиент/сервер) нет дефицита ресурсов ЦП. Кроме того, эту проверку выполнять очень легко. Если принять в качестве аргумента положение о том, что эта проверка должна выполняться во внутренней системе независимо от того, выполняется ли она во внешней системе, то можно считать, что ответ на вопрос о причинах снижения производительности найден.
CREATE TABLE emp
( empno ...
, ...
, sex CHAR ( 1 ) NOT NULL CONSTRAINT (emp_ccl)
CHECK (sex IN ('M','F'))
, ...
Если эти правила выходят из синхронного режима, то мы фактически получаем преимущество. Рассмотрим случай, когда интерфейс требует, чтобы значение было либо "f", либо "m", а база данных — чтобы оно было либо "F", либо "М". Значение, которое проходит проверку в интерфейсе, в базе данных ее не пройдет, и ввести либо изменить значение поля Sex с помощью этого интерфейса совершенно невозможно. В результате код интерфейса (или, по крайней мере, часть его) будет неработоспособным. Этот факт быстро высветит проблему и приведет к тому, что на нее обратят внимание и устранят. Представьте себе ситуацию, в которую мы попали бы, если бы в базе данных проверка не выполнялась. В этом случае приложение с ошибкой установило бы строчные значения и испортило бы базу данных.
Большинство проектировщиков и администраторов БД предпочитают, чтобы то или иное средство вообще перестало работать, чем продолжало работать неправильно, но многие пользователи не столь дальновидны. Не уподобляйтесь им!
Если мы решили реализовать правило в интерфейсе, то должны быть очень осторожны в вопросах блокировки (которые подробно рассматриваются в главе 18). Давайте поговорим о следующем правиле:
Счет клиента не может дебетоваться, если текущий остаток за вычетом суммы данной дебетовой операции превышает кредитный лимит по счету.
Предположим, что для ввода суммы дебетовой операции мы пользуемся экранной формой и что это правило реализовано в интерфейсе. Интерфейс делает следующее:
1. Выбирает сведения о счете, в том числе о кредитном лимите и текущем остатке.
2. Дает возможность пользователю ввести сведения о новой дебетовой операции (получатель платежа, дата, сумма и т.д.).
3. Если верно выражение остаток — сумма дебетовой операции > кредитный лимит, фиксирует транзакцию. В противном случае выдает ошибку и производит откат транзакции.
Проблема при такой реализации заключается в том, что правило может не сработать, если в промежутке времени между шагами 1 и 3 другие зафиксированные транзакции внесли изменение в счет или другой пользователь обновил кредитный лимит. Вероятность этого может быть довольно высокой, поскольку на шаге 2 мы ждем, пока пользователь введет данные в экранную форму.
Можно попробовать разрешить эту ситуацию с помощью "предварительной", или "пессимистической", блокировки на шаге 1 посредством предложения SELECT...FOR UPDATE, но эта блокировка должна оставаться в силе, пока мы ожидаем ввода данных пользователем.
Даже этот метод не поможет, если остаток не хранится в записи об остатке как производное значение (предположительно, ведомое триггерами). Если остаток запрашивается путем выполнения операции подведения итога, то единственный путь, позволяющий гарантировать истинность результата, — установить разделяемую блокировку на каждой таблице, содержимое которой может учитываться при вычислении конечного результата. Если таблицы подвержены интенсивным изменениям, то устанавливать для них разделяемые блокировки на время пользовательского ввода-вывода — это подход, отнюдь не повышающий производительность.
С аналогичной проблемой приходится сталкиваться при вводе уникальных ключей. Интерфейс технически может гарантировать уникальность вводимого ключа только в том случае, если:
• реально выполняется требуемая DML-операция;
• устанавливается разделяемая блокировка на таблице, над которой в конечном итоге будет выполнена эта DML-операция.
Очень эффективное решение всех этих проблем — выполнить проверку в интерфейсе без блокировки, а затем повторить ее в логике обработки в момент фиксации. (Во многих случаях логика обработки не обязательно должна явно выполнять эту проверку, потому что при выдаче DML-операций проверка выполняется неявно.)
Такая стратегия, предполагающая наличие двух типов проверок (мы называем их рекомендуемой и обязательной проверками), может быть использована для того, чтобы получить возможность продолжать практически любую транзакцию без блокировки ввода-вывода с терминала. Кроме того, она обеспечивает высокий уровень обратной связи с пользователем. Однако, как известно, ничто в этом мире не достается бесплатно. Помимо графика сообщений и нагрузки на ЦП, связанной с запросами к базе данных, необходимыми для выполнения рекомендуемых проверок, существует также риск того, что рекомендуемая проверка даст ошибочный результат.
Может случиться и так, что рекомендуемая проверка пройдет, а окончательная фиксация окажется невозможной из-за результатов обязательной проверки (например, потому что другой пользователь фиксирует транзакцию, где используется кредит, который ваша транзакция определила как имеющийся, но не заблокировала). Кроме того, в некоторых случаях проверка может сообщить о нарушении там, где фиксация была бы успешно выполнена. По этой причине мы советуем делать так, чтобы при рекомендуемой проверке способ сообщения об ошибке позволял пользователю продолжать работу (по его усмотрению) после рекомендательного сообщения. В среде, где пользователь может одновременно выполнять несколько транзакций, это также даст ему возможность решить проблему в параллельной транзакции, например путем увеличения кредитного лимита покупателя.
Качественная подготовка пользователей и продуманная оперативная справочная система сделают этот подход гораздо более подходящим для пользовательского сообщества. Те немногие аномалии, которые могут иметь место, — невысокая цена за обеспечение приемлемой производительности и целостности данных.
Разговор, который мы ведем в этой главе, в первую очередь касается трехуровневой архитектуры, в которой приложение разделено на три части, осуществляющие:
• управление пользовательским интерфейсом;
• выполнение правил обработки;
• выполнение функций хранения и выборки данных.
Одно из следствий использования трехуровневой архитектуры состоит в том, что у нас появляется возможность жестко разделить правила для процессов и правила для данных. Первые реализуются исключительно на среднем уровне, а вторые чаще всего на уровне управления данными. Одно из самых значительных преимуществ трехуровневой архитектуры заключается в том, что она позволяет приложению (среднему уровню) использовать несколько разных серверов данных без применения каких-либо шлюзовых продуктов для стыковки этих серверов между собой.
Таким образом, мы можем хранить записи о покупателях в обычных индексированных файлах, записи о заказах — в базе данных Oracle RdB, а записи о запасах — в базе данных Oracle7. Для выборки этих данных приложение вызывает каждую базу данных с помощью соответствующих вызовов, а отдельные совокупности данных не знают о существовании друг друга. К сожалению, это, в свою очередь, означает, что все вопросы целостности ссылок теперь должны решаться приложением. Поскольку ни один из этих "складов" данных ничего не знает о других, никто из них не может отвечать за ведение перекрестных ограничений между базами данных.
Хотя Oracle7 может работать в трехуровневой архитектуре как уровень управления данными, чаще всего эта СУБД разворачивается в двухуровневой архитектуре, где код пользовательского интерфейса (программа управления экранной формой или генератор отчетов) непосредственно взаимодействует с БД через SQL*Net, а средний уровень создается хранимыми процедурами PL/SQL (обычно в форме пакетов), что повышает производительность и улучшает управляемость.
Для большинства приложений Oracle7 мы рекомендуем следующий порядок размещения логики:
• правила для интерфейса — во внешней системе (Oracle Forms, PowerBuilder и т.д.);
• правила для процессов — в пакетах PL/SQL;
• правила для данных — как ограничения и триггеры.
В идеале интерфейсные процессы не смогут непосредственно вставлять, обновлять и удалять данные из таблиц, а будут вынуждены выполнять все DML-операции через хранимый PL/SQL-код. Хотя этот подход плохо сочетается со многими предлагаемыми на рынке средствами ускоренной разработки приложений (которые расхваливают за способность генерировать DML-код), он все же позволяет сконцентрировать правила для процессов в одном месте — в базе данных, чтобы их могли совместно использовать все интерфейсы.
Предоставление возможности нескольким интерфейсам совместно использовать один экземпляр правил для процессов может быть важным фактором обеспечения целостности очень распределенных приложений, использующих несколько интерфейсов, и может повлечь за собой существенное повышение производительности.
Повышенные требования к связываемости
Чем выше требования к связываемости, тем больше смысла в том, чтобы интерфейсный код касался исключительно правил для интерфейса и предусмотренной рекомендуемой проверки. Такое разделение гарантирует, что пользователи, работающие с интерфейсами разных типов, при выдаче идентичных запросов к идентичным данным получат одни и те же результаты.
Если уровень связываемости таков, что имеют место транзакции, которые обращаются к нескольким серверам данных разных типов, то вполне заслуживает внимания идея построения формального среднего уровня — либо на одном из серверов данных, либо, что более вероятно, на выделенном сервере приложений. Естественно, это приведет к тому, что потребуется рассмотреть возможность использования менеджера транзакций (например, Tuxedo и Encina). Хотя рассмотрение этого предмета выходит за рамки нашей книги, стоит еще раз упомянуть о том, что для эффективного развертывания менеджера транзакций обычно необходимо применять разделяемые соединения внутри Oracle, а они, в свою очередь, требуют использования атомарных транзакций.
Применение в пользовательском интерфейсе рекомендуемой, а не обязательной проверки означает, что этот интерфейс может работать, не устанавливая блокировки до тех пор, пока пользователь не захочет зафиксировать свои изменения. Таким образом, каждый вызов из пользовательского интерфейса можно передавать на соответствующий сервер данных как атомарную транзакцию.