mcpField
J
java-architect

java-architect

github.com/404kidwiz開発148 回インストール
リポジトリ

java-architect について

Java開発者向けのアーキテクチャ設計スキルで、Java 21、Spring Boot 3、Jakarta EEを中心に扱います。マイクロサービス、最新のJava機能、性能最適化、エンタープライズ統合パターンを解説します。

主な機能

  1. 01マイクロサービスとモノリスの設計
  2. 02Java 21の言語機能の活用
  3. 03Springエコシステムの開発ガイド
  4. 04JVMとデータベースの最適化
  5. 05イベント駆動とドメイン設計パターン

活用シーン

  1. 01Spring Bootマイクロサービスの設計
  2. 02JPAとJDBCの選択
  3. 03Kafkaイベント駆動システムの構築
  4. 04JVMまたはデータベース性能の調査

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/404kidwiz/claude-supercode-skills --skill java-architect

インストール後、Claude Code で/java-architectを入力して読み込まれたか確認します。

詳細

Java アーキテクト専門家

目的

Java 21、Spring Boot 3、Jakarta EE エコシステムを専門とする、Java アーキテクチャの専門知識を提供します。仮想スレッドやパターンマッチングなどの最新の Java 機能、マイクロサービスアーキテクチャ、包括的なエンタープライズ統合パターンを活用し、スケーラブルで保守性の高いエンタープライズグレードのアプリケーションを設計します。

使用する場面

  • Spring Boot 3 を使用したエンタープライズアプリケーション(マイクロサービス、REST API)の構築
  • Java 21 の機能(仮想スレッド、パターンマッチング、レコード、シールドクラス)の実装
  • Spring Cloud を使用したマイクロサービスアーキテクチャ(サービスディスカバリー、サーキットブレーカー)の設計
  • Jakarta EE アプリケーション(CDI、JPA、JAX-RS)の開発
  • Spring WebFlux を使用したリアクティブアプリケーションの作成
  • イベント駆動システム(Kafka、RabbitMQ)の構築
  • JVM パフォーマンス(GC チューニング、プロファイリング)の最適化

主な機能

エンタープライズアーキテクチャ
  • マイクロサービスアーキテクチャとモノリスアーキテクチャの設計
  • ドメイン駆動設計パターン(集約、境界づけられたコンテキスト)の実装
  • Spring Cloud エコシステム(Eureka、Config、Gateway)の構成
  • OpenAPI/Swagger を使用した API ファーストアーキテクチャの構築
モダンな Java 開発
  • 高い並行性を実現する Java 21 の仮想スレッドの実装
  • 型安全性を高めるパターンマッチングとシールドクラスの活用
  • 不変モデルのためのレコードとデータクラスの構築
  • ストリームを使用した関数型プログラミングパターンの適用
Spring エコシステム
  • Spring Boot アプリケーションの構成とデプロイ
  • データベースアクセスと最適化のための Spring Data JPA
  • 認証と認可のための Spring Security
  • リアクティブでノンブロッキングなアプリケーションのための Spring WebFlux
パフォーマンスの最適化
  • JVM のチューニングとガベージコレクションの構成
  • メモリプロファイリングとリーク検出
  • コネクションプーリングとデータベースの最適化
  • GraalVM を使用したアプリケーション起動の最適化


2. 判断フレームワーク

Spring フレームワークの選択判断ツリー
Application Requirements
│
├─ Need reactive, non-blocking I/O?
│  └─ Spring WebFlux ✓
│     - Netty/Reactor runtime
│     - Backpressure support
│     - High concurrency (100K+ connections)
│
├─ Traditional servlet-based web app?
│  └─ Spring MVC ✓
│     - Tomcat/Jetty runtime
│     - Familiar blocking model
│     - Easier debugging
│
├─ Microservices with service discovery?
│  └─ Spring Cloud ✓
│     - Eureka/Consul for discovery
│     - Config server
│     - API gateway (Spring Cloud Gateway)
│
├─ Batch processing?
│  └─ Spring Batch ✓
│     - Chunk-oriented processing
│     - Job scheduling
│     - Transaction management
│
└─ Need minimal footprint?
   └─ Spring Boot with GraalVM Native Image ✓
      - AOT compilation
      - Fast startup (<100ms)
      - Low memory (<50MB)
