
Imagine a company that sells a whole range of its own products - both for external users and for internal ones. Most likely, each product will not be able to exist separately, like a spherical horse in a vacuum, and to some extent will be integrated with others. As a result, all of them together form a layer of interconnected with each other and at the same time independently developing organisms. And most likely, their development is carried out by completely different teams.
A good example of such an environment is Yandex (search, Direct, maps, mail, vertical and internal services) or Google. It is clear that the listed technology giants in each product have their own, but if you take a smaller company and operate in a narrower domain, you can assume that web products will be executed on some technologies (programming languages, frameworks, etc.).
It is about the experience in organizing the architecture of the entire product line at such a company I want to tell.
Our external web products are an
online version , an open
reference and mapping
API , a feedback
service . Internal: CRM, billing, algorithmic computing, statistics systems and
export-import data.
')
We derive two concepts:
A web infrastructure is a collection of web-products of a company that are interconnected and operate in related subject areas.
Technological base - a single code base for the company's web infrastructure. Contains a set of software blocks with high and low levels of abstraction. High-level blocks are entities already developed by the development team in the company's subject area. Low-level blocks are, for example, a set of libraries or frameworks.
Surely more than once each of you worked on several projects and implemented modules, which you then reused in the next project. This does not always go smoothly: a completely separate version of the module is formed in a separate SVN branch, and then in the third project it is necessary to apply something in between the first two versions of the module, etc., etc. In other words, if you do not list all the problems, these are the criteria for the quality of the technological base. Let's label them:
- Reuse of modules and components between projects;
- extensibility of the functionality of already written modules without rewriting the entire system;
- single product deployment scheme;
- load scalability.
Our goal was to create such a web infrastructure. The first thing we started with was the preparation of a technological base. We have chosen the following frameworks and technologies:
- PHP;
- application framework - yii;
- framework for deploying and deploying an application - phing;
- framework for writing unit tests - phpUnit;
- continuous integration system - Jenkins;
- functional and UI testing - Selenium;
- Postgres DBMS;
- key-value of storage memcached, redis;
- nginx web server.
(Why these systems are chosen, we will not discuss, this is a topic for a separate publication.)
To ensure the quality criteria of the technological base, we decided to lay out a layered architecture.
A layered architecture is a system architecture in which a system consists of some ordered set of software subsystems called layers, such that:
- on each layer, nothing is known about the properties (and even the existence) of the subsequent (higher) layers;
- each layer can interact in control (access to components) with the immediately preceding (lower) layer through a predetermined interface, without knowing anything about the internal structure of all the previous layers;
- Each layer has certain resources, which it either hides from other layers, or provides directly to the next layer (through the specified interface) some of their abstractions.
Thus, in a layered software system, each layer can implement some kind of data abstraction. The connections between the layers are limited by transferring the values ​​of the parameters for each layer's access to the layer adjacent to the bottom and by outputting the results of this treatment from the top layer to the top. It is not allowed to use global data in multiple layers. A more complete definition can be found in the
works of Fowler .
Without considering the network and servers, which themselves are separate layers, consider the device technological platform. There are three layers in it:
Fig. 1. Layers of technology platform architecture
In fig. 1 there are three layers:
- Core layer - the core layer. Here are the common low-level libraries and frameworks. For example, yii.
- Shared layer - a layer of modules that implement some kind of system functionality that can be used in several projects.
- Application layer - application layer. At this level all applications with their specific modules are located.
In this case, the important point is that the modules should be able to easily move from the application layer to the level of common modules. This is explained very simply: when a new project starts, the most necessary functionality is usually implemented. As the project develops, it begins to overgrow with connections with other products (it was decided, for example, to pull reviews from one project to cafes on another our project), and perhaps some of the modules may be required for other projects. Either the project is divided into parts, and the modules are divided into several applications.
Consider in more detail the file organization of the application in the Application layer, taking into account the specificity of yii.
application
framework - the framework is here
lib - components from core and shared. For this folder, a separate alias is created in the Yii config
components
extensions
...
protected
components
extensions
...
public
themes
config
Figure 2. Application file organization yii
As we can see, components and extensions exist both at the application level and at the general level (shared). Here is the structure of the application already collected from the repository. Of course, in the version control system you can organize everything differently. We, for example, have two assembly modes, when everything is poured into the application folder and when symlinks are made to the extension library. The first is needed to isolate different applications, when we cannot separately upgrade extensions without updating all applications, and to deploy multiple instances on one machine. Or deploying different branches on the same server. The second is convenient for developers when working with code.
So, we have set a flexible foundation for our web infrastructure, but is that enough? Not. Two important things are missing:
- application architecture must be weakly engaged and shared;
- Deploy system and product assembly.
Now for each item in more detail.
Application architecture
The application architecture is also based on layers. Highlight the following levels:
Figure 3. Layers of yii applicationThe layer of “thin” controllers contains a minimum of logic and operates with the extensions API. A business logic layer consists of an extension level and a data model level. The layer of Yii-extensions and data models have a very strong degree of connectedness, this is shown in the diagram by a bold arrow.
The greatest degree of connectivity exists between the application and the “thin” controllers. The likelihood of reuse is minimal. But the connection between the layer of "thin" controllers and the layer of business logic needs to be done as little as possible, since we can and should reuse business logic in our other applications. And we can do this with the help of the flexibility of Yii and its config.
The config connects the set of components and extensions we need. Example connection extension:
'geoip' => array ( 'class' => 'application.extensions.GeoIP.CGeoIP' ) ,
The initialized extension in the application can be accessed using the geoip key. For example:
Yii :: $ app -> geoip ;
The first time the component is accessed, the CGeoIP class will be initialized. Flexibility is in the presence of a key :-) We can change the implementation at any time through the config, and the application will still work using the geoip key.
Based on this flexibility, we can easily manage the connectivity between the application and the business logic layer.
By grouping the logic of working with a certain application essence into extensions, we make it detachable. All models are thus encapsulated in the extension and the application controller works with the data through the extension API.
Extensions are an additional level of abstraction for us, behind which we can hide the data sources necessary for the inner workings of certain extension API methods.
Let's fantasize a bit and come up with an application:
Figure 4. Example of yii application architectureOur application works with several extensions. Extensions operate on a data set and are closely related to the model layer. An important point: the degree of connectivity between layers of honey grows from top to bottom. The most closely related data model.
Now let's imagine that it became necessary to create a single user authorization service for all products. We can easily implement such a project as a separate application. To work with the new application, write the extension - rest-userServiceRestClient. The client will have the same API as the UserExtension API. We look at the picture for clarity:
rice fiveWe put UserServiceRestClient in a shared-level and change the class to be used in the application's config. Since the API is the same, the implementation has been changed without changing the code. Very flexible! All other applications also work with the user service using the UserServiceRestClient.
So, we have dealt with the architecture, but the configuration of the application with this approach will be rather big. Hands to prescribe all dependencies for the application - not at all Jedi. Especially when the project lives and there are also database migrations. All these issues can be solved with the help of its assembly system and product deployment.
Deploy and assembly system
What should deploy system:
- deliver the code to the specified directory on the server;
- pull up all dependencies of the application (extensions of the shared level and the framework);
- generate application config;
- perform sql database migration.
And also - the tasks of launching unit tests, building debug-versions of the project, etc. I don’t list everything, this is a matter of taste, and quite a lot of attention has already been paid to all these operations on the Internet. For example, our deployment system configures accompanying software such as nginx, Sphinx, RabbitMQ and creates documentation. Conveniently!
In general, the tasks are ordinary: file operations, working with version control systems, launching third-party utilities (PHPUnit, for example, or Doxygen), etc. Attention should be paid to generating a config. We made it according to a template with a meta-configuration file:
application
protected
config
main.php.template - yii config template
public
themes
build.prop - meta-configuration file
build.xml - phing'a config
rice 6During the generation of the Phing config, the template is parsed and replaces all placeholders with the corresponding parameters set in the meta-configuration file. It turned out very convenient. All dependencies of the application set the meta-config:
## List of extensions, components and etc. to install ##
EXTENSIONS = myExt, myExt2
COMPONENTS = component1, component2
HELPERS = myHeper, myTextHelper
COMMANDS =
Also in the meta-config database is registered, the paths, the hosts - in general, everything. Additional convenience for automation again is that all these parameters can be passed through the command line to Phing and redefine it. And in the future, for example, to set up the build packages for the OS used by you * nix.
Thus, the meta-configuration file is a layer of abstraction on the format and number of configs in our application.
As a result, the flexibility of the configuration Yii + build system = easily configurable and assembled products.
Total
So, what did the experience of using such a scheme for a year show us?
- If there is a need to reuse any extensions, this will not become a problem, the process of continuous integration is also built without any special problems.
- There is a necessary margin of flexibility that makes it easy to develop and increase the functionality of applications. Functional blocks can be done extremely quickly, without thinking about the performance and quality of the code.
- If the interface is thought out, you can always fearlessly finish the functionality, improve performance, change the data storage and refactor.
- A well-designed application deployment system allows you to quickly deploy a system for testing and minimizes errors when deploying to combat servers due to human inattention.
- Due to the isolation of individual functional modules, it is always possible to select a separate development group for working with it. This allows you to solve the problems of growth of the development department and not turn into a large uncontrolled collective farm, where everyone works on everything, and thus only interferes with each other.
PS And yes, we
are looking for yii developers in Novosibirsk and Kiev. Come! :-)