public interface OrderService { Order place(PlaceOrderCommand cmd); Order get(OrderId id);}@Servicepublic class DefaultOrderService implements OrderService { ... }
Note: Skip interface if there's only one impl and no proxy/AOP need — Spring proxies work on classes too (CGLIB).
3. Implementing Service Transaction Boundaries
Rule
Detail
Boundary at use case
Service method = one transaction
No tx in controllers
Keep web layer thin
No tx in repos
Repository inherits ambient tx
Read-only flag
@Transactional(readOnly = true)
4. Implementing Service Composition
Example: Compose services
@Servicepublic class CheckoutService { private final OrderService orders; private final PaymentService payments; private final InventoryService inventory; @Transactional public CheckoutResult checkout(Cart cart) { inventory.reserve(cart); var order = orders.place(toCommand(cart)); var receipt = payments.capture(order, cart.payment()); return new CheckoutResult(order.id(), receipt.id()); }}
5. Implementing Service Dependencies
Direction
Allowed
Service → Repository
Yes
Service → Domain
Yes
Service → Service
Yes (acyclic)
Service → Controller
Never
Domain → Service
Never
6. Implementing Service Error Handling
Strategy
Detail
Throw domain exceptions
Translated by global handler
Result types
For expected outcomes
Wrap external errors
Don't leak vendor exceptions
Log with context
correlation ID, inputs (no PII)
7. Implementing Service Input Validation
Layer
Validates
Controller
Format (Bean Validation on DTO)
Service
Cross-field, cross-aggregate
Domain
Invariants in entity/value object
8. Implementing Service Output Transformation
Approach
Detail
Return domain
Map at controller boundary
Return DTO
Map inside service
Mapper component
Reusable, testable
9. Designing Service Granularity
Too coarse
Too fine
God service (everything)
One method per service
Hard to test
Wiring nightmare
Many actors
No abstraction
Note: One service per use case family (e.g., OrderService) is typically the sweet spot.
10. Understanding Service vs Repository Separation