*/ private array $lines; /** @var list */ private array $domainEvents = []; private function __construct( public readonly OrderId $id, public readonly CustomerId $customerId, private readonly \DateTimeImmutable $placedAt, ) { $this->status = OrderStatus::Draft; $this->lines = []; } /** * @param list $lines */ public static function place( OrderId $id, CustomerId $customerId, array $lines, \DateTimeImmutable $placedAt, ): self { if ($lines === []) { throw EmptyOrderException::noLines(); } $order = new self($id, $customerId, $placedAt); $order->lines = $lines; $order->status = OrderStatus::Placed; $order->recordEvent(new OrderPlaced($id, $customerId, $order->total(), $placedAt)); return $order; } public function confirm(): void { if ($this->status !== OrderStatus::Placed) { throw InvalidOrderStateException::cannotConfirm($this->status); } $this->status = OrderStatus::Confirmed; $this->recordEvent(new OrderConfirmed( $this->id, $this->customerId, $this->total(), $this->lines, )); } public function cancel(): void { if ($this->status !== OrderStatus::Placed) { throw InvalidOrderStateException::cannotCancel($this->status); } $this->status = OrderStatus::Cancelled; $this->recordEvent(new OrderCancelled($this->id)); } public function total(): Money { return array_reduce( $this->lines, static fn (Money $carry, OrderLine $line): Money => $carry->add($line->lineTotal()), Money::zero($this->lines[0]->unitPrice->currency), ); } public function status(): OrderStatus { return $this->status; } /** @return list */ public function lines(): array { return $this->lines; } public function placedAt(): \DateTimeImmutable { return $this->placedAt; } /** @return list */ public function releaseEvents(): array { $events = $this->domainEvents; $this->domainEvents = []; return $events; } private function recordEvent(object $event): void { $this->domainEvents[] = $event; } }