6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

実践しよう IBM Quarkus:IBM Bobと進めるSpring Bootからのマイグレーション

6
Posted at

はじめに

AIを活用したアプリケーション開発が本格化すると、企業で管理するアプリケーション数はこれまで以上のペースで増加する可能性があります。この変化に備えて特に意識すべきなのがランニングコストです。

メモリをはじめとするコンピューティングリソースの価格は高騰しており、稼働するアプリケーションの増加と掛け合わせると、必要なリソースコストは右肩上がりになると予想されます。

そのため、コンテナ化などの技術を駆使してITリソースを効率的に運用していく必要があります。
エンタープライズユースにおける標準的な言語はJavaといえますが、コンテナ利用を想定したJavaプラットフォームとしてはQuarkusが非常に適していると考えています。

以下の公式ベンチマーク記事にあるとおり、Quarkusは起動時間や初期メモリ消費量において、従来型のSpring Bootに比べ大幅な削減(半分程度)が見込めるという評価があります。また、ネイティブコンパイルに対応して高速起動が可能なことから、ゼロスケールを含めたオートスケーリングによるリソース利用の最適化にも向いています。

そのため、新規アプリケーションをQuarkusで構築するだけでなく、既存のSpring Bootアプリケーションを移行していくことも有効な選択肢となります。

そこでこの記事では、Spring BootからQuarkusへの移行が現実的な対応となりうるかを実際のコードを用いて検証していきます。

この記事で検証すること

公開されているSpring Bootアプリケーションを実際にQuarkusで動くようにマイグレーション(移行)します。その結果を以下の3つの観点で評価します。

  • 品質
  • 移行コスト
  • プロセスの透明性

なお、この記事の内容は IBM Bob 2.0 + Windows 11 環境にて検証を行っています(JDKやGit等のツール類は導入済み)。

IBM Bobとは?

IBM Bobは、コードベース上で開発者と協働し、ソフトウェア開発を支援するAIパートナーです。
コード補完だけでなく、アプリケーションの分析、設計、実装、レビュー、モダナイゼーションなど、開発ライフサイクル全体を支援します。

30日間の無料トライアルでは、IBM Bobの利用に必要な「Bobコイン」を40コイン利用できます。

Quarkusとは

Quarkusは、オープンソースで開発されているコンテナネイティブなJavaフレームワークです。
従来のJavaフレームワーク(Spring Bootなど)は起動時間が長く、メモリ消費が大きいという課題があり、これらはコンテナ/サーバーレス環境での運用と相性がよくありませんでした。

Quarkusはこの領域でJavaを最大限活用するために、以下の特徴を備えています(これらの特徴は非コンテナ環境での運用にも効果的です)。

  • MicroProfileをベースとした、コンテナ運用に適した必要十分な機能群
  • ネイティブコンパイルによる超高速起動
  • モダンアプリケーションを実現する豊富なエクステンション

IBM Enterprise Build of Quarkus

IBM Enterprise Build of Quarkus(以下、IBM Quarkus)は、コミュニティ版のQuarkusをベースに、エンタープライズ(企業向け)運用に必要なセキュリティ、長期サポート、AI連携機能を強化してIBMがパッケージ化したディストリビューションです。

この記事では、最終的にIBM Quarkus環境でアプリを起動します。

Spring Bootからの移行手順

Spring互換API+AIによる移行を選択します。

Springからの移行に関するノウハウは、公式の以下のページに集約されています。

2つの移行戦略

移行戦略には大きく分けて次の2パターンがあります。短期的に結果を出すために前者の選択も考えられますが、長期的な保守性を考慮すると後者の選択が適切です。

1. Spring互換APIを使用する

Spring互換APIは、Quarkus上でSpring風のアノテーションなどの記述を可能にする機能です。

既存の書き方のまま実装できるため移行コストを大幅に抑えられます。ただし、この互換機能はあくまで記述を似せるためのラッパーであり、完全な挙動の一致までを保証するものではありません。また、コード内にQuarkus標準APIとSpring互換APIが混在すると将来的な保守のハードルになります。長期運用を見据えるなら、まずは互換APIで移行し、段階的にQuarkusネイティブな実装へシフトしていくアプローチが現実的です。

2. QuarkusネイティブAPIを使用する

