📜 ⬆️ ⬇️

Transition from 2 links to service architecture in the SOA paradigm

In this article, I would like to share my experience in organizing the transition from the classic 2 links to the SOA paradigm, also touch on some aspects of deployment within an enterprise solution and integration with related services written in Java

Prehistory

For the last 3 years I have been working in the internal automation department of Novotelecom. The main development of systems for automating internal processes related to IT departments began in 2008 along with the active growth of the campaign itself. At that time, management did not set goals to make quality decisions, the main goal was to conquer the market, and this put an imprint on the decisions made. The main system on which the department of VA works is an internal CRM system, which also includes elements of human resource planning and help systems. For a long time, the system was written on a samopisny framework, but after becoming acquainted with Yii and implementing the company's website on it, it was decided to transfer the system to this framework. Discussions why this particular framework was chosen are beyond the scope of my article.

At the initial transfer to Yii, our team decided to leave the architecture without major changes, since the pace of development grew, and internal work organization processes were not yet at the same level as prioritizing architectural aspects. Thus, a year later, about a third of the functionality of the current system was transferred to Yii. We definitely increased the pace of development because The framework helped to solve many typical problems, the solution of which previously took a lot of time. But there was one moment that began to bother more and more. It was necessary all the same to solve architectural issues, until everything went into the zone of no return. The decisive factor in deciding on the transition to services instead of one monolithic application was the selection in the campaign of the role of an intersystem architect, who began to put in order all the internal processes and architectural aspects. An architect was assigned to this role from one of the adjacent groups that write software in Java. In their group, the soa paradigm was originally chosen and over the past 3-5 years this practice has shown all the pros and cons of this approach.

Little about the environment


')
Restrictions

Initially, a development approach was chosen using modules that combined business logic layers, for example, client application, client card, etc. When switching to services, the question was how to organize the structure of the application, how to deploy it, how to store it in SVN.
To solve such problems there are several approaches. One of them was proposed by the guys from 2Gis in his article . You can also use a similar solution in organizing the structure of the application - Yiinitializr . The decision from 2amigos is good enough, but just to take and transfer everything to it was already quite problematic. Therefore, I had to make my own bike based on the existing examples and experience gained with Yii.

Little bit about SOA

In the first stage, the following services were allocated.


The diagram has 2 internal layers: core and portal
Core - a layer of common components necessary for the correct operation of all services, plus libraries, for example, yii, behat, ratcher, etc., as well as a set of components for integration with related services.
Portal - user interfaces.
In addition, the diagram shows a layer of internal and external services and interaction protocols.

More about implementation

Each application, be it a service or user interface, has one of two entry points: web and console . For example, in the case of a web service, the entry point is rest api.

Storage of configs is done by analogy with yiinitializr, taking into account our features. Thus, each application has the following configs structure:

ApplicationDispatcher is responsible for application initialization in the common package, which implements the following functions: preparing the config, setting internal aliases, prescribing routes and provides methods for getting paths to directories depending on the environment, for example, to a folder with temporary files or logs.

The component is connected to index.php and called as follows.

Source code 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(); 


