Для побудови бізнес-логіки використовується ієрархічна модель функціональних елементів. Тобто базовим елементом бізнес-логіки є функція - компонент, яка реалізована за допомогою об'єкта класу Command з методом Exec. це типовий шаблон проектування Command
Є чотири базових класу Command. Operation. Process. Application:
- Command є логіку, яка виконується послідовно і безперервно на стороні сервера
- Operation є окремим випадком Command і є інтерактивною командою, яка взаємодіє з клієнтом (під клієнтом розуміється будь-який клієнтську програму)
- Process є окремим випадком операції і є довгограючою операцією, яка час від часу зберігає свій поточний стан на сервері незалежно від часу життя клієнтського додатку. Process має набір станів і до кожного станом прив'язується певний набір операцій, доступних в цьому стані, в тому числі в якості операції процесу може виступати інший процес, в результаті чого він стає підпроцесом.
- Application є окремим випадком процесу і є Синглетон в розрізі клієнтів. Тобто кожен клієнт (клієнтську програму) має екземпляр додатку, причому єдиний. При цьому Application є коренем додатки. Його точкою входу і виходу.
Таким чином бізнес-логіка будується в вигляді дерева з коренем Application. набором станів додатки і вкладеними операціями, прив'язаними до цих станів. Дерево має необмежену кількість рівнів.
Розглянемо як приклад майданчик для проведення аукціонів, реалізовану у вигляді WEB-додатки. Потрапити на майданчик може будь-який користувач, в тому числі не зареєстрований на майданчику. Проте, такий користувач має доступ до деякого функціоналу:
Всі ці операції доступні для додатка в стані New, яке означає, то процес запущений, але ще не має збереженого примірника в базі даних (або ще не инициализирован екземпляр).
Решта операції, які повторюються в обох станах має сенс винести в безумовні операції, так як вони не залежать від стану програми. Така можливість у процесів теж є. Тобто в процесі можна прописувати операції, залежні від контексту (прив'язані до стану), і не залежні від контексту, які можна виконати в будь-якому стані процесу, головне, щоб доступ до самого процесу був.
З урахуванням вищевказаного документа можна перебудувати дерево бізнес-логіки.
Тепер спробуємо розписати процес реєстрації нового користувача на майданчику Registration.
Тоді цілком наше дерево буде виглядати так:
В результаті отримуємо дерево, яке описує цілком все наше додаток, де доступ налаштовується тільки для запуску процесів і операцій першого рівня програми Application в стані Registered.
Дане дерево наочно показує всю логіку програми та послідовність його розробки.
Після такого проектування і узгодження із замовником залишається налаштувати процеси і реалізувати операції. Операції реалізуються відповідно до шаблону проектування MVC, де в операція виступає в якості контролера.
Запуск вкладеного процесу може відбуватися за кількома сценаріями:
- Синхронний запуск - означає, що процес, всередині якого синхронно запущений дочірній процес, стає недоступним поки свою роботу не завершить дочірній процес
- Асинхронний запуск - означає, що батьківський процес не чекає завершення дочірнього і дає доступ до запуску інших підпроцесів.
- Ініціює запуск - означає запуск залежного подпроцесса, призначеного для іншого суб'єкта, наприклад, запуск процесу перевірки заявки на реєстрацію, призначеного для оператора. Ініціює запуск може бути як синхронним, так і асинхронним. / Li>
- Множинний запуск - означає, що можна запустити кілька вкладених процесів одного і того ж типу, інакше повторний запуск подпроцесса буде приводити до доступу вже наявного підпроцесу.
Кожен екземпляр процесу безпосередньо пов'язаний з суб'єктом, який його створив, або якій призначений процес. Ніхто інший не може отримати доступ до процесу. Система розмежування прав доступу при такій побудові бизне-логіки набуває нового змісту.
- Виклик будь-якої операції або команди можливий тільки при вказівці абсолютного шляху до неї. Наприклад, op = Cabinet - відкрити особистий кабінет (коренева команда Application опускається). Кабінет відкриється, тільки якщо Application знаходиться в стані Registered. op = Cabinet / WorkSpace - відкриється робочий стіл особистого кабінету. Виклик операції op = WorkSpace безпосередньо ні до чого не приведе (крім повідомлення про відсутність доступу), тому що всередині Application такої операції немає, і вона з'являється, тільки коли инициализируется операція Cabinet. І відповідно викликати команди AddPanel і RemovePanel, які зареєстровані в операції WorkSpace також не вийде, поки не зайдеш в WorkSpace
- Виклик процесу відбувається за іншим сценарієм. Для звернення до вже наявного примірнику процесу вказується його тип і ідентифікатор, наприклад op = RegistrationOID = 8253. При цьому на стороні сервера відбувається перевірка приналежності процесу викликає соб'екту (по полю Subject в процесі). Для запуску нового процесу потрібно вказати батьківський процес (крім Application, який завжди ініціалізується і є Синглетон), його ідентифікатор і ім'я вкладеного процесу, наприклад op = Registration / SendRequestOID = 8253. При цьому запуск подпроцесса SendRequest буде можливий тільки в тому випадку, якщо процес Registration з ідентифікатором OID = 8253 належить користувачеві і знаходиться в стані, в якому доступний подпроцесс.
У підсумку маємо одночасно спосіб опису бізнес-логіки й засіб розмежування прав доступу.
Клас Command простий до неподобства. Має віртуальний метод Exec ($ params), де $ params - асоціативний масив, де ключ - назва параметра, а value - значення параметра. У спадкоємців класу Command в перекритому методі Exec пишеться логіка. Приклади команд на поточний момент: Transaction - команда, яка відкриває транзакцію, ініціює подія, обробник якого виконує логіку закриває транзакцію або відкочується в залежності від результату виконаної логіки
Sendmail - команда, яка відправляє електронного листа
Операція регламентує бізнес-логіку більш жорстко. Основним також є метод Exec. Але в спадкоємців він не перекривається. Метод Exec виглядає так:
Спочатку йде виклик віртуального методу Init, який реєструє вкладені операції і команди, які як раз і будуть доступні всередині даної операції, приклад реалізації методу Init.
Цей метод використовується в особистому кабінеті і визначає які сторінки в особистому кабінеті доступні. Оскільки вкладених операцій може бути досить багато, а кожну операцію потрібно скомпілювати, і при цьому в кожному сеансі буде обрана лише одна з них, то логічніше реєструвати не власними операції, а проксіоб'екти - команди, які вже будуть створювати операції, наприклад:
Це вже оптимізація швидкодії.
Потім в методі Exec операції визначається підоперацій, якщо вона вказана. Дивимося параметр op. До слова сказати, в $ params сидить $ _GET. У кожну операцію в параметрі op приходить рядок, в якій вже вирізана рутовий частина, що відноситься до поточної операції, тобто в операції Application рядок op виглядає так op = Cabinet / WorkSpace, а в операції Cabinet рядок op виглядає так: op = WorkSpace. Тому наступний код якраз і займається тим, що виокремлює подальший шлях, шукає вкладену серед зарегістріргованних вищевказаним методом Init і якщо знаходить, то передає їй управління, обрізавши кореневу частину рядка. Якщо не знаходить, видає помилку. Якщо вже була досягнута остання точка шляху, то виконується логіка, визначена за замовчуванням. Тобто викликається метод ExecDefault. Потім виконується метод Run, в якому прописується логіка яку необхідно виконати незалежно від варіацій обраного шляху. І якщо все ж сталася помилка, то виконуємо метод HandleError, передавши йому текст помилки. Process ще більш жорстко регламентує логіку, настільки жорстко, що створювати спадкоємці не потрібно. Доступні операції і стану прописуються в базі даних, наприклад:
Спочатку реєструються операції, потім процеси, потім операції прив'язуються до станів процесів. Таким метод Init процесу звертається до бази даних, читає налаштування і реєструє доступними операції, причому в повному обсязі, а лише ті, що прив'язані до поточного стану. Стан визначається за поданою OID процесу. Якщо OID не переданий, то значить це новий процес і стан = New.
Як уже повідомлялося, ми не перекриваємо клас Process в шарі бізнес-логіки, виняток становить тільки клас Application. Він инициализируется без OID, по зв'язку з поточним користувачем і отрисовку сторінки виконує самостійно, на відміну від процесу, який сформовану сторінку повертає викликала його функції. Application-у повертати сторінку вже нікуди, тому він її ехает.
Поскільки Application і Process є самодостатніми, то залишається розробка операцій і команд. Про розробку команд вище вже повідомлялося. Потрібно просто перекривати метод Exec, тому розглянемо докладно розробку операцій. Зробимо це на прикладі адмінки.
Створимо операцію Administration. Прикріпимо її до Application в стані Registered. Доступ до неї повинен отримувати тільки адміністратор системи. Для цього є два способи:
- У методі Init викликати збережену процедуру, яка буде перевіряти чи є користувач адміністратором і, якщо немає, то створювати виключення.
- Прив'язати операцію до процесу за умовою, тобто додати до зв'язці процес-стан-операція ще Conditon (IsAdministrator). Тоді всі, хто не адмін, не отримуватимуть доступ до операції
Виберемо перший спосіб (чисто для прикладу, на практи я реалізую другий спосіб).
1. Зареєструємо її в базі.
2. Створюємо клас Administration в PHP, успадковуємо від Operation
Перекриваємо метод Init
Дочірня операція Object надає доступ до будь-якої дії будь-якого об'єкта, її потрібно реалізувати. Але це буде далі, а зараз продовжимо реалізовувати операцію Administration. Що повинна робити дана операція за замовчуванням? Напевно, виводити список всіх класів.
Перекриємо метод ExecDefault.
Тобто за замовчуванням буде викликатися операція Object, яка буде викликати дію об'єкта List. А клас об'єкта - Class, тобто виводимо список об'єктів типу Class. Тепер потрібно перекрити метод Page, який буде виводити html на екран.
Припускаємо, що сторінку завжди буде формувати вкладена операція.
Не буду детально розписувати операцію Object, просто розпишу словами, що вона повинна являти собою. Поскільки ієрархія бізнес-логіки це один з шарів MVC, то другі два це шар бізнес-сутностей і інтерфейсу. Як влаштований шар Бізнес-сутностей ви вже знаєте. Це ієрархія класів бізнес-сутностей, і в кожної зареєстрований набір дій Edit, Create, View, Delete, Print і т.д. Це означає, що метод Init операції Object, повинен створити екземпляр класу бізнес-сутності і отримати у нього список дій. Базове дію Action є спадкоємцем операції. А метод Exec повинен виболніть це дія. Таким чином команда виду op = Admin / Object / EditOID = тисячу двісті тридцять чотири створить операцію Admisitration (якщо є доступ), потім створить опреацию Object, яка створить сутність, знайдену по параметру OID і виконає його дію Edit. Для створення нового об'єкта або виклику дії класу, потрібно вказувати не OID, а Class, наприклад op = Admin / Object / Createclass = Notice або op = Admin / Object / Listclass = Auction.