JPA と JDBC の判断マトリクス
要素JPA/Hibernate を使用JDBC(Spring JdbcTemplate)を使用
複雑さ関連を持つ複雑なドメインモデルシンプルなクエリ、レポート
パフォーマンスキャッシュを使用する OLTP(2 次キャッシュ)OLAP、一括操作
型安全性Criteria API、型安全なクエリRowMapper を使用したプレーンな SQL
保守性マイグレーションによるスキーマの進化SQL を直接制御
学習曲線急(遅延読み込み、カスケード)よりシンプルで明示的
N+1 クエリリスクあり(@EntityGraph、フェッチ結合が必要)明示的に制御可能

判断例:関連を持つ E コマースの注文システム → JPA(Order → OrderItems → Products)
判断例:集計を行う分析ダッシュボード → JDBC(複雑な SQL、パフォーマンスが重要)

仮想スレッド(Project Loom)の判断フロー
Concurrency Requirements
│
├─ High thread count (>1000 threads)?
│  └─ Virtual Threads ✓
│     - Millions of threads possible
│     - No thread pool tuning
│     - Blocking code becomes cheap
│
├─ I/O-bound operations (DB, HTTP)?
│  └─ Virtual Threads ✓
│     - JDBC calls don't block platform threads
│     - HTTP client calls scale better
│
├─ CPU-bound operations?
│  └─ Platform Threads (ForkJoinPool) ✓
│     - Virtual threads don't help
│     - Use parallel streams
│
└─ Need compatibility with existing code?
   └─ Virtual Threads ✓
      - Drop-in replacement for Thread
      - No code changes required
危険信号 → Oracle にエスカレーション
観察事項エスカレーションする理由例
JPA の N+1 クエリにより DB 呼び出しが 1000 回を超える複雑な遅延読み込みの問題「1 回のページ読み込みで SELECT クエリが 500 回実行される」
Spring Bean の循環依存アーキテクチャ設計の問題「起動時に BeanCurrentlyInCreationException が発生する」
GC をチューニングしてもメモリリークが発生する複雑なオブジェクト保持「Full GC 後もヒープが最大まで増加し、ヒープダンプに不可解な保持が示される」
複数のマイクロサービスにまたがる分散トランザクションSAGA パターンまたは補償トランザクション「Order、Payment、Inventory サービス間で ACID を実現する必要がある」
リアクティブストリームのバックプレッシャーが過負荷になる複雑なリアクティブパイプライン「Flux の生成が過剰で、下流が処理しきれない」


ワークフロー 2:Kafka を使用したイベント駆動型マイクロサービス

シナリオ:注文サービスにイベントソーシングを実装します

ステップ 1:Spring Kafka を設定する

// Configuration/KafkaConfig.java
@Configuration
@EnableKafka
public class KafkaConfig {
    
    @Value("${spring.kafka.bootstrap-servers}")
    private String bootstrapServers;
    
    @Bean
    public ProducerFactory<String, DomainEvent> producerFactory() {
        Map<String, Object> config = Map.of(
            ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers,
            ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class,
            ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class,
            ProducerConfig.ACKS_CONFIG, "all",
            ProducerConfig.RETRIES_CONFIG, 3,
            ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true
        );
        
        return new DefaultKafkaProducerFactory<>(config);
    }
    
    @Bean
    public KafkaTemplate<String, DomainEvent> kafkaTemplate() {
        return new KafkaTemplate<>(producerFactory());
    }
    
    @Bean
    public ConsumerFactory<String, DomainEvent> consumerFactory() {
        Map<String, Object> config = Map.of(
            ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers,
            ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class,
            ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class,
            ConsumerConfig.GROUP_ID_CONFIG, "order-service",
            ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest",
            ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, false,
            JsonDeserializer.TRUSTED_PACKAGES, "com.example.order.domain.events"
        );
        
        return new DefaultKafkaConsumerFactory<>(config);
    }
}

ステップ 2:ドメインイベントを定義する

// Domain/Events/DomainEvent.java
public sealed interface DomainEvent permits 
    OrderCreated, OrderItemAdded, OrderProcessingStarted, OrderCompleted, OrderCancelled {
    
    UUID aggregateId();
    LocalDateTime occurredAt();
    long version();
}

public record OrderCreated(
    UUID aggregateId,
    UUID customerId,
    LocalDateTime occurredAt,
    long version
) implements DomainEvent {}

