# Spring Boot Code Examples

Reference examples for each layer. Read the relevant section when generating code for that layer.

---

## Controller

```java
// ✅ Good
\@PostMapping
public ResponseEntity<UserResponse> createUser(
        \@Valid \@RequestBody CreateUserRequest request) {
    UserResponse response = userService.create(request);
    return ResponseEntity.status(HttpStatus.CREATED).body(response);
}

// ❌ Bad — business logic in controller
\@PostMapping
public ResponseEntity<User> createUser(\@RequestBody User user) {
    if (userRepository.existsByEmail(user.getEmail())) {
        throw new RuntimeException("Email exists");
    }
    return ResponseEntity.ok(userRepository.save(user));
}
```

---

## Service

```java
// Interface
public interface UserService {
    UserResponse create(CreateUserRequest request);
    UserResponse findById(Long id);
}

// Implementation
\@Service
\@RequiredArgsConstructor
public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;
    private final UserMapper userMapper;

    \@Override
    \@Transactional
    public UserResponse create(CreateUserRequest request) {
        if (userRepository.existsByEmail(request.getEmail())) {
            throw new DuplicateEmailException(request.getEmail());
        }
        User user = userMapper.toEntity(request);
        User saved = userRepository.save(user);
        return userMapper.toResponse(saved);
    }

    \@Override
    \@Transactional(readOnly = true)
    public UserResponse findById(Long id) {
        log.info("Finding user by id: {}", id);
        return userRepository.findById(id)
                .map(userMapper::toResponse)
                .orElseThrow(() -> {
                    log.warn("User not found: id={}", id);
                    return new ResourceNotFoundException("User", id);
                });
    }

    \@Transactional(readOnly = true)
    public Page<UserResponse> findAll(Pageable pageable) {
        return userRepository.findAll(pageable).map(userMapper::toResponse);
    }
}
```

---

## Repository

```java
// ✅ Good
public interface UserRepository extends JpaRepository<User, Long> {

    boolean existsByEmail(String email);

    Optional<User> findByEmailAndDeletedFalse(String email);

    \@Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
    Optional<User> findByIdWithRoles(\@Param("id") Long id);
}

// ❌ Bad — N+1 problem
List<Order> findAll(); // If Order has \@ManyToOne User, this causes N+1
```

---

## Entity

```java
\@Entity
\@Table(name = "users")
\@Getter
\@Setter
\@NoArgsConstructor
\@AllArgsConstructor
\@Builder
public class User extends BaseEntity {

    \@Id
    \@GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    \@Column(name = "email", nullable = false, unique = true, length = 255)
    private String email;

    \@Column(name = "full_name", nullable = false, length = 100)
    private String fullName;

    \@Column(name = "deleted", nullable = false)
    \@Builder.Default
    private boolean deleted = false;

    \@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    private List<Order> orders = new ArrayList<>();
}
```

---

## DTO

```java
// Request DTO
\@Getter
\@NoArgsConstructor
\@AllArgsConstructor
\@Builder
public class CreateUserRequest {

    \@NotBlank(message = "Email is required")
    \@Email(message = "Invalid email format")
    private String email;

    \@NotBlank(message = "Full name is required")
    \@Size(min = 2, max = 100, message = "Full name must be 2-100 characters")
    private String fullName;

    \@NotBlank(message = "Password is required")
    \@Size(min = 8, message = "Password must be at least 8 characters")
    private String password;
}

// Response DTO
\@Getter
\@NoArgsConstructor
\@AllArgsConstructor
\@Builder
public class UserResponse {
    private Long id;
    private String email;
    private String fullName;
    private LocalDateTime createdAt;
}
```

---

## MapStruct Mapper

```java
\@Mapper(componentModel = "spring")
public interface UserMapper {

    \@Mapping(target = "createdAt", source = "createdAt")
    UserResponse toResponse(User user);

    \@Mapping(target = "id", ignore = true)
    \@Mapping(target = "deleted", ignore = true)
    \@Mapping(target = "createdAt", ignore = true)
    \@Mapping(target = "updatedAt", ignore = true)
    User toEntity(CreateUserRequest request);

    List<UserResponse> toResponseList(List<User> users);
}
```

