@@ -48,47 +48,49 @@ public class LoanService {
4848 // CRIAR EMPRÉSTIMO
4949 // ─────────────────────────────────────────────
5050
51- @ Transactional
52- public LoanResponseDTO create (LoanCreateDTO dto ) {
53-
54- User user = getAuthenticatedUser ();
55-
56- // Valida existência de todos os livros antes de iniciar
57- for (Long bookId : dto .booksId ()) {
58- bookAvailabilityPort .findById (bookId )
59- .orElseThrow (() -> new BookNotFoundException (bookId ));
60- }
51+ @ Transactional
52+ public LoanResponseDTO create (LoanCreateDTO dto ) {
53+
54+ User user = getAuthenticatedUser ();
55+
56+ // Carrega todos os livros de uma vez — elimina double fetch
57+ Map <Long , Book > books = dto .booksId ().stream ()
58+ .distinct ()
59+ .collect (Collectors .toMap (
60+ id -> id ,
61+ id -> bookAvailabilityPort .findById (id )
62+ .orElseThrow (() -> new BookNotFoundException (id ))
63+ ));
6164
6265 log .info ("Creating loan for user={} books={}" , user .getEmail (), dto .booksId ());
63-
64- Loan loan = new Loan ();
65- loan .setUser (user );
66- loan .setLoanDate (LocalDate .now ());
67- loan .setDueDate (LocalDate .now ().plusDays (7 ));
68- loan .setStatus (LoanStatus .WAITING_RETURN );
6966
70- for (Long bookId : dto .booksId ()) {
71- Book book = bookAvailabilityPort .findById (bookId )
72- .orElseThrow (() -> new BookNotFoundException (bookId ));
67+ Loan loan = new Loan ();
68+ loan .setUser (user );
69+ loan .setLoanDate (LocalDate .now ());
70+ loan .setDueDate (LocalDate .now ().plusDays (7 ));
71+ loan .setStatus (LoanStatus .WAITING_RETURN );
72+
73+ for (Long bookId : dto .booksId ()) {
74+ Book book = books .get (bookId );
7375
7476 // Update atômico — evita race condition em empréstimos concorrentes
75- int updated = bookAvailabilityPort .decrementCopies (bookId );
76- if (updated == 0 ) {
77- throw new BookNotAvailableException (bookId , book .getTitle ());
78- }
79-
80- LoanItem item = new LoanItem ();
81- item .getId ().setBookId (book .getId ());
82- item .setLoan (loan );
83- item .setBook (book );
84- item .setQuantity (1 );
85-
86- loan .getItems ().add (item );
77+ int updated = bookAvailabilityPort .decrementCopies (bookId );
78+ if (updated == 0 ) {
79+ throw new BookNotAvailableException (bookId , book .getTitle ());
80+ }
81+
82+ LoanItem item = new LoanItem ();
83+ item .getId ().setBookId (book .getId ());
84+ item .setLoan (loan );
85+ item .setBook (book );
86+ item .setQuantity (1 );
87+
88+ loan .getItems ().add (item );
8789 log .debug ("Book added to loan: bookId={} title={}" , bookId , book .getTitle ());
88- }
90+ }
8991
9092 Loan saved = loanRepository .save (loan );
91-
93+
9294 // Publica evento — outros domínios podem reagir sem acoplamento direto
9395 eventPublisher .publishEvent (new LoanCreatedEvent (
9496 saved .getId (),
@@ -98,10 +100,10 @@ public LoanResponseDTO create(LoanCreateDTO dto) {
98100
99101 log .info ("Loan created: loanId={} user={} books={}" ,
100102 saved .getId (), user .getEmail (), dto .booksId ().size ());
101-
103+
102104 // Recarrega com JOIN FETCH para garantir que o mapper acessa itens dentro da transação
103105 return mapper .toDTO (findWithItemsOrThrow (saved .getId ()));
104- }
106+ }
105107
106108 // ─────────────────────────────────────────────
107109 // DEVOLVER EMPRÉSTIMO
0 commit comments