Interfaces and Transactions in the Service

Does Every Service Need an Interface?

Older Spring code always wrote ProductService + ProductServiceImpl. Today this is optional: Mockito can mock classes and Spring can proxy classes. Add an interface when there really are several implementations (e.g. PaymentGateway with RazorpayGateway and StripeGateway).

public interface PaymentGateway {
    PaymentResult charge(String orderId, BigDecimal amount);
}

@Service
@Profile("prod")
class RazorpayGateway implements PaymentGateway { ... }

@Service
@Profile("dev")
class FakePaymentGateway implements PaymentGateway { ... }   // always succeeds, for local testing

@Transactional Belongs in the Service

A service method is one business operation, so it is the right place for a transaction: either everything in it is saved, or nothing.

@Service
public class OrderService {

    @Transactional                    // one DB transaction for the whole method
    public Order placeOrder(Long userId, List<CartItem> items) {
        items.forEach(i -> productService.reserve(i.productId(), i.quantity()));
        return orderRepository.save(Order.from(userId, items));
        // if save() throws, the stock reservations are rolled back too
    }

    @Transactional(readOnly = true)
    public List<Order> history(Long userId) { return orderRepository.findByUserId(userId); }
}

Note: Propagation, isolation and rollback rules are explained in the Hibernate & JPA material (Transactions module).