互換APIを使わず、Quarkus本来のネイティブ実装へ全面的に書き換える方法です。
実装スタイルがQuarkus基準で一貫するため、移行後の運用や機能追加でもコードの統一感を保てます。互換API特有の「微妙な挙動の違い」にハマる心配もなくなるため、長期的には最もクリーンでトラブルの少ないアプローチと言えます。

2つのアプローチ(決定論とAI)

マイグレーションを進めるにあたり、手作業での対応も可能ですがツールの活用が現実的です。
OpenRewriteなどの「決定論的アプローチ(ルールベース)」で自動変換するツール群がいくつか存在します。

もう一つの手段として、AIのコード解析能力とマイグレーションの知識ベースを生かして対処する方法があります。
ただし、単純にプロンプトで依頼するだけでは効率的な処理ができないため、適切に処理できるプロセスを定義してワークフロー化することで精度を向上させます。

migrate-spring-to-quarkus

AIでマイグレーションを行う際の一例として、上記記事で紹介されているスキルの利用が挙げられます。これはSpring BootからQuarkusへの移行を自動化・効率化するためのワークフロー定義です。

RealWorld(移行対象アプリケーション)

SPA方式で実装されているRealWorldというオープンソースのアプリを移行対象とします。

RealWorld」は、QiitaのようなCMSの機能を持つアプリケーションです。バックエンドはヘッドレス(API化)仕様となっており、フロントエンドが独立した構成になっています。
バックエンドにMyBatisやGraphQLを使用し、フロントエンドがSPAというモダンな構成のため、実際のエンタープライズアプリに近い現実的な構造を持っています。

今回は構成上、バックエンドのみが移行対象となります。

バックエンド

DBはSQLiteがデフォルトで使用されるため、別途DBコンテナ等を準備する必要はありません。

IBM Bobで計測した同アプリの規模感は以下のとおりです。皆様のマイグレーション対象アプリと規模を比較する際の目安としてご利用ください。

Bobへの指示
クラス数およびライン数、その他アプリの規模感が分かる数値を算出してください。
指標
Java ファイル総数 116(main 93 / test 23)
Java 総行数 5,181(main 3,396 / test 1,785)
クラス数 100(+ Interface 16 / Enum 1 / Annotation 4)
パッケージ数 20
REST APIエンドポイント 19
MyBatis XML(行数) 8ファイル / 493行

主なアーキテクチャ上の特徴は次のとおりです。モダンかつ実用的な設計であることが分かります。

Bobへの指示
このアプリのアーキテクチャ上の特徴をリスト化してください
  • DDDライクな Core層分離 — エンティティとリポジトリ Interface が io.spring.core に集約。エンティティはフレームワーク非依存の POJO
  • CQRSライクな読み書き分離 — 書き込みは CommandService + Repository、読み取りは QueryService + ReadService(JOIN SQL → DTO 直接マッピング)
  • REST+GraphQL デュアルAPI — 同一 Application Service を REST(Spring MVC)と GraphQL(Netflix DGS)が共有。GraphQL は Relay スタイルのカーソルページネーションを実装
  • MyBatisの2系統マッパー — Mapper(書き込み用)と ReadService(読み取り専用 DTO 投影)を用途別に分離
  • JWTステートレス認証 — JwtService を Core で抽象化、Infrastructure で実装。OncePerRequestFilter で全リクエストを検証
  • SQLite+Flyway — 組み込み DB でシンプルな構成。Joda-Time の型変換を TypeHandler でカスタム実装
  • BeanValidation+カスタム制約 — DB 重複チェックをバリデーション層(ConstraintValidator)に集約
  • Lombok@EqualsAndHashCode(of = {"id"}) でドメインオブジェクトの等値比較を ID ベースに統一
  • Spotless / Google Java Format — ビルド時にフォーマットを強制適用

フロントエンド

様々なフロントエンド実装が提供されていますが、今回はVue.jsによる実装を選択します。
マイグレーション作業自体には直接関わりませんが、バックエンドの動作確認(E2E的な検証)のために使用します。

移行プロセス

スキル登録後のマイグレーション指示は、わずか1行で開始できます。
実施前にあらかじめマイグレーション戦略を決めておきましょう。

