Projeto de estudo de um CRUD de produtos com foco em aprendizado e engenharia limpa sobre uma stack Java EE/Spring (sem Spring Boot) + frontend SPA Vue 3, orquestrado em Docker.
| Camada | Tecnologia |
|---|---|
| Backend | Java 8, Spring 5.3 MVC/ORM/TX, Hibernate 5.6, HikariCP 4, Bean Validation |
| Banco | MySQL 8.4 |
| Frontend | Vue 3 + TypeScript + Vite 5, Vue Router 4, Axios, SCSS modular |
| Infra | Docker Compose, NGINX 1.27 (gateway), Tomcat 9 |
Configuração 100% Java (
@Configuration) — sem Spring Boot, sem XML além doweb.xml.
Browser
└── NGINX (gateway)
├── / → frontend (NGINX + dist Vue)
└── /api/* → rewrite → Tomcat (simple-crud.war)
└── JDBC → MySQL
com.shaiyaparadise.simplecrud
├── config/ WebConfig, PersistenceConfig, DatabaseProperties, HibernateProperties
├── domain/ Product (agregado), VOs (Sku, Price, ProductName...), SkuAvailability (policy)
├── repository/ interfaces + impl Hibernate, AttributeConverters (VO ↔ SQL)
├── service/ casos de uso (@Transactional)
├── mapper/ entidade ↔ DTO
├── dto/ ProductCreateDto (@Valid), ProductResponseDto
└── web/ ProductController, HealthController, ApiExceptionHandler (@ControllerAdvice)
Domínio rico — Product não tem setters. Mudanças passam por métodos explícitos (rename, reprice, changeSku). A regra de unicidade do SKU vive no agregado, com a checagem injetada como dependência.
Value Objects — Sku, Price, ProductName etc. validam no construtor. Entidade nunca chega ao banco em estado inválido.
Configuração — DatabaseProperties e HibernateProperties lêem env via EnvReader e falham cedo se alguma variável estiver ausente. O docker-compose.yml usa ${VAR:?} pelo mesmo motivo.
Camadas finas — controller roteia, service orquestra, agregado decide, repositório persiste.
Tratamento de erros centralizado em ApiExceptionHandler:
| Código | Causa |
|---|---|
400 |
Bean Validation, JSON malformado, argumento inválido no domínio |
404 |
ResourceNotFoundException |
409 |
DuplicateSkuException, DataIntegrityViolationException |
500 |
Genérico — stacktrace apenas no log, nunca exposto ao cliente |
Base: /api/products
| Método | Rota | Resposta |
|---|---|---|
GET |
/api/products |
200 lista |
GET |
/api/products/{id} |
200 / 404 |
POST |
/api/products |
201 criado |
PUT |
/api/products/{id} |
200 atualizado |
DELETE |
/api/products/{id} |
204 |
cp .env.example .env
docker compose up --build -d- UI:
http://localhost:${NGINX_HTTP_PORT}/ - API:
http://localhost:${NGINX_HTTP_PORT}/api/products
Todas as variáveis do .env são obrigatórias — o Compose falha explicitamente se alguma faltar.