OrderApplicationService.java

package com.example.orderplatform.orders.application;

import com.example.orderplatform.ResourceNotFoundException;
import com.example.orderplatform.customers.api.CustomerDirectory;
import com.example.orderplatform.orders.api.OrderCommand;
import com.example.orderplatform.orders.api.OrderCreatedEvent;
import com.example.orderplatform.orders.api.OrderManagement;
import com.example.orderplatform.orders.api.OrderSummary;
import com.example.orderplatform.orders.domain.OrderDraft;
import com.example.orderplatform.orders.domain.OrderLineDraft;
import com.example.orderplatform.pricing.api.PricedLine;
import com.example.orderplatform.pricing.api.PricingRequest;
import com.example.orderplatform.pricing.api.PricingService;
import java.time.OffsetDateTime;
import java.util.List;
import java.util.UUID;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * Application service that coordinates customer lookup, pricing, order persistence and order events.
 */
@Service
@Transactional
public class OrderApplicationService implements OrderManagement {

    private final CustomerDirectory customers;
    private final PricingService pricing;
    private final OrderStore orders;
    private final ApplicationEventPublisher events;

    public OrderApplicationService(
            CustomerDirectory customers,
            PricingService pricing,
            OrderStore orders,
            ApplicationEventPublisher events) {
        this.customers = customers;
        this.pricing = pricing;
        this.orders = orders;
        this.events = events;
    }

    /**
     * Places a submitted order and publishes an order-created event after persistence.
     *
     * @param command order placement command
     * @return submitted order summary
     */
    @Override
    public OrderSummary placeOrder(OrderCommand command) {
        customers.getRequiredCustomer(command.customerId());

        List<PricingRequest> pricingRequests = command.items().stream()
                .map(item -> new PricingRequest(item.productCode(), item.quantity()))
                .toList();

        List<PricedLine> pricedLines = pricing.quoteFor(pricingRequests).lines();
        OrderDraft draft = new OrderDraft(
                command.customerId(),
                pricedLines.stream()
                        .map(line -> new OrderLineDraft(line.productCode(), line.quantity(), line.unitPrice()))
                        .toList());

        OrderSummary summary = orders.save(UUID.randomUUID(), draft, OffsetDateTime.now());

        events.publishEvent(new OrderCreatedEvent(
                summary.id(),
                summary.customerId(),
                summary.total().amount(),
                summary.total().currency(),
                summary.createdAt()));

        return summary;
    }

    /**
     * Retrieves a persisted order summary.
     *
     * @param orderId order identifier
     * @return submitted order summary
     * @throws ResourceNotFoundException when the order does not exist
     */
    @Override
    @Transactional(readOnly = true)
    public OrderSummary getOrder(UUID orderId) {
        return orders.findById(orderId)
                .orElseThrow(() -> new ResourceNotFoundException("Order " + orderId + " was not found."));
    }
}