public record OrderItemAdded(
    UUID aggregateId,
    UUID productId,
    int quantity,
    BigDecimal unitPrice,
    LocalDateTime occurredAt,
    long version
) implements DomainEvent {}

public record OrderCompleted(
    UUID aggregateId,
    BigDecimal totalAmount,
    LocalDateTime occurredAt,
    long version
) implements DomainEvent {}

ステップ 3:イベントパブリッシャー

// Infrastructure/EventPublisher.java
@Component
public class DomainEventPublisher {
    
    private final KafkaTemplate<String, DomainEvent> kafkaTemplate;
    private static final String TOPIC = "order-events";
    
    public DomainEventPublisher(KafkaTemplate<String, DomainEvent> kafkaTemplate) {
        this.kafkaTemplate = kafkaTemplate;
    }
    
    @Async
    public CompletableFuture<Void> publish(DomainEvent event) {
        return kafkaTemplate.send(TOPIC, event.aggregateId().toString(), event)
            .thenAccept(result -> {
                var metadata = result.getRecordMetadata();
                log.info("Published event: {} to partition {} offset {}",
                    event.getClass().getSimpleName(),
                    metadata.partition(),
                    metadata.offset());
            })
            .exceptionally(ex -> {
                log.error("Failed to publish event: {}", event, ex);
                return null;
            });
    }
}

ステップ 4:イベントコンシューマー

// Infrastructure/OrderEventConsumer.java
@Component
public class OrderEventConsumer {
    
    private final OrderProjectionService projectionService;
    
    @KafkaListener(
        topics = "order-events",
        groupId = "order-read-model",
        containerFactory = "kafkaListenerContainerFactory"
    )
    public void handleEvent(
        @Payload DomainEvent event,
        @Header(KafkaHeaders.RECEIVED_PARTITION) int partition,
        @Header(KafkaHeaders.OFFSET) long offset
    ) {
        log.info("Received event: {} from partition {} offset {}", 
            event.getClass().getSimpleName(), partition, offset);
        
        switch (event) {
            case OrderCreated e -> projectionService.handleOrderCreated(e);
            case OrderItemAdded e -> projectionService.handleOrderItemAdded(e);
            case OrderCompleted e -> projectionService.handleOrderCompleted(e);
            case OrderCancelled e -> projectionService.handleOrderCancelled(e);
            default -> log.warn("Unknown event type: {}", event);
        }
    }
}

期待される結果:

  • Kafka を使用したイベント駆動型アーキテクチャ
  • 型安全なイベント処理(sealed インターフェース、パターンマッチング)
  • CompletableFuture を使用した非同期イベント発行
  • 冪等なイベント処理


4. パターンとテンプレート

パターン 1:仕様を使用したリポジトリパターン

ユースケース:型安全な動的クエリ

// Specification for dynamic filtering
public class OrderSpecifications {
    
    public static Specification<Order> hasCustomerId(CustomerId customerId) {
        return (root, query, cb) -> 
            cb.equal(root.get("customerId"), customerId);
    }
    
    public static Specification<Order> hasStatus(OrderStatus status) {
        return (root, query, cb) -> 
            cb.equal(root.get("status"), status);
    }
    
    public static Specification<Order> createdBetween(LocalDateTime start, LocalDateTime end) {
        return (root, query, cb) -> 
            cb.between(root.get("createdAt"), start, end);
    }
    
    public static Specification<Order> totalGreaterThan(BigDecimal amount) {
        return (root, query, cb) -> 
            cb.greaterThan(root.get("totalAmount"), amount);
    }
}

// Usage: Combine specifications
Specification<Order> spec = Specification
    .where(hasCustomerId(customerId))
    .and(hasStatus(new OrderStatus.Pending()))
    .and(createdBetween(startDate, endDate));

List<Order> orders = orderRepository.findAll(spec);


パターン 3:読み取りモデルと書き込みモデルを分離した CQRS

ユースケース:読み取りを最適化し、書き込みから独立させます

// Write model (domain entity)
@Entity
public class Order {
    // Rich behavior, complex relationships
    public void addItem(Product product, int quantity) { ... }
    public void complete() { ... }
}

// Read model (denormalized projection)
@Entity
@Table(name = "order_summary")
@Immutable
public class OrderSummary {
    
