Spring Boot

Guia de Projecte Completa

Equivalent a Angular però per Backend

November 2025

📚 Descripció General

Spring Boot és un framework per a la creació d'aplicacions Java autònomes, de producció i basades en Spring que s'executen amb la mínima configuració.

Comparació Angular vs Spring Boot

Aspecte Angular Spring Boot
Tipus Frontend Framework Backend Framework
Estructura Modular per features Capes o features
Dependències npm packages Maven/Gradle
Configuració angular.json application.yml
Server ng serve Tomcat integrat
Build ng build mvn package

🛠️ Requisits i Instal·lació

Requisits Mínims

  • Java: JDK 17 o superior (recomanat JDK 21)
  • Maven: 3.8.1+ o Gradle: 8.0+
  • Git: Per control de versions
  • IDE: IntelliJ IDEA, Eclipse o VS Code

Crear un Projecte

# Opció 1: Spring Boot CLI
spring boot new --from web --name my-app --type maven

# Opció 2: Spring Initializr
# https://start.spring.io

# Opció 3: IDE
# New Project → Spring Boot

Dependencies Bàsiques

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
</dependency>

📁 Estructura del Projecte

Estructura per Capes

my-app/
├── src/main/java/com/example/myapp/
│   ├── MyAppApplication.java
│   ├── config/
│   ├── controller/
│   ├── service/
│   ├── repository/
│   ├── entity/
│   ├── dto/
│   ├── exception/
│   └── util/
├── src/main/resources/
├── src/test/java/
└── pom.xml

Estructura per Features

my-app/
├── src/main/java/com/example/myapp/
│   ├── MyAppApplication.java
│   ├── common/
│   │   ├── config/
│   │   └── exception/
│   ├── user/
│   │   ├── UserController.java
│   │   ├── UserService.java
│   │   └── User.java
│   ├── order/
│   │   ├── OrderController.java
│   │   └── Order.java
│   └── product/
│       └── ProductController.java

⚙️ Configuració Inicial

application.yml

spring:
  application:
    name: my-app
  
  datasource:
    url: jdbc:mysql://localhost:3306/myapp
    username: root
    password: password
  
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: false
  
  servlet:
    context-path: /api

server:
  port: 8080

Configuracions per Entorn

# application-dev.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/myapp_dev
  jpa:
    show-sql: true

# application-prod.yml
spring:
  datasource:
    url: jdbc:mysql://prod-db:3306/myapp_prod
  jpa:
    show-sql: false

💻 Desenvolupament

Classe Principal

@SpringBootApplication
public class MyAppApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyAppApplication.class, args);
    }
}

Entity (Model de Dades)

@Entity
@Table(name = "users")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, unique = true)
    private String email;
    
    @Column(nullable = false)
    private String firstName;
}

DTO (Data Transfer Object)

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserDTO {
    
    private Long id;
    
    @NotBlank(message = "Email és obligatori")
    @Email(message = "Email ha de ser vàlid")
    private String email;
    
    @NotBlank(message = "Nom és obligatori")
    private String firstName;
    
    @NotBlank(message = "Cognom és obligatori")
    private String lastName;
}

Repository

@Repository
public interface UserRepository 
    extends JpaRepository<User, Long> {
    
    Optional<User> findByEmail(String email);
    List<User> findByFirstNameContaining(String name);
}

Service

@Service
@RequiredArgsConstructor
@Transactional
@Slf4j
public class UserService {
    
    private final UserRepository userRepository;
    
    public UserDTO createUser(UserDTO userDTO) {
        log.info("Creating user: {}", userDTO.getEmail());
        User user = new User();
        user.setEmail(userDTO.getEmail());
        user.setFirstName(userDTO.getFirstName());
        
        User saved = userRepository.save(user);
        return mapToDTO(saved);
    }
}

Controller

@RestController
@RequestMapping("/api/v1/users")
@RequiredArgsConstructor
@Tag(name = "Users")
public class UserController {
    
