В чем разница между DTO vs ValueObject vs Entity на примерах? Правильное именование классов.

Redjik

Джедай-мастер
@fixxxer,
я даже этого не скрываю =)
без analogue на ларавеле по-другому и не получится с его ущербным ar
 

fixxxer

К.О.
Партнер клуба
Почему не получится?
1) Обычным способом выделить aggregate roots, создавать их фабриками
2) В репозиториях загружать aggregate roots с полным набором with(), и сохранять так же целиком
3) Извне игнорировать наличие eloquent-методов, как будто их нет
 

Вурдалак

Продвинутый новичок
Почему не получится?
1) Обычным способом выделить aggregate roots, создавать их фабриками
2) В репозиториях загружать aggregate roots с полным набором with(), и сохранять так же целиком
3) Извне игнорировать наличие eloquent-методов, как будто их нет
Если он работает в одиночку, то проще отказаться от AR. Если он работает в команде, то нужно как-то это объяснить команде. С этим уже сложнее, поскольку со стороны такие манипуляции выглядят глупо.
 

fixxxer

К.О.
Партнер клуба
Да, я держал это в голове, когда писал - судя по приведенному им коду, объяснять все равно дофига придется, уже довольно нестандартные для ларавела манипуляции.

Я это вижу в контексте постепенного рефакторинга AR-приложения в сторону DDD, при наличии такого желания и понимания смысла этого действия у команды. С нуля смысла нет, конечно. При отсутствии желания и понимания у команды смысла и в приведенных кусках кода не вижу - от этого будет впечатление типа "навыпендривался блин зачем-то на пустом месте", и все нарефакторенное быстро разрефакторят обратно :)
 
Последнее редактирование:

grigori

( ͡° ͜ʖ ͡°)
Команда форума
понабирал реальный код - выходит, DI нужен достаточно редко, в основном, когда сложную логику надо вынести по частям
например
PHP:
interface PayementGatewayAdapter{
  public function getPaymentStatus($transaction_id);
}

class YandexKassa implements PayementGatewayAdapter{
  public function getPaymentStatus($transaction_id){
  return true;
  }
}
class RobocassaAdapter implements PayementGatewayAdapter{
  public function getPaymentStatus($transaction_id){
  //curl requests here
  return $result;
  }
}

class PaymentAdapterFactory{
  /** @return PayementGatewayAdapter */
  public static function getPaymentAdapter($type){
  return new $type.'Adapter';
  }
}

class PaymentStatusChecker{
  /** @var  PayementGatewayAdapter */
  private $adapter;
  public function setPaymentAdapter(PayementGatewayAdapter $Adapter){
  $this->adapter = $Adapter;
  }
  public function check(Order $Order){
  $status = $this->adapter->getPaymentStatus($Order->getExternalTransactionId());
  if (...)
  }
}

class Order{
  protected $is_paid;
  protected $payment_method;
  protected $transaction_id;
  public function setIsPaid($is_paid){$this->is_paid = $is_paid;}
  public function getPaymentMethod(){return $this->payment_method;}
  public function getExternalTransactionId(){return $this->transaction_id;}
  public static function loadById($id){
  //SELECT FROM orders to properties
  return new self;
  }
}

class OrderProcessor{
  public function checkProcessOrder($order_id){
  $Order = Order::loadById($order_id);
  $Adapter = PaymentAdapterFactory::getPaymentAdapter($Order);
  $Checker = new PaymentStatusChecker();
  $Checker->setPaymentAdapter($Adapter);
  $Checker->check($Order);
  // delivery costs accounting, setting order to the packing queue
  }
}

class OrderProcessController{
  public function processOrderNotification(){
  $order_id = $_POST['order_id'];
  $processor = new OrderProcessor();
  $processor->checkProcessOrder($order_id);
  }
}
 

Вурдалак

Продвинутый новичок
protip #1 Не нужно тащить в нейминг понятия из GoF без необходимости. Суффиксы -Adapter, переменные $adapter читабельности не придают. Это паттерный фетиш какой-то. Тем более, категоризация на паттерны иногда очень условна.

protip #2 Фабрики в явном виде — это обычно крайний случай. Тут намного удобнее был бы сервис с интерфейсом
PHP:
interface PaymentGateway
{
    public function getPaymentStatus(PaymentMethod $method, int $transactionId) : bool;
}
модель-проверяльщик ... обновляльщик
Это признаки процедурного подхода. В ООП это выглядело бы скорее как
PHP:
class Order 
{
    public function checkAndPay(PaymentGateway $paymentGateway)
    {
        if ($paymentGateway->getPaymentStatus($this->payment_method, $this->transaction_id)) {
            $this->is_paid = true;
        }
    }    
}
 

