[Из песочницы] Переход от 2-х звенки к архитектуре служб в парадигме SOA

В данной статье я бы хотел поделиться своим опытом организации перехода от классической 2-х звенки к парадигме SOA, также затронуть некоторые аспекты деплоя в рамках enterprise-решения и интеграции со смежными службами написанными на JavaПредисторияПоследние 3 года я работал в отделе внутренней автоматизации компании Новотелеком. Основное развитие систем для автоматизации внутренних процессов смежных с IT подразделений началось в 2008 году вместе с активным ростом самой кампании. В то время руководство не ставила целей делать качественные решения, основной целью было завоевание рынка, и это отложило отпечаток на принимаемые решения. Основной из систем над которыми работает отдел ВА — это внутренняя CRM система, которая включает в себя также элементы планирования человеческих ресурсов и справочные системы. Долгое время система писалась на самописном фреймворке, но после знакомства с Yii и реализации сайта компании на нем, было принято решение перевода системы на данный фреймворк. Обсуждения почему был выбран именно этот фреймворк выходят за рамки моей статьи.При первоначальном переводе на Yii наша команда решила оставить архитектуру без кардинальных изменений т.к. темп разработки рос, а внутренние процессы организации работы были еще не на том уровне, чтобы ставить в приоритет архитектурные аспекты. Таким образом спустя год на Yii было переведено примерно треть функционала текущей системы. Определенно мы увеличили темп разработки т.к. фреймворк помог решить много типовых задач, на решение которых ранее уходило много времени. Но оставался один момент, который начал беспокоить все чаще. Нужно было все таки решить архитектурные вопросы, пока все не зашло в зону не возврата. Решающим фактором в принятии решения по переходу к службам вместо одного монолитного приложения стало выделение в кампании роли межсистемного архитектора, который начал приводить в порядок все внутренние процессы и архитектурные аспекты. На данную роль назначили архитектора одной из смежных групп, которые пишут ПО на Java. В их группе изначально была выбрана soa-парадигма и за последние 3–5 лет эта практика показала все плюсы и минусы данного подхода.Немного об окружении OS Debian 6 веб-сервер nginx php 5.3 framework yii система сборки и деплоя phing службы устанавливаются как deb-пакеты протокол интеграции со смежными службами hessian Ограничения Изначально был выбран подход разработки с использованием модулей, которые объединяли в себе слои бизнес логики, например, заявка клиента, карточка клиента и т.п. При переходе к службам встал вопрос как организовать структуру приложения реализовать, как деплоить, как хранить в SVN.Для решения подобных задач есть несколько подходов. Один из них в свое время предложили ребята из 2Gis в своей статье. Также можно использовать похожее решение в организации структуры приложения — Yiinitializr. Решение от 2amigos достаточно хорошее, но просто так взять и перенести все на него было уже достаточно проблематично. Поэтому пришлось сделать свой велосипед на основе уже имеющихся примеров и полученном опыте работы с YiiНемного о SOA На первом этапе были выделены следующие службы5ad66dd592ae89562f7e51592758401a.jpgНа схеме выделены 2 внутренних слоя: core и portalCore — слой общих компонентов, необходимых для корректной работы всех служб, плюс библиотеки, например yii, behat, ratcher и т.д., а также набор компонентов для интеграции со смежными службами.Portal — пользовательские интерфейсы.Кроме этого на схеме представлен слой внутренних и внешних служб и протоколы взаимодействия

Подробнее о реализации Каждое приложение, будь то служба или пользовательский интерфейс, имеет одну из двух точек входа: web и console. Например, в случае службы web точкой входа является rest api.Хранение конфигов сделано по аналогии с yiinitializr с учетом наших особенностей. Таким образом каждое приложение имеет следующую структуру конфигов:

service.web.php — конфиг для веб-части service.console.php — конфиг для консольной части service.test.php — конфиг для запуска автотестов service.base.php — общий конфиг для всех предыдущих. Подключается внутри каждого из них env/local.php — конфиг в который вынесены параметры, зависящие от окружения (dev, test или production) sections/routes.php — правила маршрутизации За инициализацию приложения отвечает компонент ApplicationDispatcher в пакете common, который реализует следующие функции: подготовка конфига, прописывание внутренних алиасов, прописывание маршрутов и предоставляет методы для получения путей до директорий в зависимости от окружения, например, до папки с временными файлами или логами.Компонент подключается в index.php и вызывается следующим образом