    private final UserService userService;
    
    @PostMapping
    @Operation(summary = "Crear usuari")
    public ResponseEntity<UserDTO> createUser(
        @Valid @RequestBody UserDTO userDTO) {
        return ResponseEntity.status(HttpStatus.CREATED)
            .body(userService.createUser(userDTO));
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<UserDTO> getUser(
        @PathVariable Long id) {
        return ResponseEntity.ok(
            userService.getUserById(id));
    }
}

Configuration Class (CORS)

@Configuration
public class WebConfig implements WebMvcConfigurer {
    
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("http://localhost:4200")
            .allowedMethods("GET", "POST", "PUT", "DELETE")
            .allowedHeaders("*")
            .allowCredentials(true);
    }
}

Global Exception Handler

@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResponseEntity<ErrorResponse> 
        handleNotFound(RuntimeException ex) {
        
        ErrorResponse error = new ErrorResponse(
            HttpStatus.NOT_FOUND.value(),
            ex.getMessage()
        );
        return ResponseEntity
            .status(HttpStatus.NOT_FOUND)
            .body(error);
    }
}

🏗️ Estructura de Capes

Arquitectura en Capes

┌─────────────────────────────────┐
│   REST Controller (API)         │
├─────────────────────────────────┤
│   Service Layer                 │
│   (Lògica de negoci)            │
├─────────────────────────────────┤
│   Repository Layer              │
│   (Accés a dades)               │
├─────────────────────────────────┤
│   Entity/Model Layer            │
│   (Mappeig de BD)               │
└─────────────────────────────────┘

Responsabilitats de Cada Capa

Capa Responsabilitat
Controller Gestionar HTTP requests
Service Lògica de negoci
Repository Accés a dades
Entity Mappeig a BD

📖 API i Documentació

Swagger/OpenAPI Setup

Els endpoints están disponibles a:

http://localhost:8080/api/swagger-ui.html
http://localhost:8080/api/v3/api-docs

Dependency requerida:

<dependency>
    <groupId>org.springdoc</groupId>
    <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
    <version>2.0.2</version>
</dependency>

Documentar Endpoints

@RestController
@RequestMapping("/api/v1/users")
@Tag(name = "Users", description = "API de Gestió")
public class UserController {
    
    @PostMapping
    @Operation(
        summary = "Crear usuari",
        description = "Crea un nou usuari al sistema"
    )
    @ApiResponse(
        responseCode = "201", 
        description = "Usuari creat")
    @ApiResponse(
        responseCode = "400", 
        description = "Validació fallada")
    public ResponseEntity<UserDTO> createUser(
        @RequestBody UserDTO userDTO) {
        // ...
    }
}

🧪 Testing

Unit Test

@ExtendWith(MockitoExtension.class)
class UserServiceTest {
    
    @Mock
    private UserRepository userRepository;
    
    @InjectMocks
    private UserService userService;
    
    @Test
    void testCreateUser() {
        User testUser = new User(
            1L, "test@example.com", 
            "John", "Doe");
        
        when(userRepository.save(any(User.class)))
            .thenReturn(testUser);
        
        UserDTO result = userService
            .createUser(new UserDTO());
        
        assertNotNull(result);
        verify(userRepository, times(1))
            .save(any(User.class));
    }
}

Integration Test

@SpringBootTest
@AutoConfigureMockMvc
class UserControllerIntegrationTest {
    
    @Autowired
    private MockMvc mockMvc;
    
    @Test
    void testCreateUserEndpoint() throws Exception {
        String userJson = """
            {
                "email": "test@example.com",
                "firstName": "John",
                "lastName": "Doe"
            }
            """;
        
        mockMvc.perform(post("/api/v1/users")
            .contentType("application/json")
            .content(userJson))
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.email")
                .value("test@example.com"));
    }
}

🚀 Desplegament

Build per Producció

mvn clean package -DskipTests

# Resultat: target/my-app-1.0.0.jar

