Валидация данных

Обсуждаем, как правильно строить приложения
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Валидация данных

Сообщение sda »

Кто как считает, должна ли она быть фреймворко-зависимой или нет? У меня такое впечатление сложилось, что многие отделяют валидацию данных от тру бизнес-логики и считают, что она не так важна и может зависеть от слоя фреймворка. Тогда получается, что при миграции на другой фреймворк нужно будет написать не только новые контроллеры и репозитории, но также еще и правила валидации.

Вместе с тем я думаю, что внутри себя валидация вполне может содержать сложные бизнес-правила, но я не встречал еще такой информации в сети, чтобы её делали каким-то отдельным независимым слоем и объясняли бы какие проблемы это решает и насколько проще становится поддержка проекта или же наоборот, что это не имеет смысла и только всё усложняет.

У меня на текущий момент, валидация данных зависит от фреймворка, данные просто валидируются в контроллере, а затем закидываются в сервис. Если говорить в контексте Yii, то это выглядело бы как создание наследника от \yii\base\Model с описанием своих правил валидации и затем вызов его метода validate в контроллере, после чего в случае boolean true данные ушли бы в сервис.

В общем расскажите как у вас и как вы считаете, должно зависеть от фреймворка или не должно. Постарайтесь сильно не усложнять, чтобы было понятно всем, а если усложняете, тогда давайте ссылку на источник откуда вы это взяли, поскольку если это действительно стоящая идея, то она должна уже была быть кем-то описана и носить какое-то название. Спасибо.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Валидация данных

Сообщение ElisDN »

Несколько подходов уже обсуждали здесь. Зависит от степени недоверия к данным и от числа их источников.
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Re: Валидация данных

Сообщение sda »

Третий вариант довольно интересный, но получается, что я не могу напрямую использовать дефолтные валидаторы самого фреймворка. Скажите, можно же создать какую-то абстракцию, которая будет описывать методы для общих правил валидации типа required, isString, isEmail и т.д. и внедряться в другие как-то вот так

Код: Выделить всё

class UserSignupValidator
{
    private $userRepository;

    public function __construct(DefaultValidatorInterface $defaultValidator, UserRepositoryInterface $userRepository)
    {
        $this->defaultValidator = $defaultValidator;
        $this->userRepository = $userRepository;
    }

    public function validate(UserSignupCommand $command)
    {
        $errors = [];

        ...
        
        if ($this->defaultValidator->required($command->username)) {
            $errors['username'][] = 'This field is required.';
        }
        if ($this->userRepository->existsByUsername($command->username)) {
            $errors['username'][] = 'This username has already been taken.';
        }

        if ($this->userRepository->existsByEmail($command->email)) {
            $errors['email'][] = 'This email has already been taken.';
        }

        return $errors;
    }
} 
Внутри конкретной реализации DefaultValidatorInterface можно уже использовать дефолтные валидаторы самого фреймворка. Тогда вроде как получается, что при переезде на другой фреймворк нужно будет только написать новую реализацию DefaultValidatorInterface, что должно быть довольно таки простым занятием даже не включая мозг. А сложные/не сложные индивидуальные правила валидации останутся нетронутыми в UserSignupValidator и у нас не будет необходимости их переписывать каждый раз при переезде на новый фреймворк или на новую мажорную версию этого же фреймворка, как при втором варианте, который вы описали.

Кароче абстрагироваться от конкретных реализаций дефолтных валидаторов, так же как мы абстрагировались от конктерного хранилища с помощью репозиториев.

Что скажете Дмитрий?
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Валидация данных

Сообщение ElisDN »

sda писал(а):Скажите, можно же создать какую-то абстракцию, которая будет описывать методы для общих правил валидации типа required, isString, isEmail и т.д...
Да, как раз можно.
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Re: Валидация данных

Сообщение sda »

Дмитрий, у меня еще вопрос по автозагрузке. Сейчас когда мы пишем неймспейс типа \app\path\to\my\Class то класс услужливо подхватывается встроенным автозагрузчиком Yii вот здесь https://github.com/yiisoft/yii2/blob/ma ... i.php#L279 и превращается в алиас вида @app/path/to/my/Class.php
А сам алиас @app является предопределенным http://www.yiiframework.com/doc-2.0/gui ... ed-aliases и соответственно разворачивается в basePath. Затем инклюдится файл по получившемуся пути. Это всё понятно.

Но когда мы уйдем на другой фреймворк, то там никаких предопределенных алиасов не будет, но получается так, что мы уже завязали весь свой код на неймспейс \app, который нам потихому подсунул Yii фреймворк. Конечно мы сможем потом в другом фреймворке в autoload прописать этот неймспейс \app и вроде как всё должно заработать, но не считаете ли вы, что логичнее было бы сразу приложению прописать тот неймспейс, который мы сами захотим в composer.json и загружать наш код автозагрузчиком композера, а не встроенным автозагрузчиком Yii. Например вот так:

