変化に強いテストを育てるSpringBootのレイヤー設計

33.9K Views

May 30, 26

スライド概要

JJUG CCC 2026 Spring 登壇資料

profile-image

Java, Spring Boot, React, Vue.js など

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

ダウンロード

関連スライド

各ページのテキスト
1.

変化に強いテストを育てる SpringBootのレイヤー設計 2026-05-30 | JJUG CCC 2026 Spring 株式会社サンアーチ Takeshi Miyajima

2.

⾃⼰紹介 Takeshi Miyajima 宮島 健 “スピーキングエンジニア”を育てる 株式会社サンアーチ 業務システム開発 / 運⽤ツール開発 Java / Spring Boot / React / Vue / etc 2

3.

はじめに 3

4.

今⽇話すテストのスコープ 開発者テスト QAテスト 受⼊テスト こっちの話 4

5.

今⽇話すテストのスコープ E2E パフォーマンス 要件 設計 継続的インテグレーション 実装 テスト リリース CI ここら辺のテストの話 5

6.

今⽇の話のモチベーション • 開発を⽀えてくれる、価値あるテストを作るのは難しい • 設計が抱える問題点がテストを難しくしている事例が多い →”価値あるテストを⽀えるのは良い設計だ” という話をしたい • Spring Bootに限らない当たり前の話が多いが、 誰かの気づきになったら嬉しい 6

7.

今⽇話すこと・話さないこと 今⽇話すこと • 現場でよくある⾃動テストの悩みと、その原因 • 変化に強いテストを⽬指すための、設計からのアプローチ • 責務の分離が変化に強いテストを⽣み出す 話さないこと • JUnit・Mockito等の使い⽅ • アーキテクチャパターンの詳細 / ドメイン駆動設計の詳細 • 負荷テスト・パフォーマンステスト・E2Eテストの⼿法 7

8.

変化に強いテスト 8

9.

変化に強いテストとは 変化に強いテストとは 継続開発を⽀える品質ガード • • • • 関係ないテストは壊れない リファクタリングの影響を受けない 仕様が変わったらテストが検知してくれる いつでも素早く実⾏できる つまり・・・ として働くもの • 信頼性が⾼い • 保守コストが低い • フィードバックが早い 9

10.

変化に強いテストは、 プロダクトの進化を 加速 させる 10

11.

現場でよくある “変化に弱いテスト” あなたのプロジェクトも同じ悩みを抱えていませんか︖ 保守コストが⾼い 信頼性が低い • • • ⾃動テストは通るのに、 動かしてみると動かない テストの動作が不安定 結局⼿動テストが必要 • • 変更のたびに、⼤量の テスト修正が発⽣ 仕様は変わらないのに テストだけ壊れる フィードバックが遅い • • 実⾏が遅い エラー原因がわかりづらい → どうしたら “変化に強いテスト” が作れるのか 11

12.

変化に強いテストの鍵は ”プロダクトコードの改善” プロダクトコード まずい設計 構造を反映 テストコード ・・・結果的に・・・ 変化に弱いテスト ・・・結果的に・・・ 変化に強いテスト 改善 良い設計 12

13.

“責務の混在” が変化に弱いテストを⽣み出す 変化に弱いテスト 責務の混在 ⼊⼒チェック 処理フロー データの加⼯ 永続化 トランザクション 整合性チェック ルール 計算 判断 観点 無理やりテスト 観点 観点 観点 観点 観点 観点 13

14.

“責務の分離” が変化に強いテストを⽣むポイント 責務を分離 Presentation リクエスト受け取り 形式チェック レスポンス返却 Application データの加⼯・計算 整合性チェック トランザクション管理 DataAccess 変化に強いテスト 観点 観点 観点 観点 観点 観点 データ⼊出⼒ 観点 観点 データ変換 観点 観点 テスト 14

15.

ここまでのまとめ • 変化に強いテスト = 継続開発を⽀える品質ガード • 変化に強いテストには、良い設計 が必要 • 変化に強いテストには、責務の分離 が重要 15

16.

“変化に強いテスト” を⽬指す責務分離 ① 外部接続の分離 ② レイヤー内の分離 ③ 参照系の分離 16

17.

前提とするアーキテクチャ よくある三層アーキテクチャで考えていきます クライアント Presentation Application DataAccess Controller Service Repository リクエスト受け取り 形式チェック レスポンス返却 処理フロー制御 データの加⼯・計算 整合性チェック 業務ロジック トランザクション管理 データ⼊出⼒ データ変換 DB 17

18.

①“外部接続” と “それ以外” を明確に分ける Presentation 役割 外部接続 Application 分ける その他 DataAccess 分ける 外部接続 18

19.

②外部接続の責務を薄く、処理をシンプルにする Presentation Application DataAccess 役割 外部接続 その他 外部接続 責務 薄く 処理内容 シンプル 寄せる 厚く 複雑 寄せる 薄く シンプル 19

20.

よくある失敗① Controller肥⼤化 Controllerが処理フローを担ってしまう 処理フローの制御はサービスに寄せ、Controllerはサービスを呼び出すだけ シンプル < 複雑 複雑 > シンプル Presentation Application 制御・呼び分け Controller CheckService 改善 Presentation Application Controller ProcessService ProcessService1 テスト ProcessService2 テスト増・遅い・壊れやすい テスト 1. 2. 3. チェック 処理1 処理2 テスト少・早い・安定 20

21.

よくある失敗② Repositoryへの業務知識漏洩 SQLで状態チェック、SQLに業務仕様が埋め込まれている RepositoryはシンプルなデータIOに特化、チェック・判断はサービスやモデルへ SELECT * FROM orders o WHERE o.customer_id = ? AND o.status = 'CONFIRMED' AND o.cancelled_at IS NULL AND o.created_at >= NOW() - INTERVAL 30 DAY 改善 SELECT * FROM orders o WHERE o.customer_id = ? Order isValidForShipping() 業務仕様埋め込み シンプルIO + モデルメソッド 21

22.

分離してからどうテストするか︖ Presentation Application DataAccess 役割 外部接続 その他 外部接続 責務 薄い 厚い 薄い 処理内容 シンプル 複雑 シンプル テスト戦略 ︖ ︖ ︖ 22

23.

テスト戦略︓Presentationレイヤー • • • @WebMvcTest、MockMvc等を使う Applicationレイヤーをモックする 主なテスト観点 • リクエスト/レスポンスのやり取り • ⼊⼒チェック・認証認可チェック • Applicationレイヤーの呼び出し これ以外の観点が必要なら 責務混在の懸念あり Presentation Application Controller Service モック テスト @WebMvcTest MockMvc 23

24.

テスト戦略︓Presentationレイヤー サンプルコード @WebMvcTest(OrderController.class) class OrderControllerTest { @Autowired MockMvc mockMvc; @MockitoBean OrderService orderService; @Test void 注⽂作成が成功したら200を返す() { // モックのセットアップ when(orderService.create(any())).thenReturn(OrderId.of(1)); // mockMvcでControllerの挙動をテスト mockMvc.perform(post("/orders")...) .andExpect(status().isOk()); } } // モックの呼び出しを確認 verify(orderService).create(orderRequest()); 24

25.

テスト戦略︓DataAccessレイヤー • • • @DataJpaTest、JdbcClient等を使う H2やTestcontainersで実DBテストをする 主なテスト観点 • 検索・登録・更新・削除SQL • Javaクラスとの相互変換 • 取得件数ごとの結果パターン 業務観点のパターンが必要なら 責務混在の懸念あり DataAccess Repository H2 / Testcontainers テスト @DataJpaTest/@JdbcTest JdbcClient 25

26.

テスト戦略︓DataAccessレイヤー サンプルコード @DataJpaTest class OrderRepositoryTest { @Autowired OrderRepository orderRepository; @Autowired JdbcClient jdbcClient; @Test void IDで注⽂を検索できる() { // データのセットアップ // @Sqlを使うのもおすすめ jdbcClient.sql("INSERT INTO ...").update(); // メソッドを実⾏ var found = orderRepository.findById(1); } } // 結果の確認 assertThat(found).hasValue(new Order(...)); 26

27.

補⾜︓H2かTestcontainersか RepositoryがシンプルIOに特化できれば、H2で⾜りるはず DB固有のSQLが必要な場合のみTestcontainersを使うのがおすすめ H2 おすすめ Testcontainers 実⾏速度 ◎ 速い(インメモリ) △ 遅い(コンテナ起動) SQL互換性 △ ⽅⾔⾮対応のことあり ◎ 本番DBと同じ CI環境 ◎ 追加設定不要 △ Docker必須 27

28.

テスト戦略︓Applicationレイヤー • • • Springは使わずにテスト インメモリRepositoryを使う 主なテスト観点 • • • • 処理フロー データの加⼯・計算 整合性チェック 業務ロジック ⾃動テストの主戦場 テスト量も最も多いはず Application DataAccess Service Repository インメモリ Repository テスト @ExtendWith( SpringExtension.class) 28

29.
[beta]
テスト戦略︓Applicationレイヤー サンプルコード
class OrderServiceTest {
private InMemoryOrderRepository repository = new InMemoryOrderRepository();
private OrderCreationPolicy policy = new OrderCreationPolicy();
private OrderService sut = new OrderService(repository, policy);
@Test
void 注⽂を作成すると保存される() {
var request = new OrderRequest(...);
var orderId = sut.createOrder(request);

}

}

var order = repository.findById(orderId);
assertThat(order).hasValue(new Order(...));

@Test
void ⾦額が0以下の場合は注⽂を作成できない() {
assertThatThrownBy(() -> sut.createOrder(new OrderRequest(...)))
.isInstanceOf(IllegalArgumentException.class);
}

29

30.

補⾜︓Spring Testは使わず早いテストを⽬指すべし Applicationレイヤーは、Springの⼒を借りずに早いテストができる →Applicationに責務を集約し、早いテストの分量を増やす Large テストピラミッド Small > Medium > Large のバランスが良い Medium ・・・ Presentation / DataAccessレイヤーのテスト 少 Small ・・・ Applicationレイヤーのテスト 多 30

31.

補⾜︓インメモリRepositoryがおすすめ • Repositoryをモックしてしまうと、テストの保守性が下がる • インメモリRepositoryを使えば、モックの⾟みを軽減できる モック Repository Repository findById(long id) findByName(String name) 整合性・定義もれ 振る舞い1 振る舞い2 代替 インメモリ Repository ずれない・漏れない Map 31

32.
[beta]
補⾜︓インメモリRepositoryのサンプル
class InMemoryOrderRepository
implements OrderRepository {
private final Map<Long, Order> store = new HashMap<>();
private long nextId = 1L;
@Override
public Order save(Order order) {
var saved = order.withId(nextId++);
store.put(saved.getId(), saved);
return saved;
}
@Override
public Optional<Order> findById(long orderId) {
var order = this.store.get(orderId);
return Optional.ofNullable(order);
}
}

32

33.

外部接続の分離まとめ Presentation Application DataAccess 役割 外部接続 分ける その他 分ける 外部接続 責務 薄い 寄せる 厚い 寄せる 薄い 処理内容 シンプル 複雑 シンプル テスト戦略 Springありき Appはモック 早いテスト インメモリRepo Springありき 実DB 33

34.

“変化に強いテスト” を⽬指す責務分離 ① 外部接続の分離 ② レイヤー内の分離 ③ 参照系の分離 34

35.

意図的に厚くしたレイヤーとどう戦うか Presentation Application DataAccess 役割 外部接続 その他 外部接続 責務 薄い 厚い 薄い 処理内容 シンプル 複雑 シンプル 35

36.

⼤きい塊のままだと、テストも⼤きくなる BigService 構造を反映 BigServiceTest 保守コストが⾼い 変化に弱い 36

37.

⼩さく分ければ、テストも⼩さくなる SmallService SmallClass SmallClass 構造を反映 SmallServiceTest SmallClassTest SmallClassTest ⼩さいテストは扱いやすい 変化に強い︖ 37

38.

分け⽅次第で変化への強さは変わる 変化に弱い 変化に強い 適当に区切る 責務の境界で区切る ⼊⼒チェック 処理フロー データの加⼯ 永続化 トランザクション 整合性チェック ルール 計算 判断 処理フロー ルール 判断 計算 通知 構築 38

39.

よく使う責務の分離パターン ① 判断 と 処理 の分離 ② 処理フロー と 実処理 の分離 ③ 構築 と 実⾏ の分離 39

40.

分離パターン① 判断と処理の分離 業務知識としての判断はドメインモデルへ、判断を使う処理はServiceへ OrderService 処理フロー 判断 OrderService 参照 Order 情報 Order 処理フロー 判断 情報 参照 40

41.

分離パターン① 修正前 public class Order { private OrderStatus status; // getter / setter のみ } public class OrderService { public void cancel(Long orderId) { 判断 var order = orderRepository.findById(orderId).orElseThrow(); if (order.getStatus() != OrderStatus.PENDING && order.getStatus() != OrderStatus.CONFIRMED) { throw new IllegalStateException("キャンセルできません"); } order.setStatus(OrderStatus.CANCELLED); orderRepository.save(order); } } 41

42.

分離パターン① 判断を移動 public class Order { private OrderStatus status; // getter / setter public boolean isCancellable() { return status == OrderStatus.PENDING || status == OrderStatus.CONFIRMED; } } 判断 public class OrderService { public void cancel(Long orderId) { var order = orderRepository.findById(orderId).orElseThrow(); if (!order.isCancellable()) { throw new IllegalStateException("キャンセルできません"); } order.setStatus(OrderStatus.CANCELLED); orderRepository.save(order); } } 42

43.

分離パターン① さらに、判断を伴う処理ごと移動 public class Order { 判断を伴う private OrderStatus status; public boolean isCancellable() { ... } 処理 public void cancel() { if (!isCancellable()) { throw new IllegalStateException("キャンセルできません"); } setStatus(OrderStatus.CANCELLED); } } public class OrderService { public void cancel(Long orderId) { var order = orderRepository.findById(orderId).orElseThrow(); order.cancel(); orderRepository.save(order); } } 43

44.

分離パターン① テストに対するメリット モデルがロジックを持つと、独⽴性が⾼くテストしやすくなる Service側のテストパターンも減らせる テスト内容 OrderService Test 処理フロー 判断内容 テスト内容 OrderService Test 処理フロー OrderTest 判断内容 テスト しやすい ロジックが 安定 44

45.

よく使う責務の分離パターン ① 判断 と 処理 の分離 ② 処理フロー と 実処理 の分離 ③ 構築 と 実⾏ の分離 45

46.

分離パターン② 処理フローと実処理の分離 処理の塊を別クラスに逃し、処理フローを独⽴させる OrderService 処理フロー データ更新 イベント通知 OrderUseCase 処理フロー OrderRecord Service データ更新 イベント通知 46

47.

分離パターン② 修正前 OrderService // バリデーション validate(req); var order = buildOrder(req); // 在庫チェックと分岐 if (inventoryService.isAvailable(order)) { // データ更新 orderRepository.save(order); inventoryService.decrease(order); // イベント通知 eventPublisher.publish(new OrderPlaced(order)); notificationService.notify(order); } else { // 在庫不⾜ orderRepository.saveAsPending(order); notificationService.notifyOutOfStock(order); } 処理の塊 47

48.

分離パターン② 実処理を移動 OrderUseCase OrderRecordService // バリデーション validate(req); var order = buildOrder(req); // 在庫チェックと分岐 if (inventoryService.isAvailable(order)) { orderRecordService.record(order); } else { // 在庫不⾜ orderRecordService .recordAsPending(order); } void record(Order order) { // データ更新 orderRepository.save(order); inventoryService.decrease(order); // イベント通知 eventPublisher.publish( new OrderPlaced(order)); notificationService.notify(order); } 分離 void recordAsPending(Order order) { // 在庫不⾜時 orderRepository.saveAsPending(order); notificationService .notifyOutOfStock(order); } 48

49.

分離パターン② テストに対するメリット テストパターンが “掛け合わせ” から “⾜し算” になる テスト内容 テスト内容 OrderService Test 分岐 処理内容 OrderUseCase Test 分岐 OrderRecord ServiceTest 処理内容 49

50.

よく使う責務の分離パターン ① 判断 と 処理 の分離 ② 処理フロー と 実処理 の分離 ③ 構築 と 実⾏ の分離 50

51.

分離パターン③ 構築と実⾏の分離 外部APIアクセス (インフラレイヤー) でも使えるパターン 複雑な情報構築と、愚直な実⾏役に分離する Notification Service メッセージ構築 通知送信 Notification MessageBuilder Notification Sender メッセージ構築 通知送信 51

52.

分離パターン③ 修正前 NotificationService 構築 public void notify(Order order) { // 複数ソースからメッセージ構築 var user = userRepository.findById(order.getUserId()); var product = productRepository.findById(order.getProductId()); // 通知内容の構築 var subject = ... var body = ... 実⾏ // 送信実⾏ var mail = new SimpleMailMessage(); mail.setTo(user.getEmail()); mail.setSubject(subject); mail.setText(body); sender.send(mail); } 52

53.

分離パターン③ 情報の構築処理を抽出 NotificationService public void notify(Order order) { // メッセージを構築 var message = messageBuilder.build(order); // メッセージを送信 sender.send(message); } NotificationMessageBuilder public NotificationMessage build(...) { // 複数ソースからメッセージ構築 var user = ... var product = ... // 通知内容の構築 var subject = ... var body = ... NotificationSender public void send( NotificationMessage message) { var mail = new SimpleMailMessage(); mail.setTo(message.getEmail()); mail.setSubject(message.getSubject()); mail.setText(message.getBody()); sender.send(mail); } return new NotificationMessage( user.getEmail(), subject, body); } 53

54.

分離パターン③ テストに対するメリット 複雑なデータ構築パターンや、外部アクセスを切り出して重点的にテスト可能 NotificationMessageBuilderTest NotificationSenderTest • ⼊⼒データバリエーション • テンプレート置換 • メッセージパターン • 送信先・フォーマット 54

55.

補⾜︓早すぎるテストの分離に注意 テストを分離すると、リファクタリング時の修正対象テストが増える →クラス間の境界が安定するまでは、あえてテストは分離しない作戦もアリ OrderUseCase use OrderRecord Service OrderUseCase Test まとめてテストすれば クラス間で処理を移動しても壊れない 55

56.

よく使う責務の分離パターン まとめ ① 判断 と 処理 の分離 ② 処理フロー と 実処理 の分離 ③ 構築 と 実⾏ の分離 56

57.

“変化に強いテスト” を⽬指す責務分離 ① 外部接続の分離 ② レイヤー内の分離 ③ 参照系の分離 57

58.

“更新系” と “参照系” アプリ内の処理は、 “更新系” と “参照系” に分けて考えることができる 更新系 データ更新 画⾯ DB 参照系 データ取得 58

59.

共通モデルパターン シンプルなアプリでは、更新系も参照系も、⼀つのモデルを共⽤する事が多い 更新系 データ更新 共通 Order 画⾯ 参照系 データ取得 59

60.

共通モデルが引き起こす問題 更新系・参照系、相互に影響しあってテストが壊れる 更新系 参照系 Order 制約条件追加 テストが壊れる +id: Long +customerId: Long +items: List<OrderItem> +status: OrderStatus テストが壊れる フィールド追加 60

61.

そもそも要求が違う 様々な要求の違いがあるため、⼀つのモデルで扱うと問題が起きる 観点 更新系 参照系 関⼼事 正確性・整合性 ⾒やすさ・速さ 処理件数 1件 〜 少数 1件 〜 ⼤量 ビジネスルール検証 必要 不要 トランザクション 必要 基本不要 副作⽤ あり(DB書き込み・イベント) なし 変更要因 ビジネスルール UI / UX 61

62.

参照系をまるっと分離するという解決策 更新系とは別に参照系の経路を作り、モデルも処理も完全分離する (いわゆるCQRS) → 要求の違いをそのままモデルに反映することで、モデル汚染をなくす 更新系 Presentation Application DataAccess 参照系 Query 62

63.

更新系と参照系でモデルを分ける 更新系 Order +id: Long +customerId: Long +items: List<OrderItem> +status: OrderStatus 参照系 OrderView +id: Long +customer: CustomerView +items: List<OrderItemView> +status: OrderStatus +shipping: ShippingView OrderRepository +findById() OrderQueryRepository +findByShippingStatus() 63

64.

分ければテストは安定する 更新系 参照系 Order OrderView 影響なし OrderService OrderService Test OrderQuery Service OrderQuery ServiceTest 64

65.

補⾜︓参照系の分離タイミング・範囲に注意 • アプリがシンプルな状態で分離すると、開発コストが上がる • 全体に適⽤すると、シンプルな機能まで開発コストが上がる • 分離メリットがタイミング・範囲を⾒極めて適⽤すべし 同じでいいなら コストがかかるだけ 更新系 参照系 Order OrderView +id: Long +customerId: Long +items: List<OrderItem> +status: OrderStatus +id: Long +customerId: Long +items: List<OrderItemView> +status: OrderStatus 65

66.
[beta]
参照系のテストサンプル
• Serviceの責務が薄いため、DataAccessレイヤーと⼀緒にテストもあり
• DB固有機能を使⽤する割合が多いため、Testcontainersを使う場⾯が増える
@JdbcTest
@Import({ OrderQueryService.class, OrderQueryRepository.class })
@AutoConfigureTestDatabase(replace = Replace.NONE)
@Testcontainers
class OrderQueryServiceTest {
@Container @ServiceConnection
static PostgreSQLContainer<?> postgres = new PostgreSQLContainer<>("postgres:16");
@Autowired
OrderQueryService sut;
...
}

66

67.

補⾜︓参照系のみGraphQL化する選択肢 • 参照系のみGraphQL化すると、 データツリー構築のコスト削減に有利 • フロントエンド側でGraphQLのメリットが 活かせれば、特に効果が⼤きい • 通常のREST APIとは設計の考え⽅が 違うため注意 query GetOrder($id: ID!) { order(id: $id) { id status customer { id name address { prefecture city street } } items { quantity unitPrice product { id name category { id name } } } } } 67

68.

まとめ 68

69.

まとめ • 変化に強いテスト = 継続開発を⽀える品質ガード • 信頼性が⾼い、保守コストが低い、フィードバックが早い • 変化に強いテストには、良い設計 が必要 • 変化に強いテストには、責務の分離 が重要 • 外部接続の分離、レイヤー内の分離、参照系の分離 69

70.

変化に強いテストで、 プロダクトの進化を 加速 させよう 70

71.

ご清聴ありがとうございました 全体アンケート セッションアンケート 71

72.

APPENDIX 72

73.

テストの偽陽性・偽陰性 プロダクトコードの正誤と、テストの成否が⼀致しない状態のこと プロダクトコードが ! " # 結 果 & 正しい 誤っている 成功 期待通り 偽陰性 失敗 偽陽性 期待通り サバンナ便り 〜ソフトウェア開発の荒野を⽣き抜く 第2回 偽陽性と偽陰性 https://gihyo.jp/dev/serial/01/savanna-letter/0002 73

74.

テストサイズとテストピラミッド 従来の「ユニットテスト/結合テスト」のような⽬的・粒度ベースの分類ではなく、 実⾏時間・⾮決定性・外部依存の多さでテストを分類する考え⽅ Feature Small Medium Large Network access No localhost only Yes Database No Yes Yes File system access No Yes Yes Use external systems No Discouraged Yes Multiple threads No Yes Yes Sleep statements No Yes Yes System properties No Yes Yes Time limit (seconds) 60 300 900+ Test Sizes – Google Testing Blog https://testing.googleblog.com/2010/12/test-sizes.html 74

75.

集約とリポジトリ • 集約とは、「データを⼊出⼒する情報の単位」のこと • リポジトリは、集約を永続化する抽象、コレクションのように設計する • 集約とリポジトリをうまく設計することで、インメモリリポジトリのような代替が作りやすくなる 集約 注⽂ リポジトリ 注⽂明細 注⽂明細 注⽂明細 DB 永続化・復元 https://www.shoeisha.co.jp/book/detail/9784798126708 75

76.
[beta]
Spring BootでのTestcontainersの使い⽅
任意のテスト⽤アノテーションと、
@Testcontainersをつける
@Testcontainers
@SpringBootTest
class SampleIntegrationTest {
@Container
@ServiceConnection
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:16");
@Test
void myTest() {...}
}

① 起動時に⾃動でコンテナを起動
② SpringBootの接続を切り替えて
⾃動で繋げてくれる
使⽤する
コンテナを宣⾔

https://spring.pleiades.io/spring-boot/reference/testing/testcontainers.html

76

77.
[beta]
Spring GraphQLの実装⽅法
スキーマ定義

データ解決⽤のコントローラー

type Query {
order(id: ID!): Order
}

@Controller
public class OrderGraphqlController {
@QueryMapping
public Order order(@Argument Long id) {
return orderService.findById(id);
}

type Order {
id: ID!
customer: Customer!
status: OrderStatus!
totalAmount: Int!
items: [OrderItem!]!
}
src/main/resources/graphql
/schema.graphqls

@SchemaMapping(typeName = "Order")
public Customer customer(Order order) {
return customerService.findByOrderId(order.id());
}
}

https://docs.spring.io/spring-boot/reference/web/spring-graphql.html
https://docs.spring.io/spring-graphql/reference/index.html
77

78.

その他参考リンク • Spring Bootのテスト https://spring.pleiades.io/spring-boot/reference/testing/spring-bootapplications.html • @Sqlアノテーション https://spring.pleiades.io/springframework/reference/testing/annotations/integration-spring/annotationsql.html • CQRSパターン https://learn.microsoft.com/ja-jp/azure/architecture/patterns/cqrs 78

79.

EOF 79