# Executar el JAR
java -jar target/my-app-1.0.0.jar

Dockerfile

FROM openjdk:17-jdk-slim

WORKDIR /app

COPY target/my-app-1.0.0.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

docker-compose.yml

version: '3.8'

services:
  app:
    build: .
    ports:
      - "8080:8080"
    environment:
      SPRING_DATASOURCE_URL: jdbc:mysql://db:3306/myapp
      SPRING_DATASOURCE_USERNAME: root
      SPRING_DATASOURCE_PASSWORD: password
    depends_on:
      - db
  
  db:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: myapp
    ports:
      - "3306:3306"
    volumes:
      - db_data:/var/lib/mysql

volumes:
  db_data:

Executar amb Docker

# Iniciar serveis
docker-compose up -d

# Veure logs
docker-compose logs -f app

# Aturar serveis
docker-compose down

✅ Millors Pràctiques

1. Estructura de Codi

  • ✅ Organitza per features o capes consistent
  • ✅ Utilitza paquet de root amb @SpringBootApplication
  • ✅ Separa controllers, services i repositories

2. Dependències

  • ✅ Usa constructors amb @RequiredArgsConstructor
  • ✅ Evita injecció en setters
  • ✅ Usa interfícies per services quan sigui possible

3. Logging

@Slf4j
public class UserService {
    
    public void processUser(Long userId) {
        log.info("Processing user: {}", userId);
        try {
            // ...
        } catch (Exception e) {
            log.error("Error: {}", userId, e);
        }
    }
}

4. Validació

  • ✅ Utilitza Bean Validation (@Valid, @NotNull)
  • ✅ Valida a nivel de DTO
  • ✅ Retorna errors apropats (400 Bad Request)

5. DTOs vs Entities

  • ✅ Usa DTOs per API responses
  • ✅ No exposis entitats directament
  • ✅ Mapeja dades correctament

6. Transaccions

@Service
@RequiredArgsConstructor
public class OrderService {
    
    @Transactional
    public OrderDTO createOrder(OrderDTO dto) {
        // Lògica de negoci
    }
    
    @Transactional(readOnly = true)
    public OrderDTO getOrder(Long id) {
        // Lectura
    }
}

7. Configuració per Entorn

  • ✅ Utilitza application-{profile}.yml
  • ✅ Externalitza secrets (variables d'entorn)
  • ✅ Utilitza @Profile si necessari

8. Documentació

  • ✅ Documenta endpoints amb Swagger
  • ✅ Manté README.md actualitzat
  • ✅ Comenta codi complex

9. Security

  • ✅ Hash passwords (BCrypt)
  • ✅ Valida input
  • ✅ Utilitza HTTPS en producció
  • ✅ Implementa JWT per autenticació

10. Performance

@Service
public class UserService {
    
    @Transactional(readOnly = true)
    public Page<UserDTO> getAllUsers(
        Pageable pageable) {
        return userRepository.findAll(pageable)
            .map(this::mapToDTO);
    }
}
  • ✅ Pagination per a grans datasets
  • ✅ Caching on apropat
  • ✅ Evita N+1 queries

11. Naming Conventions

  • PascalCase - Classes (UserController)
  • camelCase - Mètodes (getUserById)
  • SCREAMING_SNAKE_CASE - Constants
  • snake_case - BD fields (first_name)

12. Error Handling

@ControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(EntityNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ErrorResponse handleNotFound(
        EntityNotFoundException ex) {
        return new ErrorResponse(404, 
            ex.getMessage());
    }
}

📚 Recursos Addicionals

  • Spring Boot Official Docs: docs.spring.io/spring-boot
  • Spring Data JPA: spring.io/guides/gs/accessing-data-jpa
  • Swagger/OpenAPI: springdoc.org
  • Lombok: projectlombok.org
  • Maven Repository: mvnrepository.com

🤝 Contribució i Suport

Per preguntes o suggeriments, contacta l'equip de desenvolupament.

Última actualització: November 2025