Архитектра MVC

MVC — это вариант Многоуровневой архитектуры (Layered Architecture), архитектурный паттерн и парадигма, которая делит приложение на 3 основных слоя.

  • Модель (Model). Этот слой управляет всеми данными, логикой и бизнес правилами не зависимо от представления данных. Это сердце и душа MVC приложения. Слово «Модель» иногда вносит путаницу. В архитектуре MVC слой модели — это не одно и то же что модель Laravel.
  • Вид (View). Предоставляет различные представления слоя модели. Предоставляет способ вызывать изменения в состоянии модели.
  • Контроллер (Controller). Организует взаимодействие между слоями. Выполняет действия над моделью для обновления ее состояния. Отправляет сообщения в слой Вид (View) для изменения конкретного представления модели.

Примеры взяты с главы книги Domain-Driven Design in PHP (By Carlos Buenosvinos, Christian Soronellas, Keyvan Akbary).

Слой модели

Слой модели представлен таким способом.

Класс Post (представляет собой пост блога):

class Post
{
    private $title;
    private $content;

    public static function writeNewFrom($title, $content)
    {
        return new static($title, $content);
    }

    private function __construct($title, $content)
    {
        $this->setTitle($title);
        $this->setContent($content);
    }

    private function setTitle($title)
    {
        if (empty($title)) {
            throw new RuntimeException('Title cannot be empty');
        }

        $this->title = $title;
    }

    private function setContent($content)
    {
        if (empty($content)) {
            throw new RuntimeException('Content cannot be empty');
        }

        $this->content = $content;
    }
}

PostRepository (представляет собой всю коллекцию доступных постов):

class PostRepository
{
    private $db;

    public function __construct()
    {
        $this->db = new PDO(
            'mysql:host=localhost;dbname=my_database',
            'a_username',
            '4_p4ssw0rd',
            [
                PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8mb4',
            ]
        );
    }

    public function add(Post $post)
    {
        $this->db->beginTransaction();

        try {
            $stm = $this->db->prepare(
                'INSERT INTO posts (title, content) VALUES (?, ?)'
            );

            $stm->execute([
                $post->title(),
                $post->content(),
            ]);

            $this->db->commit();
        } catch (Exception $e) {
            $this->db->rollback();
            throw new UnableToCreatePostException($e);
        }
    }
}

PostService известен как Сервис приложения (Application Service), — это слой внутри слоя модели, который организует и координирует другие слои Модели. Никакой другой тип объекта не должен иметь возможность напрямую обращаться к другим слоям модели.

class PostService
{
    public function createPost($title, $content)
    {
        $post = Post::writeNewFrom($title, $content);

        (new PostRepository())->add($post);

        return $post;
    }
}

Таким обзразом здесь хорошо видно, насколько слой модели больше чем просто модель сущности в Laravel (или ином php фреймворке).

Вид

Вид может посылать и принимать сообщения из слоя модели (через контроллер) и контроллера. Его основная цель представить модель пользователю в пользовательском интерфейсе (UI). А также обновлять представление в интерфейсе при каждом обновдлении модели.

Вид обычно получает объект Data Transfer Object (DTO), в котором собирается все необходимое для отображения.

Здесь используется шаблонизатор Twig

{% extends "base.html.twig" %}
{% block content %}    
    {% if errormsg is defined %}
        <div class="alert error">{{ errormsg }}</div>
    {% else %}
        <div class="alert success">Bravo! Post was created successfully!</div>
    {% endif %}
    <table>
        <thead>
            <tr>
                <th>ID</th>
                <th>TITLE</th>
                <th>ACTIONS</th>
            </tr>
        </thead>
        <tbody>
        {% for post in posts %}
            <tr>
                <td>{{ post.id }}</td>
                <td>{{ post.title }}</td>
                <td><a href="{{ editPostUrl(post.id) }}">Edit Post</a></td>
            </tr>
        {% endfor %}
        </tbody>
    </table>
{% endblock %}

Немного об использовании DTO вместо экземпляра класса представляющего модель. Для чего он нужен, если можно предоставить объект модели? Главной причиной этого служит Разделение проблем (Separation of Concerns). Если мы позволим Виду работать напрямую с Моделью, то тесно свяжем (см. тесное связывание) Вид и Модель. И изменение Модели потенциально сломает нам все Виды, которые работают с данной моделью.

Контроллер

Контроллер отвечает за координирование и организацию Модели и Вида. Он получает сообщения от вида и запускает изменения модели в соотвествии с текущим действием. И далее он оправляет сообщения Виду, чтобы тот отобразил представление модели.

class PostsController
{
    public function updateAction(Request $request)
    {
        if (
            $request->request->has('submit') &&
            Validator::validate($request->request->post)
        ) {
            $postService = new PostService();

            try {
                $postService->createPost(
                    $request->request->get('title'),
                    $request->request->get('content')
                );

                $this->addFlash(
                    'notice',
                    'Post has been created successfully!'
                );
            } catch (Exception $e) {
                $this->addFlash(
                    'error',
                    'Unable to create the post!'
                );
            }
        }

        return $this->render('posts/update-result.html.twig');
    }
}

Таким образом контроллер должен быть весьма минималистичным. Основную работу выполняет в приложении слой модели, а не контроллер.

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *