java-architect について
Java開発者向けのアーキテクチャ設計スキルで、Java 21、Spring Boot 3、Jakarta EEを中心に扱います。マイクロサービス、最新のJava機能、性能最適化、エンタープライズ統合パターンを解説します。
主な機能
- 01マイクロサービスとモノリスの設計
- 02Java 21の言語機能の活用
- 03Springエコシステムの開発ガイド
- 04JVMとデータベースの最適化
- 05イベント駆動とドメイン設計パターン
活用シーン
- 01Spring Bootマイクロサービスの設計
- 02JPAとJDBCの選択
- 03Kafkaイベント駆動システムの構築
- 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 型として公開します
コメント
Markdown 対応