TLDR: Как объединить модель расширенного домена с «тяжелыми» сеттерами с простым отображением форм HTML?
Проблема возникает, когда установщик одного свойства меняет другие свойства (напрямую или с помощью установщиков — это не имеет значения).
Пример класса для лучшего объяснения (в PHP, но это не имеет значения — я думаю):
class Product {
private $name;
private $cost;
private $profit;
private $price;
public function getName() {
return $this->name;
}
public function getCost() {
return $this->cost;
}
public function getProfit() {
return $this->profit;
}
public function getPrice() {
return $this->price;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function setCost($cost) {
if ($this->cost !== $cost) {
$this->cost = $cost;
$this->setPrice($this->getCost() + $this->getProfit());
}
return $this;
}
public function setProfit($profit) {
if ($this->profit !== $profit) {
$this->profit = $profit;
$this->setPrice($this->getCost() + $this->getProfit());
}
return $this;
}
public function setPrice($price) {
if ($this->price !== $price) {
$this->price = $price;
$this->setProfit($this->getPrice() - $this->getCost());
}
return $this;
}
}
Идея этого примера такова: вы можете установить стоимость (покупки / создания продукта) и прибыль, которую хотите заработать — тогда рассчитывается цена (для продажи продукта). Также вы можете установить изменение цены — тогда рассчитывается прибыль.
(Изменение стоимости может привести к изменению прибыли, а не цены, но это не имеет значения для проблемы).
Например:
$foo = new Product();
$foo->setName("Foo");
$foo->setCost(100);
$foo->setPrice(110);
assert($foo->getProfit() == 10);
$foo->setProfit(30);
assert($foo->getPrice() == 130);
Мне нужно иметь богатый объект в «бэкэнд» коде по многим причинам (например, интерфейс командной строки и задания CRON).
В веб-интерфейсе HTML вы получаете форму с 3 входами, соответствующими стоимости, прибыли и цене. Когда пользователь изменяет значения и отправляет форму, порядок установки имеет значение и может испортить ситуацию. Например:
И проблема в том, что: Бэкэнд-маппер получает значения из формы POST и вызывает: setCost (100), setProfit (20) и затем setPrice (110), что плохо, потому что это старая цена, которую следует изменить на 120 (пользователь хотел получить прибыль 20 и стоимость 100) ,
Единственное решение, которое я вижу, это реализовать все Логика модели предметной области также во внешнем интерфейсе — в JavaScript — поэтому изменяющиеся входы затрат / прибыли вызывают изменение значения при вводе цен. Тогда все входные данные имеют правильные значения и POST-данные могут быть помещены без «переопределения» проблемы. Это легко сделать с помощью этого примера, но невозможно, когда у вас много полей (много времени и ошибок при написании одного и того же кода на двух разных языках) или действительно сложная логика между ними (не все легко реализовать в JS).
Есть идеи как решить это? 🙂
PS: использование переходных свойств и ленивая оценка (т. Е. Только стоимость и цена являются собственностью и доступны для редактирования, а прибыль всегда рассчитывается) не вариант. Я должен быть в состоянии измениться с обеих сторон. Также это неудобно для некоторых тяжелых вычислений — все реквизиты должны присутствовать и затем сохраняться в базе данных.
Если вы имеете дело с унаследованным кодом, одним из вариантов будет отправка запроса в бэкэнд об изменении поля и получение обновленных полей в ответ, поэтому, в основном, во внешнем интерфейсе вы должны реализовывать только обновление формы. Но это не очень хорошее решение, я бы лучше сконцентрировался на том, что приложение действительно должно предоставить как ценность для пользователя. Если вы позволите пользователю изменить все 3 поля, почему вы должны быть параноиком? он знает, что он делает правильно? Или, если у вас есть правила проверки, оцените при отправке. Вы, наверное, уже видите, как требования могут изменить ваше поведение.
Других решений пока нет …