grigori

( ͡° ͜ʖ ͡°)
Команда форума
@Вурдалак, это псевдокод, не нужно делать code review :)

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

fixxxer

К.О.
Партнер клуба
@grigori, я из прошлой дискуссии на тему так понял, что это, грубо говоря, конечный автомат с выделенным менеджером и персистенцией состояния и связанных моделей.

Я когда в последний раз биллинг делал - что-то такое и получилось. Правда, без евентов. Просто по возвращаемым значениям методов (или выбрасываемым исключениям) стейты переключал, каждый "шаг" - в транзакции.
 
Последнее редактирование:

Redjik

Джедай-мастер
о у меня тут вопрос возник, простите, что тут спрашиваю

куда воткнуть проверку? есть Restful
есть общие авторизационные проверки по маршруту, ну типа только админ или ArticleOwner, может делать Put/Post на Article
добавилось ограничение, что ArticleOwner может делать Put/Post на Article только ограниченное кол-во раз, Admin может хоть сколько
почему то хочется вот именно эту штуку вынести на уровень валидации, или все же поправить само правило проверки и кидать UnathorizedException?
 

fixxxer

К.О.
Партнер клуба
А ты это вообще технически можешь сделать до вызова article->persist() без побочек типа race condition? ;)
 

Redjik

Джедай-мастер
канеш - select for update =)

ЗЫ. авторизационные проверки происходят после того как вытаскивается entity и в общем случае в контроллере что-то типа

Код:
$this->checkPermission($currentRoute, ['model' => $model]);
ЗЫЫ. и да у нас там полноценный Rbac, когда-нибудь руки дойдут и вынесу в пакет для ларки, ибо даже покрытие 100%
 

fixxxer

К.О.
Партнер клуба
Меня уже смущают две вещи:

1)
PHP:
$this->checkPermission
2)
PHP:
['model' => $model]
;)
 

Redjik

Джедай-мастер
?? =)
1) Rbac сам приходит в конструктор, это просто shortcut
2) Проверка может быть как по роли так и по бизнес правилу поэтому передается массив вторым параметром

можешь хоть сколько плеваться, но в целом yii шный rbac написан неплохо
http://www.yiiframework.com/doc-2.0/guide-security-authorization.html

из этой доки
PHP:
class AuthorRule extends Rule
{
    public $name = 'isAuthor';

    /**
     * @param string|integer $user the user ID.
     * @param Item $item the role or permission that this rule is associated with
     * @param array $params parameters passed to ManagerInterface::checkAccess().
     * @return boolean a value indicating whether the rule permits the role or permission it is associated with.
     */
    public function execute($user, $item, $params)
    {
        return isset($params['post']) ? $params['post']->createdBy == $user : false;
    }
}
и в целом каноничненько
https://en.wikipedia.org/wiki/Role-based_access_control
 

fixxxer

К.О.
Партнер клуба
1) тут либо раздутый до неприличных размеров базовый конструктор, либо SL
2) массив и строковые идентификаторы - сама по себе жесть, но из такого вызова еще и семантика не ясна, checkPermission что?
3) Ну и глагол check подразумевает скорее return bool, а что-то мне подсказывает, что это не так
 

Redjik

Джедай-мастер
1) пусть тебя не беспокоит конструктор ;)
2) проверка - аля черный ящик, допустимо
3) глагол check тут согласен - кидает UnauthorizedException эта проверка

но вопрос то был в другом, прокидывать проверку на кол-во перезаписей Article в правила авторизации или на уровень валидации ее тащить?
 

Redjik

Джедай-мастер
кстати
Почему не получится?
1) Обычным способом выделить aggregate roots, создавать их фабриками
2) В репозиториях загружать aggregate roots с полным набором with(), и сохранять так же целиком
3) Извне игнорировать наличие eloquent-методов, как будто их нет
вот финальный вариант
PHP:
        //создается обьект формы, в котором вся модель мапится на DTO
        $form = $this->formBuilder->create($this->form, $model);

        if ($request->isMethod(Request::METHOD_POST)){
            //апдейт именно DTO
            $form->updateData($request->input());
            //валидация DTO
            if ($form->validate()){
                //сетим DTO в модель, там уже все парсится по бизнес правилам, по сути AggregateRoot со всем говном присущим AR
                $model->updateFromDto($form->getData());
                ...
            }
        }
так как ни ресурсов нет ни команда к такому не подготвлена, приходитьcя работать с ar (я там бородатых дядек учу в модели шаблоны не рендерить, так что ...)
 
Сверху