    @Id
    private UUID orderId;
    private UUID customerId;
    private String customerName;
    private int itemCount;
    private BigDecimal totalAmount;
    private String status;
    private LocalDateTime createdAt;
    
    // Getters only (no setters, immutable)
}

// Read repository (optimized queries)
public interface OrderSummaryRepository extends JpaRepository<OrderSummary, UUID> {
    
    @Query("""
        SELECT os FROM OrderSummary os
        WHERE os.customerId = :customerId
        ORDER BY os.createdAt DESC
        """)
    List<OrderSummary> findByCustomerId(@Param("customerId") UUID customerId);
}


❌ アンチパターン:LazyInitializationException

発生例:

@Service
@Transactional
public class OrderService {
    
    public Order findById(OrderId id) {
        return orderRepository.findById(id).orElseThrow();
    }
}

@RestController
public class OrderController {
    
    @GetMapping("/orders/{id}")
    public OrderDto getOrder(@PathVariable UUID id) {
        Order order = orderService.findById(new OrderId(id));
        
        // Transaction already closed!
        var items = order.getItems(); // LazyInitializationException!
        
        return new OrderDto(order, items);
    }
}

発生する理由:

  • トランザクション外での遅延読み込み:Hibernate のプロキシがデータを読み込めません
  • N+1 クエリ:トランザクションが開いていても、遅延読み込みによって複数のクエリが実行されます

正しい方法:

// Option 1: Eager fetch with @EntityGraph
@Repository
public interface OrderRepository extends JpaRepository<Order, OrderId> {
    
    @EntityGraph(attributePaths = {"items", "items.product"})
    Optional<Order> findById(OrderId id);
}

// Option 2: DTO projection (no lazy loading)
@Query("""
    SELECT new com.example.dto.OrderDto(
        o.id, o.customerId, o.totalAmount,
        COUNT(i.id), o.status, o.createdAt
    )
    FROM Order o
    LEFT JOIN o.items i
    WHERE o.id = :id
    GROUP BY o.id, o.customerId, o.totalAmount, o.status, o.createdAt
    """)
Optional<OrderDto> findOrderDtoById(@Param("id") OrderId id);

// Option 3: Open Session in View (not recommended for APIs)
spring.jpa.open-in-view: false  // Disable to catch lazy loading issues early


6. 統合パターン

backend-developer:
  • 引き継ぎ:backend-developer がビジネスロジックを定義し、java-architect が Spring Boot のパターンを使用して実装します
  • 連携:REST API の設計、データベーススキーマ、認証と認可
  • ツール:Spring Boot、Spring Security、Spring Data JPA、Jackson
  • 例:バックエンドが注文ワークフローを定義し、java-architect が DDD 集約とドメインイベントを使用して実装します
database-optimizer:
  • 引き継ぎ:java-architect が遅い JPA クエリを特定し、database-optimizer がインデックスを作成します
  • 連携:クエリの最適化、コネクションプーリング、トランザクションの調整
  • ツール:Hibernate 統計情報、JPA Criteria API、ネイティブクエリ
  • 例:N+1 クエリの問題に対し、database-optimizer が外部キーに複合インデックスを追加します
devops-engineer:
  • 引き継ぎ:java-architect が Spring Boot アプリを構築し、devops-engineer が Docker でコンテナ化します
  • 連携:ヘルスチェック、メトリクス(Actuator)、グレースフルシャットダウン
  • ツール:Spring Boot Actuator、Micrometer、Docker のマルチステージビルド
  • 例:java-architect が /actuator/health を公開し、devops-engineer が Kubernetes の liveness probe を設定します
kubernetes-specialist:
  • 引き継ぎ:java-architect がマイクロサービスを構築し、kubernetes-specialist が K8s にデプロイします
  • 連携:レディネスプローブ、リソース制限、ローリングアップデート
  • ツール:Spring Cloud Kubernetes、ConfigMaps、Secrets
  • 例:java-architect が @ConfigurationProperties を使用し、kubernetes-specialist が ConfigMap を提供します
graphql-architect:
  • 引き継ぎ:java-architect がドメインモデルを提供し、graphql-architect が GraphQL API として公開します
  • 連携:スキーマ設計、DataLoader による N+1 の防止
  • ツール:Spring GraphQL、GraphQL Java、DataLoader
  • 例:注文集約を、リゾルバーとサブスクリプションを備えた GraphQL 型として公開します

コメント