---

## Exception Handling

```java
// Custom exception
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String resource, Long id) {
        super(String.format("%s with id %d not found", resource, id));
    }
}

// Global handler
\@RestControllerAdvice
\@Slf4j
public class GlobalExceptionHandler {

    \@ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
        log.warn("Resource not found: {}", ex.getMessage());
        return ResponseEntity.status(HttpStatus.NOT_FOUND)
                .body(ErrorResponse.of("NOT_FOUND", ex.getMessage()));
    }

    \@ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<ErrorResponse> handleValidation(MethodArgumentNotValidException ex) {
        List<String> errors = ex.getBindingResult()
                .getFieldErrors()
                .stream()
                .map(e -> e.getField() + ": " + e.getDefaultMessage())
                .toList();
        return ResponseEntity.status(HttpStatus.BAD_REQUEST)
                .body(ErrorResponse.of("VALIDATION_FAILED", errors.toString()));
    }

    \@ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleGeneral(Exception ex) {
        log.error("Unexpected error", ex);
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(ErrorResponse.of("INTERNAL_ERROR", "An unexpected error occurred"));
    }
}
```

---

## Unit Tests (Service layer)

```java
\@ExtendWith(MockitoExtension.class)
class UserServiceImplTest {

    \@Mock
    private UserRepository userRepository;

    \@Mock
    private UserMapper userMapper;

    \@InjectMocks
    private UserServiceImpl userService;

    \@Test
    void create_shouldReturnUserResponse_whenEmailIsUnique() {
        CreateUserRequest request = CreateUserRequest.builder()
                .email("test\@example.com")
                .fullName("Test User")
                .build();
        User user = User.builder().id(1L).email("test\@example.com").build();
        UserResponse expected = UserResponse.builder().id(1L).build();

        given(userRepository.existsByEmail(request.getEmail())).willReturn(false);
        given(userMapper.toEntity(request)).willReturn(user);
        given(userRepository.save(user)).willReturn(user);
        given(userMapper.toResponse(user)).willReturn(expected);

        UserResponse result = userService.create(request);

        assertThat(result).isEqualTo(expected);
        verify(userRepository).save(user);
    }

    \@Test
    void create_shouldThrowDuplicateEmailException_whenEmailAlreadyExists() {
        CreateUserRequest request = CreateUserRequest.builder()
                .email("existing\@example.com").build();
        given(userRepository.existsByEmail(request.getEmail())).willReturn(true);

        assertThatThrownBy(() -> userService.create(request))
                .isInstanceOf(DuplicateEmailException.class);
        verify(userRepository, never()).save(any());
    }
}
```

---

## Integration Tests (Controller layer)

```java
\@SpringBootTest
\@AutoConfigureMockMvc
class UserControllerIntegrationTest {

    \@Autowired
    private MockMvc mockMvc;

    \@Autowired
    private ObjectMapper objectMapper;

    \@Test
    void createUser_shouldReturn201_whenRequestIsValid() throws Exception {
        CreateUserRequest request = CreateUserRequest.builder()
                .email("test\@example.com")
                .fullName("Test User")
                .password("password123")
                .build();

        mockMvc.perform(post("/api/v1/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().isCreated())
                .andExpect(jsonPath("$.email").value("test\@example.com"))
                .andExpect(jsonPath("$.id").isNumber());
    }

    \@Test
    void createUser_shouldReturn400_whenEmailIsInvalid() throws Exception {
        CreateUserRequest request = CreateUserRequest.builder()
                .email("not-an-email")
                .fullName("Test")
                .password("password123")
                .build();

        mockMvc.perform(post("/api/v1/users")
                        .contentType(MediaType.APPLICATION_JSON)
                        .content(objectMapper.writeValueAsString(request)))
                .andExpect(status().isBadRequest())
                .andExpect(jsonPath("$.code").value("VALIDATION_FAILED"));
    }
}
```