Исходный код index.php // Скрипт инициализации приложения if (! file_exists ('/usr/share/ntk-rm-common/protected/components/ApplicationDispatcher.php')) { throw new Exception ('Необходимо установить пакет ntk-rm-common'); }

require_once ('/usr/share/ntk-rm-common/protected/components/ApplicationDispatcher.php');

// Создаем и запускаем экземпляр приложения $dispatcher = ApplicationDispatcher: getInstance ();

// Указываем тип окружения: бой или разработка $dispatcher→setEnvironment (ApplicationDispatcher: ENV_PRODUCTION);

// Указываем тип приложения $dispatcher→setApplicationType (ApplicationDispatcher: APP_TYPE_WEB);

// Запускаем скрипт создания приложения $dispatcher→create ('crm')→run (); Исходный код метода create /** * Создание экземпляра приложения * @param $service — название инициализируемой службы * @return mixed * @throws Exception */ public function create ($service) { $this→service = $service; if (empty ($this→app_type)) { throw new Exception ('Укажите тип приложения: web или console'); } // Подключаем глобальный хелпер require_once $this→getBasePath ('common') . '/helpers/global.php'; $config = $this→prepareConfig (); // прописываем путь до папки с временными файлами $config['runtimePath'] = $this→getRuntimePath ($service); // прописываем путь до папки с исходниками — protected $config['basePath'] = $this→getBasePath ($service); $this→setAliases (); if ($this→app_type == self: APP_TYPE_WEB) { $this→app = Yii: createWebApplication ($config); // Подгружаем правила маршрутизации $this→setRoutes (); // Прописываем путь до папки assets в зависимости от окружения $basePath = $this→getHtdocsPath ($this→service) . '/assets/'; $this→app→getAssetManager ()→setBasePath ($basePath); } else { defined ('STDIN') or define ('STDIN', fopen ('php://stdin', 'r')); $this→app = Yii: createConsoleApplication ($config); } return $this→app; } Логика подготовки файла конфигурации мало чем отличается от той, что используется в Yii-Boilerplate или Yiinitializr /** * Склеивание конфигов в один. * @return array|mixed * @throws Exception — ошибка в случае если не найден конфиг приложения */ private function prepareConfig () {

if (!$this→isExistsServiceConfig ()) { throw new Exception ('Конфигурационный файл службы »' . $this→getServiceConfigName () . '» не найден. Проверьте правильность пути.'); }

// Подключаем конфиги службы $service_configs = array ( '/' . $this→service. '.' . $this→app_type. '.php', '/env/local.php' );

$config = $this→mergeConfigs (array (), $service_configs, $this→getConfigPath (($this→service)));

// Подключаем общие конфиги $common_configs = array ( '/env/local.php', '/common.base.php', $this→app_type == self: APP_TYPE_WEB? '/common.web.php' : '/common.console.php', );

$config = $this→mergeConfigs ($config, $common_configs, $this→getConfigPath ('common'));

// Подключаем конфиги backend части $backend_configs = array ( '/php-backend.base.php', '/env/local.php', );

$config = $this→mergeConfigs ($config, $backend_configs, $this→getConfigPath ('php-backend'));

return $config; } Структура пакета Как уже было сказано выше установка служб в production происходит через deb-пакеты. Для полной поддержки debian-way при установке deb-пакета приложение раскидывается по следующим директориям:/usr/share/<имя-службы>/protected/ — исходники службы /usr/share/doc/<имя-службы>/ — пример конфига local.default.php /usr/bin/<имя-службы> — исполняемый файл для запуска роботов /var/www/<имя-службы>/htdocs/ — директория для веб-севера /var/tmp/<имя-службы>/ — директория для хранения временных файлов /var/log/<имя-службы>/ директория для хранения логов службы Сборка пакета с помощью утилиты Phing За сборку пакета отвечает утилита phing, которая: выкачивает из svn актуальную версию из ветки trunk разносит все по нужным директориями в соответствии с тем как описано выше вызывает команду для сборки пакета создает метку в svn заливает пакет на сервер-репозиторий debian пакетов Пример кода из задачи по формированию структуры пакета

Заключение В заключении хотелось бы отметить, что у данного решения есть как плюсы, так и минусы. Из основных плюсов, полученных после внедрения данной схемы можно выделить поддержку debian-way для пакетов, что облегчает жизнь ребятам из поддержки. Также стало проще параллельно разрабатывать и внедрять новый функционал.Что можно почитать:

© Habrahabr.ru