Java Springベストプラクティス入門（第2部）：詳細な実装パターン

# Java Springベストプラクティス入門（第2部）：詳細な実装パターン

## はじめに

前回の記事では、Springプロジェクトの基本的なフォルダ構成について説明しました。今回は、より詳細な実装パターンとベストプラクティスについて説明します。

## 1. レイヤー間のデータ変換

### DTOパターンの実装

```java
// DTOクラス
public class UserDTO {
    private Long id;
    private String name;
    private String email;
    // getters, setters, constructor
}

// マッパークラス
@Component
public class UserMapper {
    public UserDTO toDTO(User user) {
        return new UserDTO(
            user.getId(),
            user.getName(),
            user.getEmail()
        );
    }

    public User toEntity(UserDTO dto) {
        User user = new User();
        user.setName(dto.getName());
        user.setEmail(dto.getEmail());
        return user;
    }
}
```

## 2. 例外処理の実装

### カスタム例外の定義

```java
// カスタム例外クラス
public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

// グローバル例外ハンドラー
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(ResourceNotFoundException.class)
    public ResponseEntity<ErrorResponse> handleResourceNotFoundException(
            ResourceNotFoundException ex) {
        ErrorResponse error = new ErrorResponse(
            HttpStatus.NOT_FOUND.value(),
            ex.getMessage()
        );
        return new ResponseEntity<>(error, HttpStatus.NOT_FOUND);
    }
}
```

## 3. バリデーションの実装

### エンティティのバリデーション

```java
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @NotBlank(message = "名前は必須です")
    @Size(min = 2, max = 50, message = "名前は2文字以上50文字以下で入力してください")
    private String name;

    @Email(message = "有効なメールアドレスを入力してください")
    @NotBlank(message = "メールアドレスは必須です")
    private String email;
}
```

## 4. トランザクション管理

### サービスクラスでのトランザクション管理

```java
@Service
@Transactional
public class UserService {
    private final UserRepository userRepository;
    private final UserMapper userMapper;

    @Autowired
    public UserService(UserRepository userRepository, UserMapper userMapper) {
        this.userRepository = userRepository;
        this.userMapper = userMapper;
    }

    @Transactional(readOnly = true)
    public List<UserDTO> findAll() {
        return userRepository.findAll().stream()
            .map(userMapper::toDTO)
            .collect(Collectors.toList());
    }

    public UserDTO create(UserDTO userDTO) {
        User user = userMapper.toEntity(userDTO);
        User savedUser = userRepository.save(user);
        return userMapper.toDTO(savedUser);
    }
}
```

## 5. セキュリティの実装

### Spring Securityの設定

```java
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
                .antMatchers("/api/public/**").permitAll()
                .antMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            .and()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            .and()
            .addFilterBefore(jwtAuthenticationFilter(),
                UsernamePasswordAuthenticationFilter.class);
    }
}
```

## 6. テストの実装

### サービスクラスのテスト

```java
@SpringBootTest
class UserServiceTest {
    @Autowired
    private UserService userService;

    @MockBean
    private UserRepository userRepository;

    @Test
    void whenFindById_thenReturnUser() {
        // テストデータの準備
        User user = new User();
        user.setId(1L);
        user.setName("Test User");
        user.setEmail("test@example.com");

        // モックの設定
        when(userRepository.findById(1L))
            .thenReturn(Optional.of(user));

        // テストの実行
        UserDTO result = userService.findById(1L);

        // 検証
        assertNotNull(result);
        assertEquals("Test User", result.getName());
        assertEquals("test@example.com", result.getEmail());
    }
}
```

## まとめ

この記事では、Springプロジェクトにおける詳細な実装パターンについて説明しました。主なポイントは以下の通りです：

1. レイヤー間のデータ変換（DTOパターン）
2. 例外処理の実装
3. バリデーションの実装
4. トランザクション管理
5. セキュリティの実装
6. テストの実装

これらのパターンを適切に組み合わせることで、保守性が高く、拡張性のあるアプリケーションを構築することができます。

## 参考資料

- [Spring Boot公式ドキュメント](https://spring.io/projects/spring-boot)
- [Spring Security公式ドキュメント](https://spring.io/projects/spring-security)
- [Spring Data JPA公式ドキュメント](https://spring.io/projects/spring-data-jpa)