Aula 16: Integração Final — CritiqueHub Completo
Objetivos
- Integrar todas as camadas do CritiqueHub em uma aplicação funcional end-to-end
- Revisar os padrões arquiteturais aplicados ao longo do curso
- Validar a aplicação contra um checklist de qualidade profissional
- Refatorar pontos de melhoria identificados durante o desenvolvimento
- Consolidar o aprendizado com uma visão holística do sistema
Contexto e Motivação
Ao longo de 15 aulas, construímos o CritiqueHub peça por peça: entidades JPA, repositórios, serviços CDI, endpoints REST, interfaces Jakarta Faces, testes automatizados, segurança e containerização. Agora é hora de enxergar o todo, conectar as pontas e garantir que a aplicação funcione como um sistema coeso.
Esta aula não introduz tecnologias novas — ela consolida, integra e refina. O foco está em arquitetura, qualidade e visão sistêmica, habilidades que diferenciam desenvolvedores juniores de profissionais experientes.
Conteúdo
Arquitetura Final do CritiqueHub
Visão em Camadas
O CritiqueHub segue uma arquitetura em camadas (Layered Architecture), onde cada camada tem uma responsabilidade clara e se comunica apenas com a camada imediatamente abaixo:
Mapa de Dependências
Cada componente do CritiqueHub participa de um fluxo claro. Por exemplo, o fluxo completo de "criar uma avaliação":
Inventário de Componentes
Entidades JPA
| Entidade | Aula | Relacionamentos | Papel |
|---|---|---|---|
Filme | 8, 9 | 1:N Avaliação, N:N Gênero, N:1 Diretor | Item cultural principal |
Avaliacao | 8, 9 | N:1 Filme, N:1 Usuário | Core do domínio |
Genero | 9 | N:N Filme | Classificação |
Diretor | 9 | 1:N Filme | Metadado de filme |
Usuario | 14 | 1:N Avaliação, roles | Identidade e segurança |
Serviços CDI
| Serviço | Responsabilidade | Injeções |
|---|---|---|
FilmeService | CRUD de filmes, busca, ranking | FilmeRepository, MeterRegistry |
AvaliacaoService | CRUD de avaliações, validação de negócio | AvaliacaoRepository, FilmeRepository |
UsuarioService | Cadastro, perfil | EntityManager |
Endpoints REST
| Método | Path | Auth | Descrição |
|---|---|---|---|
GET | /filmes | Público | Listar filmes paginados |
GET | /filmes/{id} | Público | Detalhe de um filme |
GET | /filmes/busca | Público | Busca avançada com filtros |
GET | /filmes/ranking | Público | Ranking por nota |
POST | /filmes | ADMIN | Criar filme |
PUT | /filmes/{id} | ADMIN | Atualizar filme |
DELETE | /filmes/{id} | ADMIN | Remover filme |
POST | /avaliacoes | USER | Criar avaliação |
GET | /avaliacoes/filme/{id} | Público | Avaliações de um filme |
GET | /perfil | Autenticado | Perfil do usuário logado |
Páginas Jakarta Faces
| Página | Escopo do Bean | Funcionalidade |
|---|---|---|
index.xhtml | — | Página inicial |
filmes.xhtml | @ViewScoped | Lista de filmes com DataTable |
filme-detalhe.xhtml | @ViewScoped | Detalhe + avaliações |
filme-form.xhtml | @ViewScoped | Formulário de cadastro/edição |
login.xhtml | — | Autenticação |
perfil.xhtml | @RequestScoped | Perfil do usuário |
Revisão de Padrões e Boas Práticas
Padrões Aplicados
| Padrão | Onde Aplicamos | Benefício |
|---|---|---|
| Repository | FilmeRepository, AvaliacaoRepository | Abstrai acesso a dados |
| Service Layer | FilmeService, AvaliacaoService | Centraliza lógica de negócio |
| DTO | FilmeDTO, AvaliacaoDTO, PaginaDTO | Desacopla representação de domínio |
| Dependency Injection | CDI em todas as camadas | Baixo acoplamento, testabilidade |
| Interceptor | @Auditavel , @Transactional | Cross-cutting concerns |
| Observer | Eventos CDI | Desacoplamento de notificações |
Checklist de Qualidade
Use esta lista para validar o CritiqueHub antes de considerar o projeto completo:
Arquitetura e Código:
- Cada classe tem uma responsabilidade única (SRP)
- Services não acessam
EntityManagerdiretamente — usam Repositories - DTOs são usados para transferência entre camadas (nunca expõe entidades em endpoints)
- Nenhuma lógica de negócio nos Managed Beans ou Resources
- Injeção de dependência via CDI (sem
newpara serviços)
Persistência:
- Entidades mapeadas com relacionamentos bidirecionais quando necessário
- Métodos auxiliares
addAvaliacao()/removeAvaliacao()para consistência - FetchType.LAZY como padrão, EAGER apenas quando justificado
- Fetch Join ou Entity Graph para evitar N+1 em listagens
- Paginação implementada em todas as listagens
Segurança:
- Senhas armazenadas com BCrypt (nunca texto plano)
- Endpoints de escrita protegidos com
@RolesAllowed - Endpoints de leitura pública anotados com
@PermitAll - Proteção CSRF ativa em formulários
- Sem concatenação de strings em queries (prevenção SQL Injection)
Testes:
- Testes unitários para Services com Mockito
- Testes de integração com
@QuarkusTestpara endpoints - Testes de segurança com
@TestSecurity - Dados de teste isolados (não dependem de estado externo)
Observabilidade e Deploy:
- Health checks (liveness + readiness) implementados
- Métricas de negócio registradas (Micrometer)
- Perfis configurados (dev, test, prod)
- Dockerfile multi-stage funcional
- Docker Compose com banco de dados
Refatoração Guiada
Nesta seção, aplicamos melhorias comuns que surgem ao revisitar o código com uma visão sistêmica.
1. Extrair Interface para Services
Facilita testes e permite múltiplas implementações:
// Interface
public interface FilmeService {
PaginaDTO<FilmeDTO> listar(int pagina, int tamanho);
FilmeDTO buscarPorId(Long id);
FilmeDTO criar(FilmeDTO dto);
void deletar(Long id);
}
// Implementação
@ApplicationScoped
public class FilmeServiceImpl implements FilmeService {
@Inject
FilmeRepository filmeRepository;
@Override
@Timed("critiquehub.filmes.listagem")
public PaginaDTO<FilmeDTO> listar(int pagina, int tamanho) {
PanacheQuery<Filme> query = filmeRepository.findAll(Sort.by("titulo"));
query.page(Page.of(pagina, tamanho));
List<FilmeDTO> dtos = query.list().stream()
.map(this::toDTO)
.toList();
return new PaginaDTO<>(dtos, pagina, tamanho, query.count(), query.pageCount());
}
private FilmeDTO toDTO(Filme filme) {
return new FilmeDTO(filme.id, filme.titulo, filme.anoLancamento, filme.sinopse);
}
}
2. Global Exception Handler
Centraliza tratamento de erros em endpoints REST:
@Provider
public class GlobalExceptionMapper implements ExceptionMapper<Exception> {
@Override
public Response toResponse(Exception exception) {
if (exception instanceof ConstraintViolationException cve) {
Map<String, String> erros = cve.getConstraintViolations().stream()
.collect(Collectors.toMap(
v -> v.getPropertyPath().toString(),
ConstraintViolation::getMessage
));
return Response.status(Status.BAD_REQUEST)
.entity(Map.of("erros", erros))
.build();
}
if (exception instanceof NotFoundException) {
return Response.status(Status.NOT_FOUND)
.entity(Map.of("mensagem", exception.getMessage()))
.build();
}
Log.errorf(exception, "Erro inesperado: %s", exception.getMessage());
return Response.status(Status.INTERNAL_SERVER_ERROR)
.entity(Map.of("mensagem", "Erro interno do servidor"))
.build();
}
}
3. Mapper Centralizado
Evita duplicação de código de conversão entidade/DTO:
@ApplicationScoped
public class FilmeMapper {
public FilmeDTO toDTO(Filme filme) {
return new FilmeDTO(
filme.id,
filme.titulo,
filme.anoLancamento,
filme.sinopse,
filme.generos.stream().map(g -> g.nome).toList()
);
}
public Filme toEntity(FilmeDTO dto) {
Filme filme = new Filme();
filme.titulo = dto.titulo();
filme.anoLancamento = dto.anoLancamento();
filme.sinopse = dto.sinopse();
return filme;
}
public void atualizarEntity(Filme filme, FilmeDTO dto) {
filme.titulo = dto.titulo();
filme.anoLancamento = dto.anoLancamento();
filme.sinopse = dto.sinopse();
}
}
Teste End-to-End
Um teste de integração final que valida o fluxo completo:
@QuarkusTest
@TestMethodOrder(OrderAnnotation.class)
class CritiqueHubIntegrationTest {
static Long filmeId;
@Test
@Order(1)
@TestSecurity(user = "admin@critiquehub.com", roles = "ADMIN")
void deveCriarFilme() {
filmeId = given()
.contentType(ContentType.JSON)
.body("""
{
"titulo": "Interestelar",
"anoLancamento": 2014,
"sinopse": "Exploração espacial para salvar a humanidade"
}
""")
.when()
.post("/filmes")
.then()
.statusCode(201)
.body("titulo", is("Interestelar"))
.extract().jsonPath().getLong("id");
}
@Test
@Order(2)
void deveListarFilmesPublicamente() {
given()
.when()
.get("/filmes")
.then()
.statusCode(200)
.body("$.size()", greaterThan(0));
}
@Test
@Order(3)
@TestSecurity(user = "user@critiquehub.com", roles = "USER")
void deveCriarAvaliacao() {
given()
.contentType(ContentType.JSON)
.body("""
{
"filmeId": %d,
"nota": 5,
"comentario": "Obra-prima de Christopher Nolan"
}
""".formatted(filmeId))
.when()
.post("/avaliacoes")
.then()
.statusCode(201);
}
@Test
@Order(4)
void deveVerAvaliacoesDoFilme() {
given()
.when()
.get("/avaliacoes/filme/" + filmeId)
.then()
.statusCode(200)
.body("$.size()", is(1))
.body("[0].nota", is(5));
}
@Test
@Order(5)
@TestSecurity(user = "user@critiquehub.com", roles = "USER")
void usuarioNaoDeveDeletarFilme() {
given()
.when()
.delete("/filmes/" + filmeId)
.then()
.statusCode(403);
}
@Test
@Order(6)
void healthCheckDeveEstarUp() {
given()
.when()
.get("/q/health")
.then()
.statusCode(200)
.body("status", is("UP"));
}
}
Retrospectiva do Curso
Mapa de Especificações Jakarta EE Cobertas
| Especificação | Versão | Aulas | Status |
|---|---|---|---|
| Jakarta CDI | 4.0 | 4, 5 | Fundamentos + Avançado |
| Jakarta REST (JAX-RS) | 3.1 | 3 | Completo |
| Jakarta Persistence (JPA) | 3.1 | 8, 9, 10 | Completo |
| Jakarta Faces (JSF) | 4.0 | 11, 12, 13 | Completo |
| Jakarta Bean Validation | 3.0 | 5 | Fundamentos |
| Jakarta Security | 3.0 | 14 | Fundamentos |
| Jakarta Interceptors | 2.1 | 5 | Fundamentos |
Habilidades Desenvolvidas
Próximos Passos
O curso cobriu os fundamentos sólidos do desenvolvimento Jakarta EE com Quarkus. Para continuar evoluindo:
| Tema | Tecnologia | Recurso |
|---|---|---|
| Mensageria | Jakarta Messaging + SmallRye Reactive | Quarkus Messaging Guide |
| Cache Distribuído | Infinispan / Redis | Quarkus Cache Guide |
| GraphQL | SmallRye GraphQL | Quarkus GraphQL Guide |
| gRPC | Quarkus gRPC | Quarkus gRPC Guide |
| Kubernetes | Quarkus Kubernetes | Quarkus Kubernetes Guide |
| Compilação Nativa | GraalVM + Mandrel | Quarkus Native Guide |
Exercícios Finais
- Complete o CritiqueHub com todas as entidades do domínio (
Diretor,Ator,Lista,PerfilDetalhado) e seus relacionamentos - Implemente um endpoint
/filmes/{id}/completoque retorna filme com diretor, gêneros, avaliações e média de notas — otimizado com Entity Graph - Crie um fluxo completo Jakarta Faces: login → lista de filmes → detalhe → avaliar → ver avaliação publicada
- Execute todos os testes (unitários + integração + segurança) e garanta 100% de sucesso
- Containerize a aplicação final e valide com
docker compose upque todos os endpoints, health checks e métricas funcionam - Documente os endpoints da API REST usando OpenAPI/Swagger (adicione
quarkus-smallrye-openapi) e valide em/q/swagger-ui