バックエンドの取得(移行対象)

以下のコマンドで正常に起動することを確認します。

# 1. バックエンドリポジトリの取得
git clone https://github.com/gothinkster/spring-boot-realworld-example-app.git
cd spring-boot-realworld-example-app

# 2. 起動 (ポート8080で起動します)
./gradlew bootRun

フロントエンドの取得

以下のコマンドでセットアップを実施します。

git clone https://github.com/gothinkster/vue-realworld-example-app.git
cd vue-realworld-example-app
git submodule update --init --recursive
npm install

バックエンド(ポート8080)と接続するため、以下の2点を修正します。

1. .env.local ファイルの作成(vue-realworld-example-app 直下に配置)

.env.local
VITE_API_URL=http://localhost:8080

2. vite.config.js のポート変更(ポート重複を避けるため8080以外に変更)

vite.config.js
  server: {
    // Explicit IPv4 loopback: under bun, binding "localhost" can pick ::1
    // while Playwright's webServer readiness probe connects to 127.0.0.1.
    host: "127.0.0.1",
    port: 5173,
    strictPort: true
  }

上記修正が終了したら、次のコマンドで起動することができます。

npm run serve

起動後、ブラウザで http://localhost:5173 にアクセスすると初期画面が表示されます。

初期画面

Bobへのスキル登録

バックエンドのディレクトリで以下のコマンドを実行します。
使用している skills は、Vercelを中心に提供されているスキル用パッケージマネージャーです。今回は Quarkus 公式が提供している migrate-spring-to-quarkus スキルを追加します。

npx skills add quarkusio/skills --skill migrate-spring-to-quarkus

利用するエージェントの選択画面が表示されたら「IBM Bob」を選択します。
エージェント選択

スコープの選択では「Project」を指定します。
スコープ選択

インストール方法の選択では、推奨されている「Symlink」(1箇所に登録してプロジェクトへリンクを配置する形式)を選択します。
インストール方法選択

確認画面で「Yes」を選択して進めます。
確認画面

最後に find-skills(公開スキルを検索する追加機能)の導入確認が入ります。今回の作業には必須ではないため任意で追加してください。
find-skills確認画面

Bobによる移行作業

Bobを起動してアプリケーションのフォルダを開き、プロンプトで次の指示を出します。

Bobへの指示
このアプリをQuarkus用にマイグレーションしてください

これだけで自動的にマイグレーション処理がスタートします。

ワークフローに沿って、まずはコードの解析から始まります。
解析結果表示

解析後、先述した2つの移行戦略のどちらで進めるかのヒアリング(フォローアップ)があります。
今回は「Spring互換モード」を選択しました。
マイグレーション戦略

続いてGit操作の確認が行われます。今回は「現在のブランチでそのまま作業する」を選択しました。
Git戦略

デフォルト設定では変更・実行の承認リクエストが多数発生しますが:dizzy_face:、画面上で承認を進めていくだけでスムーズに処理が進行します。

フローの最後に「マイグレーションレポート」が生成されれば移行作業は完了です。

マイグレーションスキルのトラブルシュート

症状 対応
タスク制限に達しました アクションが大量に発生した場合、無限ループを防止するため自動停止します。
正常に動いている場合は「タスクを続行」をクリックすれば処理が再開されます。
OSコマンドで止まる テストフェーズ等でコマンド実行後に応答がなくなる場合があります。
その際は一度コマンドを停止し、Bobへ「さきほど停止したコマンドの状況をコンソール出力から確認し、問題を把握した上で適切に処理を継続してください」と指示を出してください。

IBM Quarkusへの切り替え

マイグレーションスキルは標準でコミュニティ版Quarkus向けの設定を出力するため、IBM Quarkusを使用するように追加指示を行います。

Bobへの指示
IBM Enterprise Build Of Quarkusを使用するように定義を修正してください

機能性はコミュニティ版と同等ですが、リソースの取得元などがIBM専用に切り替わります。今回はアプリがGradleを使用しているため、build.gradle などの定義ファイルが自動修正されます。

:warning: 今回の検証では Quarkus 3.9で変更されたCORSハンドリングの仕様変更 による動作不具合が発生しましたが、Bobにエラー状況を伝えると短時間で原因を特定・修正してくれました。

