Application architecture development using dependency inversion

  • Tutorial


In this article, I want to talk again about the development of application architecture using Inversion of Control .
I already wrote on a habr about the IoC library and about Modular . Now I have gone even further and simplified everything that is possible and try to explain the principles of architecture. And also I will tell about the new Granula library .

Let's imagine that we want to make a library for managing users on the site. The first thing we need is a place where we will store information about
our users.
Let's describe the storage interface:

interface StorageInterface
{
    public function set($key, $value);
    public function get($key);
    public function save();
    public function load();
}

Ok, now we need an implementation of this interface. To begin with, we will store information in files. Create a class FileStorage.
Filestorage.php
class FileStorage implements StorageInterface
{
	private $file = 'data.json';
	private $data = array();	
    public function set($key, $value)
    {
    	$this->data[$key] = $value;
    }
    public function get($key) 
    {
    	return $this->data[$key];
    }
    public function save()
    {
 	file_put_contents($this->file, json_encode($this->data));
    }
    public function load()
    {
    	$this->data = json_decode(file_get_contents($this->file));
    }
}


Now create a user class

class User
{
    public function __construct(StorageInterface $storage)
    {
    }
}

Now to create an instance of the User class:

$user = new User(new FileStorage());

Well, what if some other programmer wants to use a database instead of files? To do this, he needs to create the DatabaseStorage class, implement the StorageInterface interface and replace all occurrences of FileStorage. But changing the library promises problems with its updates.
To avoid this, let's introduce the options:

$options = array(
    'StorageInterface' => 'FileStorage',
);
$user = new User($option['StorageInterface']);

Now to replace FileStorage with DatabaseStorage, you just need to specify this in the options:

$options['StorageInterface'] = 'DatabaseStorage';

What we now call options is actually an IoC container.

It is this architecture that allows you to build the most flexible applications and libraries.
In my previous article I talked about the Modular library, I continued to develop it, tried to simplify everything for the best understanding. Its main task is to train the application of IoC in practice, creating a modular architecture of the application.

Now it is called Granula .

Any library can be a module for granules. For example, you can create an MVC application from Symfony Components in the likeness of Symfony itself.

Each granule module must be described by its class:

use Granula\Module;
use Inversion\Container;
class MyModule extends Module
{
    public function build(Container $container)
    {
        // Опишите свой модуль здесь.
    }
}

For example, the description of the library that we created at the beginning of the article will be like this:

$container['StorageInterface'] = 'FileStorage';

You can even cut even more:

$container[] = 'FileStorage';

But in this case, lazy loading of classes will not work, since FileStorage will be loaded by Inversion (the library of IoC containers) immediately to determine its interfaces.

Example module description for Symfony Routing Component
        $container['request']
            = $container['Symfony\Component\HttpFoundation\Request']
            = new Factory('Symfony\Component\HttpFoundation\Request', 'createFromGlobals');
        $container['Symfony\Component\Config\FileLocator']
            = 'Symfony\Component\Config\FileLocator';
        $container['Doctrine\Common\Annotations\Reader']
            = 'Doctrine\Common\Annotations\AnnotationReader';
        $container['Symfony\Component\Routing\Loader\AnnotationClassLoader']
            = 'Granula\Router\AnnotatedRouteControllerLoader';
        $container['Symfony\Component\Config\Loader\LoaderInterface']
            = 'Symfony\Component\Routing\Loader\AnnotationDirectoryLoader';
        $container['request.context']
            = $container['Symfony\Component\Routing\RequestContext']
            = new Service('Symfony\Component\Routing\RequestContext');
        $container['router']
            = $container['Symfony\Component\Routing\RouterInterface']
            = new Factory('Granula\Router\RouterFactory');


Now you can create instances as in Symfony using the container:

$object = $container->get('Class');

Or with the help of the factory (when using trait):

$user = User::create();

Then all the necessary modules are specified in the Front Controller:

class App extends Granula\App
{
    public function register()
    {
        return array(
            new MyModule(),
            // Список модулей
        );
    }
}

And in the index.php file, they run:

$app = new App();
$app->run();


I designed all the necessary modules to create a full-fledged MVC application. To play with it, use Composer to install:

composer create-project granula/app www


It includes:
  • Symfony components
  • Twig
  • Doctrine orm


useful links




Contributors are Welcome!

Also popular now: