TLDR: How combine rich domain model with "heavy" setters with simple HTML form mapping?
Problem occurs when setter of one property change other properties (directly or by using setters - this doesn't matter).
Sample class for better explaining (in PHP, but this doesn't matter - I think):
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;
}
}
The idea is this sample is that: you can set cost (of buying/creating product) and profit you want to earn - then the price (for selling product) get calculated. Also, you can set change the price - then profit is calculated. (Changing cost could trigger to change profit not price, but it doesn't matter for the problem).
For example:
$foo = new Product();
$foo->setName("Foo");
$foo->setCost(100);
$foo->setPrice(110);
assert($foo->getProfit() == 10);
$foo->setProfit(30);
assert($foo->getPrice() == 130);
I need to have rich object in "backend" code due to many reasons (ie. command line frontend and CRON jobs).
In HTML frontend you get form with 3 inputs corresponding to cost, profit and price. When user changes values and submit form order of setters does matters and can mess things up. For example:
- user goes click to edit product button
- sees form with values: cost=100, profit=10, price=110
- changes profit to 20
- submit form
And the problem is: Backend mapper gets values from POST form and calls: setCost(100), setProfit(20) and then setPrice(110) which is bad because it is old price which should be changed to 120 (user wanted to have 20 profit and 100 cost).
Only solution i see is to implement all domain model logic also in frontend - in JavaScript - so changing cost/profit inputs trigger to change value in price input. Then all inputs have right values and are POSTed data can be put without "overriding" problem. It is easy with this sample but impossible when you have many fields (time consuming and error prone to write same code in two different languages) or really complex logic between them (not everything can be easy implemented in JS).
Any ideas how to solve this? :)
PS: Using transient properties and lazy evaluation (ie. only cost and price is property and editable, and profit is always calculated) is not an option. I must be able to change from both ways. Also it is in inconvenient for some heavy calculations - all props must be present and then saved to database.