移行結果

品質:partly_sunny::Spring互換API周りで1点ハマりどころがありましたが、最終的には修正を完了して正常動作しました。
移行コスト:sunny::手動対応と比較してコスト・工数ともに大幅削減を達成。手間の少なさも際立ちます。
透明性:sunny::詳細なマイグレーションレポートが生成されるため、実施内容と残課題が一目で把握できます。

品質

マイグレーション完了後、フロントエンド経由でAPI接続を行い、期待どおりCRUD処理が動作するかを検証しました。

結果として、ランタイムエラーが2件、意図しない挙動(バグ)が1件発生しました。
いずれもBobとのやり取りで解決できましたが、最後の1件は原因特定が難しく、解決まで結構な回数Bobとのラリーが必要になりました:disappointed_relieved:

:bulb: 学び
Spring互換API利用時、リクエストの Authorization ヘッダーやJSONのパース処理で細かな挙動の差異が発生していました。「記述は似せられるが、完全な互換動作ではない」という点に気づきにくく、原因特定が遅れる原因となりました。
初手から「QuarkusネイティブAPI」を選択して移行を進める方が賢明かもしれません。

変更量の定量評価

移行直後のコード状態について、どれくらいの変更が行われたかをBobに分析してもらいました。

Bobへの指示
アプリのマイグレーションを行いました。フォルダ1は移行前、フォルダ2は移行後のリソースです。これらの類似度を定量的に算出してください。

分析結果は以下のとおりです。変更された行数を考慮すると、発生した問題の数は十分に少なかったと言えます。

類似度サマリー:

対象 類似度 概要
Java main 全体 60.8% diff 1,487行 / 元 3,798行
リソース (XML/SQL等) 85.8% DDL・マッパー XML・GraphQL スキーマは完全保持
テスト 30.1% テスト戦略を大幅改修

レイヤー別の特徴:

レイヤー 類似度 要因
core(ドメイン) 100% 完全無変更 — ビジネスロジックに一切手を入れていない
infrastructure 86.8% MyBatis 設定の軽微な整理のみ
application 79.9% UpdateUserConstraint/Validator 2 本追加が主な差異
api (Controller) 69.4% WebSecurityConfig の大幅改修が影響
graphql 13.3% DGS コード生成(graphql/types/ 21 本追加)と全 Datafetcher の再実装

移行コスト

Bobの利用にあたっては「Bobコイン」という課金単位を使います。契約によって異なりますが、概ね 1Bobコインは0.5ドルに相当します。この記事を記載時点のレートで換算すると1Bobコインは80円くらいになります。

今回のマイグレーションスキルの完了までに消費したコインは 122.16 Bobコイン でした。日本円に換算すると約1万円となります。

最終的な受入テストや評価の工数は別途必要ですが、主軸となるソースコード書き換え・設定変更タスクがこの価格で完了したのは驚異的です。

手動でコード変更を行った場合、規模的に数人日〜1人月相当(数十万〜百万円クラス)の工数を見込むのが一般的です。これと比較すると、約1万円というAI移行コストのROI(投資対効果)は極めて高いと言えます。

プロセスの透明性

マイグレーション完了時に出力されるレポートにより、どのような変更が行われたのかを詳細に確認できます。

特に以下の2つのセクションは、機能ごとのリスクや変更の集中箇所(ホットスポット)を把握するための重要な情報源になります。前述のレイヤー別変更率と組み合わせることで、重点的にテストすべきポイントを絞り込んだ効率的な検証計画が立てられます。

セクション 説明
Key Migration Decisions マイグレーションの実施方針・採用した仕様
Notable Issues Discovered 受入検証・テスト時に重点的に確認すべき要注意ポイント

なお、今回のレポートでは「Checks Passed」項目のうち1件(Starts up:起動検証)が失敗となっていました。
原因を確認したところ、移行前のプロジェクトがGradleの互換都合で JDK 17 環境のままになっており、JDK 21 前提で生成された今回のQuarkusアプリがそのままでは起動できなかったためでした。実行環境のJDKを21に切り替えることで正常起動を確認できました。

マイグレーション結果1 マイグレーション結果2 マイグレーション結果3

その他評価・反省点