The source code of the create method
  /** *    * @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; } 


The logic of preparing the configuration file is not much different from that used in Yii-Boilerplate or 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; } 


Package structure

As mentioned above, the installation of services in production occurs via deb-packages. For full support of debian-way when installing a deb-package, the application is scattered in the following directories:

Build a package using the Phing utility

The phing utility is responsible for building the package, which:

Sample code from the task of forming the package structure
  <!-- ============================================ --> <!-- Target: prepare --> <!-- ============================================ --> <target name="prepare" depends="clean"> <echo msg="    " /> <mkdir dir="${project.packageDir}" /> <copy todir="${project.packageDir}"> <fileset dir="${project.basedir}/debian"> <include name="**/*" /> <exclude name=".svn" /> <exclude name="cron.d/" /> <exclude name="cron.d/*" /> </fileset> </copy> <exec command="svn info | grep 'URL: '" outputProperty="project.tmp.svnInfo" /> <php expression="end(explode(': ', '${project.tmp.svnInfo}'));" returnProperty="project.tmp.svnUrl" /> <echo msg="    SVN" /> <exec command="rm -Rf ${project.packageDir}/var/www/ntk-rm-crm/*" /> <exec command="svn export --force ${project.tmp.svnUrl} ${project.packageDir}/export/"/> <echo msg="     - /var/www/ntk-rm-crm/htdocs/" /> <mkdir dir="${project.packageDir}/var/www/ntk-rm-crm/" /> <copy todir="${project.packageDir}/var/www/ntk-rm-crm/htdocs/" > <fileset defaultexcludes="false" expandsymboliclinks="true" dir="${project.packageDir}/export/htdocs/"> <include name="**/*" /> </fileset> </copy> <mkdir dir="${project.packageDir}/var/www/ntk-rm-crm/htdocs/assets/" /> <copy file="${project.packageDir}/var/www/ntk-rm-crm/htdocs/index-prod.php" tofile="${project.packageDir}/var/www/ntk-rm-crm/htdocs/index.php" overwrite="true" /> <delete file="${project.packageDir}/var/www/ntk-rm-crm/htdocs/index-prod.php" /> <echo msg="     /usr/share/ntk-rm-crm/protected/" /> <mkdir dir="${project.packageDir}/usr/share/ntk-rm-crm/protected/" /> <copy todir="${project.packageDir}/usr/share/ntk-rm-crm/protected/" > <fileset defaultexcludes="false" expandsymboliclinks="true" dir="${project.packageDir}/export/protected/"> <include name="**/*" /> <exclude name="configs/*" /> <exclude name="**/yiic*" /> </fileset> </copy> <delete dir="${project.packageDir}/usr/share/ntk-rm-crm/protected/configs/" includeemptydirs="true" /> <echo msg="  yiic.php   " /> <copy file="${project.packageDir}/export/protected/yiic-prod.php" tofile="${project.packageDir}/usr/share/ntk-rm-crm/protected/yiic.php" overwrite="true" /> <copy file="${project.packageDir}/export/protected/yiic-prod" tofile="${project.packageDir}/usr/bin/ntk-rm-crm" overwrite="true" /> <echo msg="     /etc/ntk-rm-crm/" /> <mkdir dir="${project.packageDir}/etc/ntk-rm-crm/" /> <copy todir="${project.packageDir}/etc/ntk-rm-crm/" > <fileset defaultexcludes="false" expandsymboliclinks="true" dir="${project.packageDir}/export/protected/configs/"> <include name="**/*" /> <exclude name="**/crm.test.php" /> <exclude name="**/local.default.php" /> </fileset> </copy> <echo msg="     /usr/share/doc/ntk-rm-crm/" /> <mkdir dir="${project.packageDir}/usr/share/doc/ntk-rm-crm/" /> <copy file="${project.packageDir}/export/protected/configs/env/local.default.php" tofile="${project.packageDir}/usr/share/doc/ntk-rm-crm/local.default.php" overwrite="true" /> <echo msg=" cron- /etc/cron.d/ntk-rm-crm" /> <copy file="${project.basedir}/debian/cron.d/ntk-rm-crm" tofile="${project.packageDir}/etc/cron.d/ntk-rm-crm" overwrite="true" /> <echo msg="    /var/log/ntk-rm-crm/" /> <mkdir dir="${project.packageDir}/var/log/ntk-rm-crm/" /> <echo msg="     /var/tmp/ntk-rm-crm/" /> <mkdir dir="${project.packageDir}/var/tmp/ntk-rm-crm/" /> <echo msg="  export" /> <delete dir="${project.packageDir}/export/" includeemptydirs="true" /> </target> 



Conclusion

In conclusion, I would like to note that this solution has both advantages and disadvantages. Of the main advantages obtained after the implementation of this scheme, we can distinguish debian-way support for packages, which makes life easier for the support team. It also became easier to simultaneously develop and implement new functionality.

The article is for informational purposes only. in one article does not reveal all aspects. Ready to answer questions if they appear.

In addition, here are some references to materials about the SOA paradigm:

Source: https://habr.com/ru/post/223385/


All Articles