Product.java

package com.dmasone.identity.catalog.domain;

import java.math.BigDecimal;
import java.util.Objects;

/**
 * Catalog aggregate that owns stock reservation rules. The JPA entity is kept
 * outside the domain package so the rule can be tested without persistence
 * concerns and reused by application services.
 */
public final class Product {

    private final Long id;
    private final String sku;
    private final String name;
    private final BigDecimal price;
    private int availableQuantity;

    private Product(Long id, String sku, String name, BigDecimal price, int availableQuantity) {
        this.id = Objects.requireNonNull(id, "id must not be null");
        this.sku = Objects.requireNonNull(sku, "sku must not be null");
        this.name = Objects.requireNonNull(name, "name must not be null");
        this.price = Objects.requireNonNull(price, "price must not be null");
        if (availableQuantity < 0) {
            throw new IllegalArgumentException("availableQuantity must not be negative");
        }
        this.availableQuantity = availableQuantity;
    }

    public static Product restore(Long id, String sku, String name, BigDecimal price, int availableQuantity) {
        return new Product(id, sku, name, price, availableQuantity);
    }

    public void reserve(int quantity) {
        if (quantity <= 0) {
            throw new InvalidStockReservationException(quantity);
        }
        if (availableQuantity < quantity) {
            throw new InsufficientStockException(id);
        }
        availableQuantity -= quantity;
    }

    public Long id() {
        return id;
    }

    public String sku() {
        return sku;
    }

    public String name() {
        return name;
    }

    public BigDecimal price() {
        return price;
    }

    public int availableQuantity() {
        return availableQuantity;
    }
}