Bobコインの消費内訳としては、「コード・設定更新」までに約1/3、「テスト実行および失敗時の修正やり取り」に残り2/3を消費した印象です。

マイグレーション完了後、Bobに対して振り返りの質問を行いました。

Bobへの指示
今回のマイグレーションではテストで試行錯誤が多かったです。反省点などがあれば教えてください。

Bobから返ってきたフィードバック(反省点)は以下のとおりです。事前にフレームワーク固有の仕様を知識として把握・指示しておけば、さらに効率よく処理できたことが分かります。

社内システムのように標準アーキテクチャや共通コンポーネントが統一されている場合、初回で得られた知見をプロンプトやプロファイルとして整理しておくことで、2回目以降の移行作業を劇的にスピードアップできる可能性があります。

問題 何が起きたか 改善策
Quarkus のテストアーキテクチャを事前に理解していなかった @QuarkusTest は abstract クラスから継承しても機能しないことを、実際にエラーが出るまで知らなかった
@InjectMock@Singleton スコープ(Spring @Service の変換先)に使えないことを知らずに進めた
@InjectMock を持つテストと DB テストが別 Quarkus コンテキストを起動するため競合することを見落とした
移行開始前に Quarkus テストガイド を読み、以下を確認してから進めるべきだった:

@QuarkusTest のコンテキスト共有ルール
@InjectMock の CDI スコープ要件(normal scope のみ)
@TestProfile による分離の必要条件
@Service@ApplicationScoped 変換を後回しにした コード移行時に @Service/@Repository を Spring 互換アノテーションのまま残した。これが @InjectMock: invalid bean scope: Singleton エラーの直接原因となり、テスト移行フェーズで大きく後退した。 コード移行(build/code モジュール)の段階で「テストでモックが必要になる Bean は全て @ApplicationScoped にする」というルールを最初から適用すべきだった。Spring 互換モードでも @ApplicationScoped は完全に使える。
MyBatis の XML 解決を過小評価した Quarkiverse MyBatis 2.x の XML 読み込みについて、以下を順番に試行してしまった:

・mapper-locations=mapper/.xml (glob 未対応)
・classpath:mapper/
.xml (動かない)
・xmlconfig モード(別コンテキスト問題)
・XML を @Mapper 同パッケージに配置(最終正解)
最初に quarkus-mybatis の 公式ドキュメントとソースコード(MyBatisProcessor.class)を確認すれば、「@Mapper インターフェースと同パッケージの XML が自動検出される」という仕様に早期にたどり着けた。
加えて TransferData.xml(共有 ResultMap)の存在も初期設計時に把握すべきだった。
テスト分離の設計が後手だった Spring Boot では @Transactional + @Rollback でテスト後に自動ロールバックされていた。Quarkus にはこの仕組みがないため、同じメールアドレスを使うテスト同士が UNIQUE 制約違反で干渉した。これも実際のテスト実行まで気づかなかった。 テスト移行フェーズの最初に「Spring のテスト分離機構(@Transactional + @Rollback)は Quarkus では動かない」を確認し、DbTestBase.cleanDb() のような代替策を設計段階で組み込むべきだった。
キャッシュへの対応が甘かった application.properties を変更しても UP-TO-DATE で古いビルドが使われ、「変更が効いていない」と誤診して同じ修正を繰り返す場面があった。 設定変更後は必ず ./gradlew clean してから test を実行する、もしくは --rerun-tasks フラグを使う習慣をつける。

おわりに

Spring BootからQuarkusへの移行手順とその検証結果について解説しました。

AIパートナー(IBM Bob)とマイグレーションスキルを活用することで、非常に軽快なステップで移行作業を進められることが確認できました。テストの修正やログの確認などで試行錯誤する時間もありましたが、1万円程度のコストと短期間で実用的なレベルまでマイグレーションできた成果は大きな魅力です。

次回以降は「最初からQuarkusネイティブAPIをターゲットにすること」「今回のテストでの反省点をナレッジとしてAIに事前共有すること」を徹底することで、より高精度かつ低コストな移行が可能になるはずです。

コスト最適化やコンテナ運用の高速化を目指す第一歩として、まずは小規模なアプリケーションからQuarkusへの「お引っ越し」を試してみてはいかがでしょうか。

6
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?