Код: Выделить всё

//composer.json
...

"autoload": {
    "psr-4": {
       "AppName\\": "src/"
    }
}

...
То есть если отвязываться от фреймворка, то отвязываться полностью. Как вы считаете?
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Валидация данных

Сообщение zelenin »

sda писал(а):Третий вариант довольно интересный, но получается, что я не могу напрямую использовать дефолтные валидаторы самого фреймворка. Скажите, можно же создать какую-то абстракцию, которая будет описывать методы для общих правил валидации типа required, isString, isEmail и т.д. и внедряться в другие как-то вот так

Код: Выделить всё

class UserSignupValidator
{
    private $userRepository;

    public function __construct(DefaultValidatorInterface $defaultValidator, UserRepositoryInterface $userRepository)
    {
        $this->defaultValidator = $defaultValidator;
        $this->userRepository = $userRepository;
    }

    public function validate(UserSignupCommand $command)
    {
        $errors = [];

        ...
        
        if ($this->defaultValidator->required($command->username)) {
            $errors['username'][] = 'This field is required.';
        }
        if ($this->userRepository->existsByUsername($command->username)) {
            $errors['username'][] = 'This username has already been taken.';
        }

        if ($this->userRepository->existsByEmail($command->email)) {
            $errors['email'][] = 'This email has already been taken.';
        }

        return $errors;
    }
} 
Внутри конкретной реализации DefaultValidatorInterface можно уже использовать дефолтные валидаторы самого фреймворка. Тогда вроде как получается, что при переезде на другой фреймворк нужно будет только написать новую реализацию DefaultValidatorInterface, что должно быть довольно таки простым занятием даже не включая мозг. А сложные/не сложные индивидуальные правила валидации останутся нетронутыми в UserSignupValidator и у нас не будет необходимости их переписывать каждый раз при переезде на новый фреймворк или на новую мажорную версию этого же фреймворка, как при втором варианте, который вы описали.

Кароче абстрагироваться от конкретных реализаций дефолтных валидаторов, так же как мы абстрагировались от конктерного хранилища с помощью репозиториев.

Что скажете Дмитрий?
ну зачем завязываться в такой мелочи на фреймворк? Напишите пяток валидаторов типа StringValidator, NumberValidator, и юзайте их в UserDtoValidator.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Валидация данных

Сообщение zelenin »

sda писал(а):Дмитрий, у меня еще вопрос по автозагрузке. Сейчас когда мы пишем неймспейс типа \app\path\to\my\Class то класс услужливо подхватывается встроенным автозагрузчиком Yii вот здесь https://github.com/yiisoft/yii2/blob/ma ... i.php#L279 и превращается в алиас вида @app/path/to/my/Class.php
А сам алиас @app является предопределенным http://www.yiiframework.com/doc-2.0/gui ... ed-aliases и соответственно разворачивается в basePath. Затем инклюдится файл по получившемуся пути. Это всё понятно.

Но когда мы уйдем на другой фреймворк, то там никаких предопределенных алиасов не будет, но получается так, что мы уже завязали весь свой код на неймспейс \app, который нам потихому подсунул Yii фреймворк. Конечно мы сможем потом в другом фреймворке в autoload прописать этот неймспейс \app и вроде как всё должно заработать, но не считаете ли вы, что логичнее было бы сразу приложению прописать тот неймспейс, который мы сами захотим в composer.json и загружать наш код автозагрузчиком композера, а не встроенным автозагрузчиком Yii. Например вот так:

Код: Выделить всё

//composer.json
...

"autoload": {
    "psr-4": {
       "AppName\\": "src/"
    }
}

...
То есть если отвязываться от фреймворка, то отвязываться полностью. Как вы считаете?
так и надо
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Re: Валидация данных

Сообщение sda »

Выходит нам нужно просто обложиться абстракциями со всех флангов, с которых есть угроза проникновения врага в наш девственно чистый код и проблемы фреймворкозависимости решены? Ну кроме проблемы самой абстракции.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Валидация данных

Сообщение ElisDN »

sda писал(а):Выходит нам нужно просто обложиться абстракциями со всех флангов, с которых есть угроза проникновения врага в наш девственно чистый код и проблемы фреймворкозависимости решены?
Да, как и обсуждали в соседней теме: пользуясь принципами ООП написать чистое ядро, принимающее чётко определённые команды и запросы и взаимодействующее с инфраструктурой через адаптеры.

