Java および Spring Boot コーディング規約

## 目次
1. [一般原則](#一般原則)
2. [命名規則](#命名規則)
3. [コード構造とフォーマット](#コード構造とフォーマット)
4. [コメントとドキュメント](#コメントとドキュメント)
5. [例外処理](#例外処理)
6. [Spring Boot 特有のガイドライン](#spring-boot-特有のガイドライン)
7. [パッケージ構造](#パッケージ構造)
8. [テスト](#テスト)
9. [セキュリティ対策](#セキュリティ対策)
10. [パフォーマンス最適化](#パフォーマンス最適化)

## 一般原則

### 1.1 SOLID原則の遵守
- **単一責任の原則（SRP）**: クラスは単一の責任を持つべき
- **オープン・クローズドの原則（OCP）**: 拡張に対してオープンで修正に対してクローズド
- **リスコフの置換原則（LSP）**: サブタイプはその基本型と置換可能であるべき
- **インターフェイス分離の原則（ISP）**: クライアントは使用しないインターフェイスに依存すべきでない
- **依存性逆転の原則（DIP）**: 上位モジュールは下位モジュールに依存すべきでない。両方とも抽象に依存すべき

### 1.2 設計原則
- DRY (Don't Repeat Yourself): コードの重複を避ける
- KISS (Keep It Simple, Stupid): シンプルさを保つ
- YAGNI (You Aren't Gonna Need It): 必要になるまで機能を追加しない
- 明示的なコードを書く: 暗黙の動作や魔法のような振る舞いを避ける

### 1.3 可読性とメンテナンス性
- 自己説明的なコード: コードを読むだけで何をしているかわかる
- 一貫性: 一貫したスタイルとパターンを使用
- 適切な抽象化: 適切なレベルの抽象化を選択

## 命名規則

### 2.1 一般ガイドライン
- 全ての識別子は英語で記述
- 省略形の使用を最小限に抑える（一般的に知られているものを除く：HTMLなど）
- 意図を明確に表現する名前を選択

### 2.2 クラス名
- パスカルケース (PascalCase) を使用
- 名詞または名詞句を使用
- インターフェースはプレフィックス「I」を付けない
- 例: `UserService`, `OrderProcessor`, `PaymentController`

### 2.3 変数名
- キャメルケース (camelCase) を使用
- 意味のある名前を選択
- 例: `userId`, `orderCount`, `isActive`

### 2.4 定数
- スネークケース + 大文字 (UPPER_SNAKE_CASE) を使用
- 例: `MAX_RETRY_COUNT`, `DEFAULT_TIMEOUT`

### 2.5 メソッド名
- キャメルケース (camelCase) を使用
- 動詞または動詞句で始める
- 例: `findUserById()`, `processPayment()`, `validateInput()`

### 2.6 パッケージ名
- 全て小文字
- ドット区切りの階層構造
- 例: `com.company.project.module`

### 2.7 Spring Boot 特有の命名

#### 2.7.1 コンポーネント
- `Controller`: サフィックスとして使用（例: `UserController`）
- `Service`: サフィックスとして使用（例: `UserService`）
- `Repository`: サフィックスとして使用（例: `UserRepository`）
- `Config`: サフィックスとして使用（例: `SecurityConfig`）

#### 2.7.2 その他のBean
- `Impl`: 実装クラスのサフィックスとして使用しない（例: `DefaultUserService` の方が `UserServiceImpl` より好ましい）
- `Factory`: 適切な場所でファクトリークラスに使用（例: `UserFactory`）

#### 2.7.3 テスト
- テストクラスは `Test` サフィックスを使用
- 例: `UserServiceTest`, `OrderControllerTest`

## コード構造とフォーマット

### 3.1 インデント
- 空白4文字を使用（タブではなく）
- 行の継続の場合は8文字インデント

### 3.2 行の長さ
- 120文字を超えないようにする
- 長い行は適切に分割する

### 3.3 改行
- 各ステートメントは新しい行に記述
- メソッド間は1行空ける
- 関連するコードブロック間は1行空ける
- クラス、インターフェース間は2行空ける

### 3.4 括弧
- 開き括弧は前の行の末尾に配置
- 閉じ括弧は独立した行に配置
- if, for などの制御構造でも必ず括弧を使用（単一行でも）

```java
// 良い例
if (condition) {
    doSomething();
}

// 悪い例
if (condition) doSomething();
```

### 3.5 空白
- 二項演算子の前後に空白を入れる
- カンマ、セミコロンの後に空白を入れる
- メソッド名と開き括弧の間に空白を入れない
- キャスト式の括弧内に空白を入れない

```java
// 良い例
int x = y + z;
method(a, b, c);
for (int i = 0; i < 10; i++) {

// 悪い例
int x=y+z;
method(a,b,c);
for(int i=0;i<10;i++){
```

### 3.6 imports
- ワイルドカードインポート (`import java.util.*`) を避ける
- 未使用のインポートを削除
- 以下の順序でグループ化:
  1. Java標準ライブラリ
  2. サードパーティライブラリ（Spring含む）
  3. プロジェクト内のクラス
- 各グループ内ではアルファベット順に並べる

### 3.7 クラス構造
- 以下の順序で記述:
  1. 静的変数
  2. インスタンス変数
  3. コンストラクタ
  4. メソッド（公開メソッドから始めて、関連するメソッドをグループ化）

### 3.8 ラムダ式とStream API
- 短いラムダ式は1行で記述
- 長いラムダ式は複数行に分割
- Stream操作は適切に改行

```java
// 良い例 - 短いラムダ
list.forEach(item -> System.out.println(item));

// 良い例 - 長いラムダ
list.forEach(item -> {
    String processed = processItem(item);
    System.out.println(processed);
    logProcessing(item, processed);
});

// 良い例 - Stream操作
return users.stream()
        .filter(user -> user.isActive())
        .map(User::getName)
        .collect(Collectors.toList());
```

## コメントとドキュメント

### 4.1 Javadoc
- すべての公開API（publicクラス、メソッド）にJavadocを付ける
- Javadocは「何を」するかを説明し、「どのように」するかの詳細は必要に応じて
- パラメータ、戻り値、例外をすべて文書化

```java
/**
 * ユーザーIDに基づいてユーザーを検索します。
 *
 * @param userId 検索するユーザーの一意識別子
 * @return 見つかったユーザー、存在しない場合はnull
 * @throws IllegalArgumentException userIdがnullまたは空の場合
 */
public User findUserById(String userId) {
    // 実装
}
```

### 4.2 実装コメント
- 複雑なロジックや非自明な実装にのみコメントを付ける
- TODOコメントには担当者と期限を含める

```java
// TODO(yamada): このロジックをリファクタリングする - 2023/12/31まで
```

### 4.3 不要なコメント
- 自明なコードにコメントを付けない
- コメントとコードの同期を保つ（古くなったコメントを避ける）

## 例外処理

### 5.1 例外の選択
- 回復可能な状況には検査例外 (checked exception) を使用
- プログラミングエラーには非検査例外 (unchecked exception) を使用
- 独自例外クラスを適切に定義（特に業務ロジックエラー用）

### 5.2 例外処理のベストプラクティス
- 例外を無視しない（空のcatchブロックを避ける）
- 例外を適切な抽象化レベルに変換（低レベルの例外を上位レベルにラップ）
- finally ブロックでリソースを確実に閉じる（または try-with-resources を使用）

```java
// 良い例
try (Connection conn = dataSource.getConnection()) {
    // データベース操作
} catch (SQLException e) {
    throw new DatabaseException("データの取得に失敗しました", e);
}

// 悪い例
try {
    // 何らかの操作
} catch (Exception e) {
    // 何もしない
}
```

### 5.3 Spring Boot での例外処理
- `@ExceptionHandler` と `@ControllerAdvice` を使用して一元的に例外を処理
- REST APIでは適切なHTTPステータスコードを返す
- 例外メッセージはユーザーフレンドリーで、かつセキュリティを考慮したものにする

## Spring Boot 特有のガイドライン

### 6.1 アプリケーション設定
- 環境固有の設定は外部化（`application-{profile}.properties` または YAML）
- 機密情報は環境変数または安全な方法で管理
- `@ConfigurationProperties` を活用して設定をタイプセーフに

```java
@Configuration
@ConfigurationProperties(prefix = "app.mail")
public class MailProperties {
    private String host;
    private int port;
    private String username;
    private String password;
    
    // getterとsetter
}
```

### 6.2 依存性注入
- コンストラクタ注入を優先（フィールド注入より）
- 複数のコンストラクタがある場合は `@Autowired` を明示
- 循環依存を避ける

```java
// 良い例
@Service
public class UserServiceImpl implements UserService {
    private final UserRepository userRepository;
    private final EmailService emailService;

    public UserServiceImpl(UserRepository userRepository, EmailService emailService) {
        this.userRepository = userRepository;
        this.emailService = emailService;
    }
    
    // メソッド実装
}

// 悪い例
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserRepository userRepository;
    
    @Autowired
    private EmailService emailService;
    
    // メソッド実装
}
```

### 6.3 RESTfulコントローラ
- URI設計は名詞ベース、適切な階層構造
- HTTPメソッドを正しく使用（GET、POST、PUT、DELETE）
- 適切なHTTPステータスコードを返す
- バージョニングを考慮

```java
@RestController
@RequestMapping("/api/v1/users")
public class UserController {
    private final UserService userService;
    
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    @GetMapping("/{id}")
    public ResponseEntity<UserDto> getUser(@PathVariable Long id) {
        UserDto user = userService.findById(id);
        return ResponseEntity.ok(user);
    }
    
    @PostMapping
    public ResponseEntity<UserDto> createUser(@Valid @RequestBody UserCreateDto userDto) {
        UserDto created = userService.create(userDto);
        URI location = ServletUriComponentsBuilder
                .fromCurrentRequest()
                .path("/{id}")
                .buildAndExpand(created.getId())
                .toUri();
        return ResponseEntity.created(location).body(created);
    }
    
    // 他のエンドポイント
}
```

### 6.4 データアクセス
- リポジトリインターフェースには Spring Data JPA を活用
- 複雑なクエリには `@Query` アノテーションを使用
- ページネーションを適切に実装
- N+1問題を避けるためJOIN FETCHを適切に使用

```java
public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByEmail(String email);
    
    @Query("SELECT u FROM User u WHERE u.status = :status")
    List<User> findByStatus(@Param("status") UserStatus status);
    
    @Query("SELECT u FROM User u JOIN FETCH u.roles WHERE u.id = :id")
    Optional<User> findByIdWithRoles(@Param("id") Long id);
}
```

### 6.5 サービス層
- トランザクション境界を適切に設定
- ビジネスロジックをここに集中
- ドメインオブジェクトとDTOの変換を管理

```java
@Service
@Transactional(readOnly = true)
public class OrderServiceImpl implements OrderService {
    private final OrderRepository orderRepository;
    private final ProductRepository productRepository;
    
    // コンストラクタ注入
    
    @Override
    @Transactional
    public OrderDto createOrder(OrderCreateDto orderDto) {
        // ビジネスロジック
        // バリデーション
        // リポジトリ呼び出し
        // DTOへの変換
    }
    
    // 他のメソッド
}
```

### 6.6 バリデーション
- Bean Validationを活用
- カスタムバリデーションには `@Validated` と `@Valid` を使用
- コントローラーでのリクエストバリデーションを徹底

```java
public class UserCreateDto {
    @NotBlank(message = "ユーザー名は必須です")
    @Size(min = 3, max = 50, message = "ユーザー名は3〜50文字である必要があります")
    private String username;
    
    @NotBlank(message = "メールアドレスは必須です")
    @Email(message = "有効なメールアドレスを入力してください")
    private String email;
    
    @NotBlank(message = "パスワードは必須です")
    @Pattern(regexp = "^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=])(?=\\S+$).{8,}$",
            message = "パスワードは最低8文字で、少なくとも1つの数字、大文字、小文字、特殊文字を含む必要があります")
    private String password;
    
    // getterとsetter
}
```

## パッケージ構造

### 7.1 レイヤードアーキテクチャ
```
com.company.project
  ├── config        // アプリケーション設定
  ├── controller    // RESTコントローラー
  ├── dto           // データ転送オブジェクト
  ├── entity        // JPA エンティティ
  ├── repository    // データアクセス層
  ├── service       // ビジネスロジック層
  ├── exception     // カスタム例外
  ├── util          // ユーティリティクラス
  └── Application.java  // メインクラス
```

### 7.2 モジュラーモノリス（機能別）
```
com.company.project
  ├── common        // 共通機能
  │   ├── config
  │   ├── exception
  │   └── util
  ├── user          // ユーザー管理機能
  │   ├── controller
  │   ├── dto
  │   ├── entity
  │   ├── repository
  │   └── service
  ├── order         // 注文管理機能
  │   ├── controller
  │   ├── dto
  │   ├── entity
  │   ├── repository
  │   └── service
  └── Application.java
```

### 7.3 ドメイン駆動設計（DDD）
```
com.company.project
  ├── application           // アプリケーションサービス
  ├── domain                // ドメインモデル
  │   ├── model
  │   ├── service
  │   ├── repository        // リポジトリインターフェース
  │   └── event
  ├── infrastructure        // 技術的な実装
  │   ├── config
  │   ├── persistence
  │   └── messaging
  ├── interfaces            // ユーザーインターフェース
  │   ├── rest
  │   ├── web
  │   └── dto
  └── Application.java
```

## テスト

### 8.1 テスト構造
- 本番コードと同じパッケージ構造を使用（testディレクトリ内）
- テストクラス名は対象クラス名 + `Test` サフィックス

### 8.2 単体テスト
- JUnitとMockitoを使用
- 各メソッドに対して複数のテストケース（正常系、異常系）
- Given-When-Thenパターンを使用

```java
@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
    @Mock
    private UserRepository userRepository;
    
    @InjectMocks
    private UserServiceImpl userService;
    
    @Test
    void findById_UserExists_ReturnsUser() {
        // Given
        User user = new User();
        user.setId(1L);
        user.setUsername("testuser");
        when(userRepository.findById(1L)).thenReturn(Optional.of(user));
        
        // When
        UserDto result = userService.findById(1L);
        
        // Then
        assertNotNull(result);
        assertEquals(1L, result.getId());
        assertEquals("testuser", result.getUsername());
        verify(userRepository).findById(1L);
    }
    
    @Test
    void findById_UserNotExists_ThrowsException() {
        // Given
        when(userRepository.findById(1L)).thenReturn(Optional.empty());
        
        // When & Then
        assertThrows(EntityNotFoundException.class, () -> userService.findById(1L));
        verify(userRepository).findById(1L);
    }
}
```

### 8.3 統合テスト
- `@SpringBootTest` を使用
- テスト用のプロファイルと設定
- TestContainersを活用してデータベースなどを統合テスト

```java
@SpringBootTest
@ActiveProfiles("test")
public class UserRepositoryIntegrationTest {
    @Autowired
    private UserRepository userRepository;
    
    @Test
    void findByEmail_UserExists_ReturnsUser() {
        // Given
        User user = new User();
        user.setUsername("testuser");
        user.setEmail("test@example.com");
        userRepository.save(user);
        
        // When
        Optional<User> result = userRepository.findByEmail("test@example.com");
        
        // Then
        assertTrue(result.isPresent());
        assertEquals("testuser", result.get().getUsername());
    }
}
```

### 8.4 APIテスト
- MockMvcを使用
- HTTPリクエスト/レスポンスをテスト
- JSONアサーションを活用

```java
@WebMvcTest(UserController.class)
public class UserControllerTest {
    @Autowired
    private MockMvc mockMvc;
    
    @MockBean
    private UserService userService;
    
    @Test
    void getUser_UserExists_ReturnsUser() throws Exception {
        // Given
        UserDto userDto = new UserDto(1L, "testuser", "test@example.com");
        when(userService.findById(1L)).thenReturn(userDto);
        
        // When & Then
        mockMvc.perform(get("/api/v1/users/{id}", 1L))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.id").value(1L))
                .andExpect(jsonPath("$.username").value("testuser"))
                .andExpect(jsonPath("$.email").value("test@example.com"));
        
        verify(userService).findById(1L);
    }
}
```

## セキュリティ対策

### 9.1 認証と認可
- Spring Securityを適切に設定
- JWTまたはOAuth2を適切に実装
- 権限ベースのアクセス制御を実装

```java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final JwtTokenProvider jwtTokenProvider;
    
    public SecurityConfig(JwtTokenProvider jwtTokenProvider) {
        this.jwtTokenProvider = jwtTokenProvider;
    }
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        return http
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/api/v1/auth/**").permitAll()
                .antMatchers("/api/v1/users/**").hasRole("ADMIN")
                .anyRequest().authenticated()
                .and()
                .apply(new JwtConfigurer(jwtTokenProvider))
                .and()
                .build();
    }
    
    // 他の設定
}
```

### 9.2 データバリデーション
- すべてのユーザー入力を検証
- XSS、SQLインジェクションなどを防止
- 出力エンコーディングを適用

### 9.3 機密情報の管理
- パスワードはBCryptなどで適切にハッシュ化
- APIキーなどの機密情報は適切に保護
- ログに機密情報を出力しない

## パフォーマンス最適化

### 10.1 データベースアクセス
- インデックスを適切に設定
- N+1問題を回避
- ページネーションを実装
- 大量データ処理にはバッチ処理を使用

### 10.2 キャッシング
- Spring Cacheを活用
- 適切なキャッシュ戦略を選択
- TTL（Time to Live）を適切に設定

```java
@Service
public class ProductServiceImpl implements ProductService {
    private final ProductRepository productRepository;
    
    public ProductServiceImpl(ProductRepository productRepository) {
        this.productRepository = productRepository;
    }
    
    @Override
    @Cacheable(value = "products", key = "#id")
    public ProductDto findById(Long id) {
        return productRepository.findById(id)
                .map(this::convertToDto)
                .orElseThrow(() -> new EntityNotFoundException("Product not found"));
    }
    
    @Override
    @CacheEvict(value = "products", key = "#productDto.id")
    public ProductDto update(ProductDto productDto) {
        // 更新ロジック
    }
    
    // 他のメソッド
}
```

### 10.3 非同期処理
- `@Async` アノテーションを活用
- WebFluxを検討（適切な場合）
- スレッドプールを適切に設定

```java
@Service
public class EmailServiceImpl implements EmailService {
    private final JavaMailSender mailSender;
    
    public EmailServiceImpl(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }
    
    @Async
    @Override
    public CompletableFuture<Void> sendEmail(String to, String subject, String content) {
        MimeMessage message = mailSender.createMimeMessage();
        try {
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setTo(to);
            helper.setSubject(subject);
            helper.setText(content, true);
            mailSender.send(message);
            return CompletableFuture.completedFuture(null);
        } catch (MessagingException e) {
            return CompletableFuture.failedFuture(new EmailSendException("Failed to send email", e));
        }
    }
}
```

### 10.4 アプリケーションプロファイリング
- Spring Boot Actuator を活用
- Meterレジストリでメトリクスを収集
- Prometheusなどでモニタリング

## 参考資料

1. Oracle Java コーディング規約
2. Spring Framework リファレンスドキュメント
3. Spring Boot リファレンスドキュメント
4. Clean Code by Robert C. Martin
5. Effective Java by Joshua Bloch