Definição dos Contratos da API
O desenvolvimento de um backend robusto para um sistema de gestão de biblioteca requer uma estrutura bem definida e contratos claros entre o frontend e o backend. A seguir, estão especificados os endpoints iniciais necessários para a autenticação de usuários e recupreação do catálogo de livros.
Endpoint de Autenticação
Responsável por validar as credenciais de acesso do usuário.
- Método: POST
- URL: /api/auth/signin
- Parâmetros: username (string), password (string)
- Resposta: true (credenciais válidas) ou false (credenciais inválidas)
Endpoint de Catálogo de Livros
Responsável por retornar a lista completa de itens disponíveis no acervo.
- Método: GET
- URL: /api/catalog/items
- Parâmetros: Nenhum
- Resposta: Lista de objetos JSON representando os livros.
Estrutura do Objeto de Resposta (Livro)
| Campo | Descrição |
|---|---|
| id | Identificador único do livro |
| title | Título da obra |
| writer | Nome do autor |
| stockQuantity | Quantidade disponível em estoque |
| retailPrice | Preço de capa |
| publisher | Editora responsável |
| availabilityStatus | Código de status (1 para disponível, outros para indisponível) |
| statusDescription | Descrição legível do status |
Arquitetura em Camadas
Para garantir a manutenibilidade e a separação de responsabilidades, o projeto foi estruturado utilizando o padrão de arquitetura em camadas. Os pacotes principais são:
- controller: Recebe as requisições HTTP, delega o processamento e retorna as respostas.
- service: Contém as regras de negócio e a lógica de processamento de dados.
- repository: Gernecia o acesso e a persistência dos dados (neste estágio, utilizando dados em memória).
- model: Define as entidades e objetos de transferência de dados.
Implementação das Camadas
Camada de Modelo (Model)
A entidade que representa os dados do livro no sistema.
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
public class BookItem {
private Integer id;
private String title;
private String writer;
private Integer stockQuantity;
private BigDecimal retailPrice;
private String publisher;
private Integer availabilityStatus;
private String statusDescription;
private LocalDateTime createdAt;
private LocalDateTime modifiedAt;
}
Camada de Repositório (Repository)
Responsável por gerar e fornecer os dados. Como o banco de dados ainda não foi integrado, utilizamos uma implementação simulada (Mock).
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@Repository
public class MockBookRepository {
public List<BookItem> generateMockInventory() {
List<BookItem> inventory = new ArrayList<>();
Random randomizer = new Random();
for (int index = 1; index <= 5; index++) {
BookItem item = new BookItem();
item.setId(index);
item.setTitle("Título do Livro " + index);
item.setWriter("Autor " + index);
item.setStockQuantity(index * 10);
item.setRetailPrice(BigDecimal.valueOf(randomizer.nextInt(150) + 10));
item.setPublisher("Editora " + index);
item.setAvailabilityStatus(1);
inventory.add(item);
}
return inventory;
}
}
Camada de Serviço (Service)
Orquestra a lógica de negócio, buscando os dados no repositório e formatando as informações de status para a camada de apresentação.
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BookCatalogService {
private final MockBookRepository bookRepository;
public BookCatalogService(MockBookRepository bookRepository) {
this.bookRepository = bookRepository;
}
public List<BookItem> retrieveAllBooks() {
List<BookItem> rawBooks = bookRepository.generateMockInventory();
rawBooks.forEach(book -> {
boolean isAvailable = book.getAvailabilityStatus() == 1;
book.setStatusDescription(isAvailable ? "Disponível para Empréstimo" : "Indisponível");
});
return rawBooks;
}
}
Camada de Controle (Controller)
Expõe os endpoints da API. Utilizamos injeção de dependência via construtor e retornamos ResponseEntity para um melhor controle sobre o status HTTP.
Controlador de Autenticação:
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import jakarta.servlet.http.HttpSession;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private static final String VALID_USER = "admin_user";
private static final String VALID_PASS = "secure_pass_123";
@PostMapping("/signin")
public ResponseEntity<Boolean> authenticateUser(
@RequestParam String username,
@RequestParam String password,
HttpSession httpSession) {
if (username == null || username.isBlank() || password == null || password.isBlank()) {
return ResponseEntity.ok(false);
}
if (VALID_USER.equals(username) && VALID_PASS.equals(password)) {
httpSession.setAttribute("currentUser", username);
return ResponseEntity.ok(true);
}
return ResponseEntity.ok(false);
}
}
Controlador de Catálogo:
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/catalog")
public class CatalogController {
private final BookCatalogService catalogService;
public CatalogController(BookCatalogService catalogService) {
this.catalogService = catalogService;
}
@GetMapping("/items")
public ResponseEntity<List<BookItem>> fetchAvailableBooks() {
List<BookItem> books = catalogService.retrieveAllBooks();
return ResponseEntity.ok(books);
}
}