Реструктуризация кода в системе онлайн‑магазина

Перенесите reserve и replenish в Product, закройте quantity от прямой записи и проверяйте положительность количества. InventoryService оставляет координацию загрузки и сохранения.

Короткий ответ

Что ответить на собеседовании

Перенесите reserve и replenish в Product, закройте quantity от прямой записи и проверяйте положительность количества. InventoryService оставляет координацию загрузки и сохранения.

Подробный разбор

Ответ с пояснениями

Условие

В проекте онлайн‑магазина присутствуют несколько классов, описывающих товары и их хранилища. Реализованы две основные операции: резервирование товара и пополнение его количества. Требуется переписать код так, чтобы он был готов к дальнейшему расширению и лучше соблюдал принципы инкапсуляции.

namespace MyAwesomeShop;

final readonly class ProductId {}

final class NotEnoughProductInStock extends \Exception {}

interface ProductRepository
{
    public function get(ProductId $productId): Product;
    public function save(Product $product): void;
}

final class Product
{
    public int $quantity = 0;

    public function __construct(
        public readonly ProductId $productId,
        public string $title,
        public string $description = '',
    ) {}
}

final readonly class InventoryService
{
    public function __construct(
        private ProductRepository $repository,
    ) {}

    public function reserve(ProductId $itemId, int $amount): void
    {
        $product = $this->repository->get($itemId);

        if ($product->quantity < $amount) {
            throw new NotEnoughProductInStock();
        }

        $product->quantity -= $amount;

        $this->repository->save($product);
    }

    public function replenish(ProductId $itemId, int $amount): void
    {
        $product = $this->repository->get($itemId);
        $product->quantity += $amount;
        $this->repository->save($product);
    }
}

Ответ

Перенесите reserve и replenish в Product, закройте quantity от прямой записи и проверяйте положительность количества. InventoryService оставляет координацию загрузки и сохранения.

Смысл доменных методов:

public function reserve(int $amount): void {
    if ($amount <= 0) throw new \InvalidArgumentException();
    if ($this->quantity < $amount) throw new NotEnoughProductInStock();
    $this->quantity -= $amount;
}
public function replenish(int $amount): void {
    if ($amount <= 0) throw new \InvalidArgumentException();
    if ($this->quantity > PHP_INT_MAX - $amount) throw new \OverflowException();
    $this->quantity += $amount;
}

quantity становится private; начальное значение тоже валидируется. get/save должны выполняться с optimistic version check либо в транзакции с блокировкой: одна инкапсуляция не предотвращает одновременное резервирование. ProductId должен действительно хранить идентификатор.

ИИ-помощник для собеседований

Хочешь уверенно проходить собеседования?

Попробуй ИИ-помощник для собеседований: слышит вас и собеседника, анализирует экран, подсказывает ответы в реальном времени, работает без VPN и не попадает в захват экрана.

Подробнее