Получаем быстрые тесты, стопроцентную независимость от чего угодно и переносимость на любой фреймворк, CMS и прочую инфраструктуру. Заодно получаем возможность разработки всей системы ещё до выбора фреймворка и БД. И, кстати, если PHP затормозит, получаем портируемость на любой ООП-язык простой правкой синтаксиса.

Вот мы и изобрели заново ту самую архитектуру.
sda писал(а):Ну кроме проблемы самой абстракции.
Какой именно проблемы?
nootropil
Сообщения: 46
Зарегистрирован: 2015.11.21, 18:45

Re: Валидация данных

Сообщение nootropil »

ElisDN писал(а):
sda писал(а):Ну кроме проблемы самой абстракции.
Какой именно проблемы?
"Любую проблему можно решить введением дополнительного уровня абстракции … кроме проблемы слишком большого количества уровней абстракции." :D
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Re: Валидация данных

Сообщение sda »

ElisDN писал(а):
sda писал(а):Ну кроме проблемы самой абстракции.
Какой именно проблемы?
Ну есть такая поговорка или высказывание, я о ней давно статью находил в англ вики, но уже забыл. Звучит примерно так "Любую проблему можно решить дополнительным уровнем абстрации, кроме проблемы слишком большого количества абстракций".
SamDark частенько её на этом форуме упоминал.

Мне еще вот, что интересно. Если приложение это api, то всё понятно, а вот если это веб-сайт, то еще интересны правила, которыми стоит руководствоваться при разработке html шаблонов, чтобы они тоже были независимы и их можно было бы взять и унести в другой фреймворк. Понятно, что не стоит использовать всякого рода виджеты. Но еще есть csrf токен например и может что-то еще, что также нужно как-то решить, чтобы не привязывать html шаблоны к фреймворку. Но я помню, что Yii довольно плотно участвует в жизни html шаблонов, всякие там ассеты и прочее.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Валидация данных

Сообщение ElisDN »

sda писал(а):Ну есть такая поговорка или высказывание, я о ней давно статью находил в англ вики, но уже забыл. Звучит примерно так "Любую проблему можно решить дополнительным уровнем абстрации...".
Как недавно говорил, архитектуру придумали для упрощения сложного кода, а не для усложнения лёгкого.
sda писал(а):"...кроме проблемы слишком большого количества абстракций".
А слишком большое количество - это сколько?
sda писал(а):SamDark частенько её на этом форуме упоминал.
У него своя альтернативная позиция по отношению к ООП и архитектуре.
sda писал(а):а вот если это веб-сайт, то еще интересны правила, которыми стоит руководствоваться при разработке html шаблонов, чтобы они тоже были независимы и их можно было бы взять и унести в другой фреймворк... Понятно, что не стоит использовать всякого рода виджеты.
HTML - он и в Африке HTML. В чём проблема перенести и немного переписать вёрстку? Или сложно будет потом вместо GridView написать <table> с foreach? А так вполне универсален Twig.
sda писал(а):Но еще есть csrf токен например и может что-то еще, что также нужно как-то решить, чтобы не привязывать html шаблоны к фреймворку.
У каждого фреймворка есть свои конструкторы форм с CSRF и прочими заморочками. Проблем с ними нет.
sda писал(а):Но я помню, что Yii довольно плотно участвует в жизни html шаблонов, всякие там ассеты и прочее.
Не мелочитесь шаблонами и ассетами. Ассеты - это просто пару js-файлов подключить. Дел на один день.

Или Вы думаете, что когда пять человек в команде полгода программируют сервис бронирования билетов или ERP для местного бизнеса их сильно волнует вёрстка? Так что программируйте чистым изначально только ядро.
Последний раз редактировалось ElisDN 2016.08.06, 20:06, всего редактировалось 10 раз.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Валидация данных

Сообщение zelenin »

в ddd есть 4 слоя - один из них это слой представления (контроллеры плюс вьюхи). Он единственный фреймворкозависимый, хотя в случае продуманного выбора и он может быть переносимым. Например в psr7-совместимых фреймворках твой экшн будет являться стандартизированной мидлварью с зависимостью от рендерера темплейтов

Код: Выделить всё

<?php

namespace Zelenin\Sport\Presentation\Action\Federation;

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zelenin\MessageBus\Contract\Bus;
use Zelenin\Sport\Application\Dto\Federation\FederationDto;
use Zend\Expressive\Template\TemplateRendererInterface;

final class Create
{
    /**
     * @var TemplateRendererInterface
     */
    private $template;

    /**
     * @var Bus
     */
    private $commandBus;

    /**
     * @param TemplateRendererInterface $template
     * @param Bus $commandBus
     */
    public function __construct(TemplateRendererInterface $template, Bus $commandBus)
    {
        $this->template = $template;
        $this->commandBus = $commandBus;
    }

