Open webdevilopers opened 5 years ago
fromArray_v2
all the way: I avoid public function __construct
in my systems, as data structures have multiple ways of being instantiated. Reusing private
logic is fine, but the ctor should really not be public
.
Thanks for the feedback! Same here regarding private and named ctors. The example was just for demonstration.
At the bottom line do you even think about other developers of your team using the fromArray_v2
method to create invalid data? It maybe sounds a little bit paranoid though. :)
$self = new self(); $self->quantity = $array['quantity']; $self->subtotal = $array['subtotal']; $self->total = $array['total']; return $self;
This bit can be in any private
method, if duplicate.
Sorry, but could you explain what you mean? I missed the point. :/
I took a snippet from the above code: you can make it a private
method if validation duplication is a concern.
But how would you init the object then for instance in an event like this?
public function customPrices(): CustomPrices
{
if (null === $this->customPrices) {
$this->customPrices = new CustomPrices(array_map(function(array $rate) {
return CustomPrice::fromArray($rate);
}, $this->payload['customPrices']));
}
return $this->customPrices;
}
I'd do that at initialisation, not when that data is requested. Or even better: keep it as value objects as much as possible as long as it's in memory
Thanks for explaining!
BTW the example was taken from the older version of @prooph event store. In the current version they skipped the serialization of Domain Event value objects:
final class Basket extends AggregateRoot
{
public static function startShoppingSession(
ShoppingSession $shoppingSession,
BasketId $basketId)
{
//Start new aggregate lifecycle by creating an "empty" instance
$self = new self();
//Record the very first domain event of the new aggregate
//Note: we don't pass the value objects directly to the event but use their
//primitive counterparts. This makes it much easier to work with the events later
//and we don't need complex serializers when storing events.
$self->recordThat(ShoppingSessionStarted::occur($basketId->toString(), [
'shopping_session' => $shoppingSession->toString()
]));
//Return the new aggregate
return $self;
}
If I'm right either way the value object stays in memory until the apply method on the AR.
When deserializing messages (e.g. Domain Events inside the same context) that hold value objects, do you still validate the given data or do you simply trust the incoming data?
Currently my objects offer a toArray() and fromArray() (see fromArray_v2) method. But the latter could theoretically be used by a developer to create "invalid state".
Especially when value objects do some custom logic e.g. price calculation it gets tricky. Maybe the calculation has changed when replaying the Domain Event. Alternatively I could go through the constructor again. But then the calculation is again executed though it may has changed.
Which version do you prefer? Or are there any additional checks or other factory method suggestions?
Original discussion started here:
P.S.: The value object shown here is stored inside the payload of a Domain Event. The payload uses primitives only. In order to use on a event handler method on the aggregate Root it has to be re-constructed from those primitives.