    /**
     * @param ServerRequestInterface $request
     * @param ResponseInterface $response
     * @param callable|null $next
     *
     * @return ResponseInterface
     */
    public function __invoke(
        ServerRequestInterface $request,
        ResponseInterface $response,
        callable $next = null
    ): ResponseInterface
    {
        $federationDto = new FederationDto();

        $response->getBody()->write($this->template->render('@sport/federation/create', [
            'federationDto' => $federationDto
        ]));

        return $response
            ->withHeader('Content-Type', 'text/html; charset=utf-8');
    }
}
в данном случае рендерер от зенд экспрессив, и реализован в виде 3 адаптеров над сторонними системами рендеринга. Я уверен что менять фреймворк не буду, т.к. он мне предоставляет самый минимум - di + мидлвари - ничего из этого я 100% не захочу заменить, т.к. это простО как кирпич. (если бы закладывал возможность переноса, то обернул бы TemplateRendererInterface зенда в свой TemplateRendererInterface)
Экшны переносимы в любой другой psr7-фреймворк без правок, шаблоны у меня на твиге - также переносимы (без всяких ассетсов и прочих улучшений фреймворка).

В случае с yii ты ничего не перенесешь, т.к. контроллеры - свои, а не стандартизированные, шаблон - с кучей хаков от фреймворка, итд.

Вывод: для слоистой архитектуры выбрать psr7-микро-фреймворк, чтобы последний презентационный слой по максимуму развязать, и не тащить с собой 90% ненужного кода.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Валидация данных

Сообщение ElisDN »

nootropil писал(а):Любую проблему можно решить введением дополнительного уровня абстракции… кроме проблемы слишком большого количества уровней абстракции.
Ну, например, перепишу я всеми любимый классический код без абстракций:

Код: Выделить всё

if ($order->status == 1 || ($order->status == 2 && $order->deliveryDate > time() + 86400) || ($order->status == 4 && $order->paymentDate < time())) {
   ...
}
в свой "сильно сложный" ООП-шный с абстракцией:

Код: Выделить всё

if ($order->isCancelable()) {
    ...
}
Слишком много абстракции в проекте станет?
nootropil
Сообщения: 46
Зарегистрирован: 2015.11.21, 18:45

Re: Валидация данных

Сообщение nootropil »

ElisDN писал(а): Ну, например, перепишу я всеми любимый классический код без абстракций:
Тут лишь первый уровень абстракции, надо больше что бы стало плохо. :D Нужна обёртка для обёртки обёртки обёртки ... и т.д.
Да и цитата, как мне кажется, скорее о кровавом ентерпрайзе на Java.
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Валидация данных

Сообщение ElisDN »

nootropil писал(а):Тут лишь первый уровень абстракции, надо больше что бы стало плохо. :D Нужна обёртка для обёртки обёртки обёртки ... и т.д
Да, такое у новичков часто бывает. Скиньте пример в личку, если встретите.
zelenin
Сообщения: 10596
Зарегистрирован: 2013.04.20, 11:30

Re: Валидация данных

Сообщение zelenin »

nootropil писал(а):
ElisDN писал(а): Ну, например, перепишу я всеми любимый классический код без абстракций:
Тут лишь первый уровень абстракции, надо больше что бы стало плохо. :D Нужна обёртка для обёртки обёртки обёртки ... и т.д.
Да и цитата, как мне кажется, скорее о кровавом ентерпрайзе на Java.
это цитата для тех, кто не понимает зачем в проект вводятся абстракции.
Аватара пользователя
samdark
Администратор
Сообщения: 9489
Зарегистрирован: 2009.04.02, 13:46
Откуда: Воронеж
Контактная информация:

Re: Валидация данных

Сообщение samdark »

И для тех, кто понимает зачем, но перебарщивает. Дмитрий выше верно заметил, что "архитектуру придумали для упрощения сложного кода, а не для усложнения лёгкого".
sda
Сообщения: 334
Зарегистрирован: 2013.12.19, 09:29

Re: Валидация данных

Сообщение sda »

ElisDN имеет ли смысл делать двойную валидацию, первую на уровне доменных объектов, когда домен кидает исключения если пришли плохие данные, а вторую уже исключительно для пользователей (для отображения ошибок на сайте) с помощью инструментов которые предлагает фреймворк?
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Валидация данных

Сообщение ElisDN »

sda писал(а):ElisDN имеет ли смысл делать двойную валидацию, первую на уровне доменных объектов, когда домен кидает исключения если пришли плохие данные, а вторую уже исключительно для пользователей (для отображения ошибок на сайте) с помощью инструментов которые предлагает фреймворк?
Если хотите красивую Ajax-валидацию, то придётся клиентскую делать. А так я в своём ответе несколько вариантов рассматривал.
Ответить