2023 年の振り返りと 2024 年の抱負
約 1 年前に 2023 年の抱負を書いていたので、それの振り返りと 2024 年の抱負を書こうと思う。
前回の記事は「お仕事」と「学習」に分けて振り返りをしていたが、お仕事に学習も含まれるので今回は特に分けずに振り返る。
2023 年の振り返り
2023 年も仕事では大きな転換があった。9 月に約 2 年間勤めたオイシックス・ラ・大地株式会社 (以下「前職」という) を退職し、合同会社 DMM.com (以下「現職」という) へ転職した。ネガティブな理由も少しあったが、最も大きな理由として、より技術的な成長をするために転職を決断した。
現職に転職してからは、今までにも経験があったレガシーアプリケーションの刷新プロジェクトにアサインされている。運良くプロジェクトの開始のタイミングで入社することができたこともあり、Web フロントエンドチームの開発リーダーというポジションに就いている。チームは 2023 年末時点で私も含め 5 人のメンバー + マネージャーで構成されている。フロントエンド以外も含めると、ざっと 50 人以上 (もっと多いかも) が関わる大きなプロジェクトである。
新フロントエンドの技術選定、アーキテクチャ設計、初期コードの実装を行い、リニューアル対象のプロトタイピングを行い、早期に課題を発見し改善しながらリリースの準備を進めている。重要な意思決定で 1 度決定すると変更することが大きなコストになるものは ADR を作成するなどにも注力している。また、チームのロードマップを作成しそれの管理をするなど、役割を与えられたうえでの管理的な業務は初めてなので、良い経験もさせてもらっている。何より忖度なく意見を出し合えるメンバーと楽しく仕事ができている。
前置きが長くなったが、2023 年はフワッと以下を目標としていた。
- フロントエンド周辺技術のインプットを継続する
- ソフトウェア開発手法を学習して実践する
- プログラミング言語 Rust に入門する
- Rust で Web アプリケーションを作る
フロントエンド周辺技術のインプットを継続する
フロントエンド周辺技術については、プロジェクトで利用する OSS の学習をする時間が多かった。インプットもできたが、(特に後半は) 今までの経験をもとにアウトプットするシーンが多かった。改めて振り返ってみると、必要最低限のインプットしかできていない気もする。
ソフトウェア開発手法を学習して実践する
ソフトウェア開発手法については、スクラムガイド を読んだくらいであまり時間をとって学習はできていない。開発リーダーを任されている身なので、もう少し学習が必要だとは感じている。
プログラミング言語 Rust に入門する
前職時代に、バックエンド開発チームに異動できると聞いていたので、前半は Java や Kotlin を学習しながら簡単な Web アプリケーションを作っていたが、異動計画が大人の事情で延期され、そのまま転職したので Rust に入門することができた。
私は全くの素人だが、現職では Rust 勉強会を主催 (といっても大したことはしていない) している。TRPL を輪読しており、計 5 回ほど開催した。また、個人でも書籍を読み、Rust のコンセプトや基本的な構文を学習した。
Rust で Web アプリケーションを作る
作るまで至っていない。
読んだ書籍
部分的に読んだ書籍は省略する。
これは何で読んだのだろう。読んだ後に GitHub Actions でええやん。と思った記憶がある。
2 年くらい前に読んだことがあったが、実践していないので全く覚えていなかった。
前職での異動を期待してウキウキで読んだ。
上と同じ。悲しい結末になった。
前職は Vue.js で、現職は部署によって異なるが主に React が使われているとのことだったのでキャッチアップをした。
TRPL と合わせて読んだ。コンセプトから理解するというアプローチが個人的に好きで読みやすかった。
上記以外にも自動テストや TypeScript の書籍などを部分的に読んだ。2023 年は転職活動やお仕事が忙しく、あまり書籍を読むことができなかった。2024 年はもっと読みたい。
2024 年の抱負
相変わらず Web バックエンド (というよりフロントエンド以外) に対する興味が強い。現職でフロントエンドチーム以外の方とも接することが増えたが、プロジェクトを進めるうえでフロントエンドだけの知識では不足している感じるシーンが多い。フロントエンド以外の学習については損得をあまり考えず、バックエンドに限らずインフラ領域など、その時必要な知識や興味がある分野を学習してアウトプットすることを抱負にしたい。
- お仕事のプロジェクトを計画通り進める (だいじ..)
- お仕事でも個人でも、とにかくコードや文章を書く
- フロントエンド周辺技術のインプットを継続する
- Rust の勉強を継続する
いつも通り抽象的な抱負だが誰も見ることがないだろうから インターネッツ へ公開してみようと思う。終わり。
Kotlin + Spring Boot + Doma2 の環境を構築する
私は「現職で利用されているから」という理由で Java の学習を始めた。しかし、現職のメインの開発言語は Java から Kotlin へ移行している。学習開始頃の私は、メイン言語である Kotlin の学習始めたが、Java 周辺技術やオブジェクト指向プログラミングの知識などが乏しく、とりあえず Java を触ろうと考えていたのだった。それを最近思い出した。
前回、書籍「ユースケース駆動開発実践ガイド」に登場する ICONIX プロセスを用いて、すごく簡単に Java 製 REST API の設計をしてみた。Java で作る前提で設計していたが、当初の目的に立ち返って Kotlin で作ることにした。
余談
記事タイトルから逸れるが、Java と Spring Boot を利用してプロトタイプを作った際に読んだ以下の書籍について、雑なメモが残っていたので思い出すために整理する。
この書籍は、Spring Framework をはじめ、Spring Web や Spring Security など、Spring で扱う主なプロジェクトの紹介から始まる。その後、DI の必要性など、最低限必要な前提知識の説明があり、ハンズオン形式で Spring Boot を用いた Web アプリケーションを作成していく。
Spring 周辺のエコシステムは、長い歴史からあまりに大きいため、どのプロジェクトがどの様に関連しているのか、何をどの様に利用すればよいのか初学者には把握が難しいと思うが、この書籍を読むことで Starter の存在や、関連や仕組みなどを何となく把握できる。
一方で、Bean という概念についての説明はされていないが、「Spring Boot における Bean 定義について理解することは非常に重要だ」と書かれていて、そこから先を読むために、Bean って何やねんを別途理解する必要があった。Spring Boot が裏でいい感じにしてくれていると思いながら付与していた @Service
アノテーションや @Controller
などは、クラスを Bean として登録するための @Component
というアノテーションを利用している。つまり、@Service
アノテーションを付与すると、その中で @Component
を利用して Bean に登録され、Spring Boot アプリケーションの中で再利用できる様に ApplicationContext という箱に詰められ、私たち開発者は意識することなく利用できていたらしい(大まかにしか理解していない)。Spring Boot は便利すぎるため、知ろうとしないと中で何が起きているのか分かりにくいので、必要に応じて調べようと思う。
Kotlin 入門 + Doma2 の設定
Java もオブジェクト指向も Spring もよく分からない頃、とりあえず現職のバックエンド技術に合わせて読んだ書籍「Kotlin サーバーサイドプログラミング実践開発」を再度読んでみた。
当時は読んで書いてその通り動かすことはできたがわりと難しく感じた。Java や Spring を少し学習して改めて読むと以前と比較して難しさは感じていない。
とりあえず Kotlin の基本的な機能などの説明を読んで、試しに書いて動かして、本格的にアプリケーションを作成する 6 章の冒頭まで読んだ。このハンズオンでは O/R マッパーに MyBatis を利用しているのだが、私は Doma2 を利用したかったので、紹介されている構成と変えながら進めている。
Doma2 の公式ドキュメントには、サンプルプロジェクトのリンクがあるだけで、詳しいインストール方法は書かれていない。少し調べながら環境を作ったのでメモ的に簡単にまとめる。Node.js ベースの環境構築は慣れているが、Java や Kotlin の環境構築は分からないことが多い。
私は今回 Kotlin, Spring Boot, Gradle の構成なので、README.md を見ながら build.gradle.kts
に以下の依存を追加した。
dependencies { implementation("org.seasar.doma:doma-kotlin:2.53.3") kapt("org.seasar.doma:doma-processor:2.53.3") }
ここで唐突に kapt が登場するが、plugins に kapt の宣言をする必要があるらしい。
plugins { kotlin("kapt") version "1.8.21" }
よく見ると README.md の下の方の Related projects に doma-spring-boot のリンクがあり、これの README.md を見て以下を追加した。
dependencies { ... implementation("org.seasar.doma.boot:doma-spring-boot-starter:1.7.0") }
ここまでの設定で利用できそうに思うが、正しく動作しなかった。私は DAO Style かつ SQL テンプレート?を利用する前提で、ChatGPT 先生や、Google 先生に質問すると、以下の様な記述が必要だと教えてくれた。
val compileKotlin: KotlinCompile by tasks kapt { arguments { arg("doma.resources.dir", compileKotlin.destinationDirectory) } } tasks.register("copyDomaResources", Sync::class) { from(sourceSets.main.get().resources.srcDirs) into(compileKotlin.destinationDirectory) include("doma.compile.config") include("META-INF/**/*.sql") include("META-INF/**/*.script") } tasks.withType<KotlinCompile> { dependsOn(tasks.getByName("copyDomaResources")) ... }
公式ドキュメントに同様の説明が見当たらなかったので、疑いながら調べていると doma-compile-plugin という素晴らしいプラグインを発見した。これは、上記の様なコンパイルの設定をしてくれるプラグインらしい。よく見るとこれも Related projects にリンクがある。ここまで調べるのに時間がかかった。これは気付けないよと思いながらプラグインとして追加した。
plugins { ... id("org.domaframework.doma.compile") version "2.0.0" }
最終的な build.gradle.kts
は以下の様になった。
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile plugins { id("org.springframework.boot") version "2.7.11" id("io.spring.dependency-management") version "1.0.15.RELEASE" id("com.diffplug.spotless") version "6.18.0" kotlin("jvm") version "1.6.21" kotlin("plugin.spring") version "1.6.21" // Doma のために追加した kotlin("kapt") version "1.8.21" id("org.domaframework.doma.compile") version "2.0.0" } group = "x" version = "0.0.1-SNAPSHOT" java.sourceCompatibility = JavaVersion.VERSION_11 repositories { mavenCentral() } dependencies { implementation("org.springframework.boot:spring-boot-starter-web") implementation("com.fasterxml.jackson.module:jackson-module-kotlin") implementation("org.jetbrains.kotlin:kotlin-reflect") // Doma のために追加した implementation("org.seasar.doma:doma-kotlin:2.53.3") kapt("org.seasar.doma:doma-processor:2.53.3") implementation("org.seasar.doma.boot:doma-spring-boot-starter:1.7.0") implementation("mysql:mysql-connector-java:8.0.32") testImplementation("org.springframework.boot:spring-boot-starter-test") } tasks.withType<KotlinCompile> { kotlinOptions { freeCompilerArgs = listOf("-Xjsr305=strict") jvmTarget = "11" } } tasks.withType<Test> { useJUnitPlatform() } spotless { kotlin { ktlint() } }
諸事情により Java11 を利用しているが、17 やそれ以降も使えるらしい。
Spring Boot を BootRun すると、Docker を利用して立ち上げた MySQL から値を取得することができた。Doma2 を利用するために、複数のプラグインや依存が必要なのが難しく感じた。
これから
書籍「Kotlin サーバーサイドプログラミング実践開発」の 6 章以降を参考に、前回作った設計の実装を進めていく。
そういえば、Java は VSCode で書けたけが、さすがに Kotlin を書くには限界を感じたので IntelliJ IDEA に移行した。
ICONIX プロセスによる Java 製 REST API の設計
先日 Java と Spring Boot を使って REST API のプロトタイプを作った。さらに、書籍「ユースケース駆動開発実践ガイド」で登場する ICONIX プロセスを参考にユースケース記述の工程までを試してみた。
ICONIX プロセスを用いた設計も Java も Spring Boot も学習中なので、いろいろ雑だと思うが学びの記録として続きの工程を簡単にまとめてみる。ICONIX プロセスは工程を進めながらイテレーティブに各図を洗練されていく様なので、上記記事でのアウトプットも一部変更して再度この記事に貼り付ける。
紙芝居
作ろうとしているアプリケーションの画面構成を簡単に表現している。
機能要求
アプリケーションができることは何かを定義する。
- ユーザーは node を登録できなければならない
- node には名前を登録できなければならない
- root 以外の node は 1 つの parent node を登録しなければならない
- node は child node を任意個数登録できなければならない
- node と node は edge で結ばれていなければならない
- ユーザーは node の内容を編集できなければならない
- ユーザーは node を削除できなければならない
- ユーザーは tree を閲覧できなければならない
用語集
機能要求やユースケースで登場する用語の説明をする。
用語 | 説明 |
---|---|
node | ・システムに登録することができる最小単位の要素 |
edge | ・node と node を結び付ける要素 |
tree | ・node と edge で構成される node の集合体 |
root | ・最上流に位置する node |
parent node | ・ある node と edge で結ばれ上流側の node ・各 node にとって parent node は単一となる |
child node | ・ある node とedge で結ばれた下流側の node ・各 node には任意個数の child node を持つことができる |
ドメインモデル
用語集に登場する概念らの関係を整理・視覚化する。
ユースケース図
このアプリケーションのユーザーが、このアプリケーションを使用して何ができるのかを表現している。
ユースケース記述
ユースケース図をより具体化するためにユーザーとアプリケーションの対話を記述する。本来ならユースケースの代替コース(例外)も記述するべきだが面倒なので省略する。
- node を登録する
- ユーザーはページへアクセスする
- システムは tree を表示する
- ユーザーは node の右上の「・・・」ボタンを押下する
- システムはドロップダウンメニューを表示する
- ユーザーは「Add child node」ボタンを押下する
- システムは child node を作成し、作成された child node の名前を入力するための input 要素にフォーカスする
- ユーザーは node の名前を入力しフォーカスアウトする
- node の名前を編集する
- ユーザーはページへアクセスする
- システムは tree を表示する
- ユーザーは node の右上の「・・・」ボタンを押下する
- システムはドロップダウンメニューを表示する
- ユーザーは「Edit」ボタンを押下する
- システムは node の名前を入力するための input 要素にフォーカスする
- ユーザーは node の名前を変更しフォーカスアウトする
- parent node を編集する
- ユーザーはページへアクセスする
- システムは tree を表示する
- ユーザーは node の右上の「・・・」ボタンを押下する
- システムはドロップダウンメニューを表示する
- ユーザーは「Change parent node」ボタンを押下する
- システム parent node にしたい node の選択(クリック)を促すメッセージを表示する
- ユーザーは任意の node をクリックする
- システムは選択された parent node と当該 node を結びつける
- node を削除する
- ユーザーはページへアクセスする
- システムは tree を表示する
- ユーザーは node の右上の「・・・」ボタンを押下する
- システムはドロップダウンメニューを表示する
- ユーザーは「Delete」ボタンを押下する
- システム node を削除し、child node がある場合は削除した node の parent node と child node 結びつける
- tree の参照
- ユーザーはページへアクセスする
- システムは tree を表示する
アーキテクチャ
オニオンアーキテクチャの思想をベースにしている。
レイヤ | 役割 |
---|---|
Domain | ・Entity ビジネスの概念やルールを表現したドメインオブジェクト・ Repository ドメインオブジェクトの取得や更新を表現したインターフェース |
Service | ・Service ユースケースを実現するアプリケーション固有のロジック |
UI | ・Controller リクエストを解釈しレスポンスを生成するオブジェクト |
Infrastructure | ・Repository いわゆる Data Access Object |
ロバストネス図
ユースケースとオブジェクトを関連付ける。この記事の最後にも感想を書いたが、この工程が難しいと感じた。次の工程であるシーケンス図の作成にあまり役にたたなかった。
1.node を登録する
2.node の名前を編集する
3.parent node を編集する
4.node を削除する
5.tree の参照
クラス図
アーキテクチャをベースにクラスに対する責務を割り当てる。
シーケンス図
ユースケースの振る舞いを、オブジェクトがどの様に達成するのかを示す。すなわち、ユースケース毎にシーケンス図を書くのが望ましいが、面倒になってきた参照と登録さえ設計できていれば、他のユースケースも同じ様に実装できそうなので一部省略している。
5.tree の参照
1.node を登録する
データベース設計
約 2 年ほど前に購入して読んでいた書籍「達人に学ぶDB設計 徹底指南書 ~初級者で終わりたくないあなたへ」を読んでテーブルに格納する値の例と ER 図を考えてみた。
このアプリケーションは木構造でデータを管理するのだが、この書籍を読んでリレーショナデータベースが木構造でデータを格納するのが苦手なことを知った。
tree
tree_id(主キー) | tree_name |
---|---|
TREE1 | 動物 |
TREE2 | Foo |
node
node_id(主キー) | node_name | parent_node_id | tree_id | user_id |
---|---|---|---|---|
NODE-A1 | 動物 | TREE1 | USER1 | |
NODE-A2 | 脊椎動物 | NODE-A1 | TREE1 | USER1 |
NODE-A3 | 無脊椎動物 | NODE-A1 | TREE1 | USER1 |
NODE-A4 | 哺乳類 | NODE-A2 | TREE1 | USER1 |
NODE-B1 | Bar | TREE2 | USER2 |
user
すぐに利用する予定はないが、システムとして当然必要なので作成しておく。
user_id(主キー) | user_name |
---|---|
USER1 | tarou |
USER2 | jirou |
ER 図
感想
書籍「ユースケース駆動開発実践ガイド」には、先にシーケンス図から作成し、その後(その都度?)クラス図を整理する様に書かれていたが、私は先にクラス図を作成する方が好みだった。これは今までの経験に依存するのかもしれない。
ロバストネス分析が難しい。私が書いたロバストネス図は、アプリケーションのユースケースの細かい振る舞いは表現している。しかし、ここで作りたいのはバックエンドアプリケーションなので、「ボタンを押下する」などのコントローラーはシーケンス図を作成するためには不要だった。ロバストネス図からシーケンス図がかなり飛躍しているので、うまく活用できていないのだろう。正直ロバストネス分析は不要だと感じている。ICONIX プロセスに関する記事などを読むと、ロバストネス分析が ICONIX の肝である様に書かれてあったが、個人的には、以下のアウトプットでも(もちろん開発対象によるとは思うが)充分だと感じた。
どんなに小規模なアプリケーションであっても、ドメインモデルからアーキテクチャの検討までは実施したい。考慮できていない仕様に気付くことができるので有用だと思う。
これから
本当は書籍「Web APIの設計」を読んで API の設計をしたいのだが、お仕事が忙しかったのと体調不良で、プロトタイプ作成から時間があき過ぎている。そろそろ Java と Spring Boot の知識が脳内から消えていきそうなので実装に入ろうと思っている。
途中で変更するかもしれない。ChatGPT 先生に頼ることになりそうだ。
Java 製 REST API のプロトタイピング
先日 Java の開発環境と CI 環境を作ったので、次のステップとして Java と Spring Boot を使って REST API のプロトタイプを作ってみた。
また、書籍「ユースケース駆動開発実践ガイド」で説明されている ICONIX プロセスを参考にアプリケーションの機能要求やユースケースなどを洗い出した。プロトタイプを作りながらアーキテクチャも検討したので学びの記録として簡単にまとめてみる。
Java も Spring Boot も REST API も SQL も学習中なので、突っ込みどころは多くあるだろうが実際のプロトタイプのリンクを貼っておく。
紙芝居
作ろうとしているアプリケーションの画面構成を簡単に表現した。
機能要求
システムができることは何かを定義する。
- ユーザーは node を登録できなければならない
- node には名前を登録できなければならない
- root 以外の node は 1 つの parent node を登録しなければならない
- node は child node を任意個数登録できなければならない
- node と node は edge で結ばれていなければならない
- ユーザーは node の内容を編集できなければならない
- ユーザーは node を削除できなければならない
- ユーザーは tree を閲覧できなければならない
用語集
機能要求やユースケースで登場する用語の説明をする。
用語 | 説明 |
---|---|
node | ・システムに登録することができる最小単位の要素 |
edge | ・node と node を結び付ける要素 |
tree | ・node と edge で構成される node の集合体 |
root | ・最上流に位置する node |
parent node | ・ある node と edge で結ばれ上流側の node ・各 node にとって parent node は単一となる |
child node | ・ある node とedge で結ばれた下流側の node ・各 node には任意個数の child node を持つことができる |
ドメインモデル
これは用語集に登場する概念らの関係を視覚化した図で、この図を作る作業はドメインモデリングと呼ばれている。
ユースケース図
このアプリケーションのユーザーが、このアプリケーションを使用して何ができるのかを表現している。
ユースケース記述
ユースケース図をより具体化するためにユーザーとアプリケーションの対話を記述する。
本来ならユースケースの代替コース(例外)も記述するべきだが面倒なので省略する。
- node を登録する
- ユーザーはページへアクセスする
- システムは tree を表示する
- ユーザーは node の右上の「・・・」ボタンを押下する
- システムはドロップダウンメニューを表示する
- ユーザーは「Add child node」ボタンを押下する
- システムは child node を作成し、作成された child node の名前を入力するための input 要素にフォーカスする
- ユーザーは node の名前を入力しフォーカスアウトする
- node の名前を編集する
- ユーザーはページへアクセスする
- システムは tree を表示する
- ユーザーは node の右上の「・・・」ボタンを押下する
- システムはドロップダウンメニューを表示する
- ユーザーは「Edit」ボタンを押下する
- システムは node の名前を入力するための input 要素にフォーカスする
- ユーザーは node の名前を変更しフォーカスアウトする
- parent node を編集する
- ユーザーはページへアクセスする
- システムは tree を表示する
- ユーザーは node の右上の「・・・」ボタンを押下する
- システムはドロップダウンメニューを表示する
- ユーザーは「Change parent node」ボタンを押下する
- システム parent node にしたい node の選択(クリック)を促すメッセージを表示する
- ユーザーは任意の node をクリックする
- システムは選択された parent node と当該 node を結びつける
- node を削除する
- ユーザーはページへアクセスする
- システムは tree を表示する
- ユーザーは node の右上の「・・・」ボタンを押下する
- システムはドロップダウンメニューを表示する
- ユーザーは「Delete」ボタンを押下する
- システム node を削除し、child node がある場合は削除した node の parent node と child node 結びつける
- tree の参照
- ユーザーはページへアクセスする
- システムは tree を表示する
アーキテクチャ
オニオンアーキテクチャの思想をベースにした。
Node.js のフレームワークである Express を利用して似た構成で簡単なアプリケーションを作ったことがあるが、それに比べると DI(dependency injection)が魔法のように簡単に実現できて感動した。
レイヤ | 役割 |
---|---|
Domain | ・Entity ビジネスの概念やルールを表現したドメインオブジェクト・ Repository ドメインオブジェクトの取得や更新を表現したインターフェース |
Service | ・Service ユースケースを実現するアプリケーション固有のロジック |
UI | ・Controller リクエストを解釈しレスポンスを生成するオブジェクト |
Infrastructure | ・Repository いわゆる Data Access Object |
これから
この後どの様にアプリケーション開発を進めていくか予定を立てる。
書籍「達人に学ぶDB設計 徹底指南書 ~初級者で終わりたくないあなたへ」を読んで DB 設計をする。この書籍は約 2 年ほど前に購入して読んでいるが、あまり活用する機会がなく忘れているのでもう 1 度読む。
Gradle に Java ライブラリを追加すると cannot be resolved と怒られた
build.gradle
の dependencies
に Spring Boot Starter Validation を追加した。
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-validation'
}
これを import jakarta.validation.constraints.NotBlank;
してみると依存を解決できないと怒られた。
The import jakarta.validation cannot be resolvedJava(268435846)
Spring Boot Starter Validation を追加すると、jakarta.validation.constraints.XX
が利用できるようだが、私の環境では上手く利用できないので調べてみる。
仮説
Spring Boot Starter Validation が Jakarta Bean Validation に依存しているから、私たち利用する側は明示的に追加する必要がないものだと勝手思っている。
であれば、追加したのに依存が解決されていない原因は大きく分けると以下の 2 つであると考えた。
- プロジェクトに正しくライブラリを追加できていない
- エディタがライブラリを認識できていない
検証
1 から順に確認する。
1. プロジェクトに正しくライブラリを追加できていない
依存をリフレッシュするコマンドを実行した。
./gradlew build --refresh-dependencies
この後、以下のコマンドで依存関係を表示してみた。
./gradlew dependencies
すると、Spring Boot と同じバージョンの org.springframework.boot:spring-boot-starter-validation -> 3.0.2
がツリーの中に表示されいたので、おそらく正しくプロジェクトには追加されているのだろう。
また、仮説の通り Jakarta Bean Validation と思われるライブラリも表示されている。
compileClasspath - Compile classpath for source set 'main'. | # ..省略 \--- org.springframework.boot:spring-boot-starter-validation -> 3.0.2 +--- org.springframework.boot:spring-boot-starter:3.0.2 (*) +--- org.apache.tomcat.embed:tomcat-embed-el:10.1.5 \--- org.hibernate.validator:hibernate-validator:8.0.0.Final +--- jakarta.validation:jakarta.validation-api:3.0.2 +--- org.jboss.logging:jboss-logging:3.4.1.Final -> 3.5.0.Final \--- com.fasterxml:classmate:1.5.1
ここで追加したライブラリはどこに追加されているのか気になったので調べてみた。
Gradle の公式ドキュメントを参照すると、$USER_HOME/.gradle
の modules-2
に依存ライブラリが格納されていると書かれている。
├── caches │ ├── 4.8 │ ├── 4.9 │ ├── ⋮ │ ├── jars-3 │ └── modules-2 # <-
私は Docker の中に開発環境を構築していたので、Docker Container に接続して確認してみると、確かにライブラリが追加されていた。
ls ~/.gradle/caches/modules-2/files-2.1/org.springframework.boot/spring-boot-starter-validation/ # 3.0.2
プロジェクトにライブラリは追加されている様だ。
2. エディタがライブラリを認識できていない
VSCode の拡張機能 Extension Pack for Java によって表示されている JAVA PROJECTS の Project and External Dependencies を見てみると、パスがローカル PC を指していることに気付いた。
ソースコードを Docker Container にバインドマウントした状態で、Docker Container に接続して Gradle のコマンドを実行していたので、あたかも Docker 内で開発している気分だったが、VSCode は Container には接続していないので当然 Conainer の中のライブラリは参照できなかったのだ。
今まではローカル PC に同じバージョンの Spring Boot 等のライブラリがたまたま存在していたから、VSCode 上でも補完が効いていたのだろうか。そして今回ローカル PC に存在しないライブラリを追加したタイミングで初めて問題になったのだろうか。
解決
原因が分かったので VSCode を Docker Container に接続するために、今まで存在は知っていたが必要ないだろうと思っていた VSCode Dev Containers を設定することにした。
docker-compose.yml
や Dockerfile
をそのまま利用できてすぐに環境を作ることができた。
とりあえず最小限の設定を書いた。
// .devcontainer/devcontainer.json { "name": "plantree-dev-container", "dockerComposeFile": [ "../docker-compose.yml" ], "service": "workspace", "remoteUser": "devuser", "workspaceFolder": "/home/devuser/workspace", "customizations": { "vscode": { "extensions": [ "vscjava.vscode-java-pack", "vscjava.vscode-gradle" ] } } }
Dev Container を起動した状態で JAVA PROJECTS とやらを確認すると、パスが Docker Container の中のライブラリを指していた。また、ローカル PC には無かった Spring Boot Starter Validation を認識していた。
エディタ上で jakarta.validation.constraints.NotBlank
を import することができた。さらに、Dev Container を利用することで、Gradle のタスクを VSCode から実行できるようになった。すごく便利だ。
かなり初歩的なことが原因だったが、Java や Gradle について分からないことが多いので問題の切り分けに時間がかかってしまった。
Java の開発環境と CI 環境を構築する
先日、2023 年の抱負として Rust を学習しながら Web アプリケーションを作ると宣言をした。この時はやるべきよりやりたいを優先しようと思っていた。
記事を書いた数日後に現職でバックエンド開発チームへ転向できる可能性があることを聞いた。マネジメント層に対して以前からバックエンド開発に関心があることは伝えており、日々の業務でもある程度評価していただいている様でチャンスがあるとのことだった。
そのため Rust 学習計画は中断し、チャンスに備えて現職で利用されている Java を用いた Web アプリケーションを作る計画に変更した。2023 年の抱負を書いて早々に興味ドリブンで学習をする方針を変更することになったが、業務で経験できるチャンスを逃さないためにできる準備をしておこうと思う。
昨年、書籍「プロになる Java」を読んで、 JVM や JDK などを含む Java 自体の基礎知識と基本的な文法を学習し、手元の PC にインストールした H2 Database とやりとりをする簡単な ToDo アプリケーションを作成している。なので、次のステップとしてもう少しマシなアプリケーションを作りながら、自分が知らないことをその都度学習することをしばらくの目標にする。
Java の開発環境を作ってみる
開発環境に対する理解は必須だと思うので、まずは環境を作成して開発の準備から始めようと思う。
まず周辺ツールのトレンドが気になったので調べてみた。
これは JetBrains 社が Java 開発者に対して行った調査で、Java のどのバージョンを利用しているか、何のビルドシステムを利用しているか等の調査結果が記載されている。
ツールそれぞれ良し悪しがあるのだろうが、調べているとキリがなさそうだったので、とりあえず以下の構成にした。
ちなみにエディタは VSCode を利用しようと思っている。Java 開発には IntelliJ IDEA が最も多く利用されている様で、私も書籍「プロになる Java」を読むタイミングで利用を始めたが、昔から使い慣れている VS Code の方が書きやすかったのが理由だ。
以下の VS Code 拡張機能を入れたが、初学者の私にとってはインテリセンスなど IntelliJ IDEA と比較しても遜色ない様に感じている。Web で提供されている Spring Initializr を利用しなくても VS Code 上で簡単にプロジェクトを作成できて感動した。
VS Code の拡張機能などについては WEB+DB PRESS Vol.124 を参考にした。
ローカルの開発環境は Ubuntu をベースに Docker イメージを作成した。ユーザ追加など諸々を行った後に SDKMAN! CLI をインストールして、SDKMAN! で Java と Gradle をインストールしている。
# docker/workspace/Dockerfile FROM ubuntu:22.04 ARG USER_NAME=default ARG USER_GROUP_NAME=workspace ARG PKG="git vim curl unzip zip sudo" SHELL ["/bin/bash", "-c"] RUN apt update \ && apt install -y ${PKG} \ && groupadd ${USER_GROUP_NAME} \ && useradd --shell /bin/bash -m ${USER_NAME} \ && echo %${USER_GROUP_NAME} ALL=\(ALL\) NOPASSWD:ALL > /etc/sudoers.d/${USER_GROUP_NAME} \ && chmod 0440 /etc/sudoers.d/${USER_GROUP_NAME} ARG JAVA_VERSION=17.0.5-ms ARG GRADLE_VERSION=7.5 RUN su ${USER_NAME} --command \ 'curl -s "https://get.sdkman.io" | bash \ && source "${HOME}/.sdkman/bin/sdkman-init.sh" \ && sdk install java "${JAVA_VERSION}" \ && sdk install gradle "${GRADLE_VERSION}"'
Docker Compose はとりあえず最低限書いた。
# docker-compose.yml version: "3.9" services: workspace: container_name: plantree-workspace env_file: .env build: context: . dockerfile: docker/workspace/Dockerfile args: USER_NAME: ${USER_NAME} USER_GROUP_NAME: ${USER_GROUP_NAME} tty: true volumes: - type: bind source: . target: /home/${USER_NAME}/workspace working_dir: /home/${USER_NAME}/workspace ports: - 8080:8080
docker compose up
からの docker compose exec
でワークスペースへ接続して、ユーザーを切り替えた後 ./gradlew build
でプロジェクトをビルドできる様にした。これがベストプラクティスなのかは分からないが、他の PC でも同じ環境でビルドや実行をできる様になった。
Java アプリケーションの CI 環境を作ってみる
継続的インテグレーション(以下 CI と呼ぶ)を実施するために書籍「[改訂第3版]Jenkins実践入門―ビルド・テスト・デプロイを自動化する技術」を読んだ。
冒頭は CI の概念やメリット・デメリットなどが解説されている。これはフロントエンドにもバックエンドにも共通する知識で、すでにある程度意識して業務で実践できていた部分なのでスラスラと読んだ。
Jenkins を使って Java アプリケーションの CI を実現するために、テストやインスペクションなどで広く利用されているツールも紹介されていた。
しかし、今回はもう少しお手軽に CI を実現したかったので、Jenkins は利用せず GitHub Actions を利用することにした。以下を参考にした。
そもそも Java プロジェクトのビルドはどの様なタスクで構成されているのか簡潔に説明されており、Java におけるビルドの具体的な役割イメージすることができる良い書籍だった。 また、CI の旨味なども丁寧に説明されていたので、先の Jenkins 本を買わずに無料で読めるこちらだけ読んでも充分な気がする。
ほとんど書籍そのままだが、Workflow の実行タイミングや、uses: XX
のバージョンなど少しだけ変えた。
# .github/workflows/build.yml name: Build on: pull_request: branches: - "**" push: branches: - main jobs: build: runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v3 - uses: gradle/wrapper-validation-action@v1 - name: Set up Java 17 uses: actions/setup-java@v2 with: distribution: microsoft java-version: 17 cache: gradle - run: ./gradlew build
実際に GitHub Actions の上でビルドが成功した。取り急ぎなので必要に応じてチューニングしていこうと思う。
以上でシンプルな開発環境と CI 環境を構築できたので、ついに開発を始めていこうと思う。Java 初学者が調べながら書いた文書なので不適切な説明などあるかもしれない。
2022 年の振り返りと 2023 年の抱負
2023 年になり 20 日も経過しているが、2022 年の振り返りと 2023 年の抱負を書き記そうと思う。
ここ最近自分が目指す方向性を見失っている気がするので、この記事を書くことで少しでも解消できると嬉しい。
2022 年の振り返り
お仕事
仕事では大きな転換があった。今までは創業から 20 年以上運用されてきたレガシーアプリケーションのフロントエンド刷新チームに所属していたが、2022 年 7 月からは社内基幹システムのフロントエンド開発チームへ異動となった。
社内基幹システムは新規開発だったので、機能開発に加えてビジュアルリグレッションテストや Web API のモックツールの導入など他にもいろいろ環境構築系のタスクを消化した。デプロイ・リリースの運用方針や、テスト戦略などをゼロから策定して実際にチームで運用できたことは良い経験となった。
また、尊敬する上司が 2 名退職された。今まで改善案の壁打ち等もろもろ相談をさせてもらっていたので、正直なところ相談できる相手が居なくなったことに大きな不安を抱えていた。しかし最近は自分が成長するしかないと開き直っている。
上司が退職されたことで、初めて採用活動に参加することになった。私よりも経験が長い候補者の方の書類選考や面接をすることになったが真摯に取り組むことができた。質問で相手の良いところを引き出す難しさを痛感した。これも良い経験になったと思う。
学習
2022 年前半は特定の JavaScript フレームワークの学習よりも要求分析や基本設計を学習すること優先した。書籍「ユースケース駆動開発実践ガイド」を読み ICONIX プロセスを実践した。これは業務にもすぐ活きる良い学習だった。
年の半ば辺りからバックエンドを経験したい欲が増してきており、以下の理由で Java を学習した。
- 現職のバックエンドで利用されている
- 設計について語られる際に Java を用いて説明している書籍が多い気がする
その後サーバーサイドで動作するすごく簡単な Web アプリケーションを作成してみたり、前々から気になっていたアルゴリズムの基礎を学習するなどした。
読んだ書籍
以下の書籍らを読んだ。2 ヶ月に 1 冊は読んでいるので、個人的には悪くない学習量だと思う。
2023 年の抱負
お仕事
フロントエンド周辺技術のインプットに加えて、ソフトウェア開発手法(アジャイル開発など)をきちんと学習して実践していきたい。
2023 年にもまた所属チームが変わりそうな予感がしているので、モチベーションを保つための努力も必要だと思っている。これについての対応策は見当もつかないが何とかする。
学習
2022 年はやるべきであろう学習を優先してきたので、2023 年は興味ドリブンで学習したいと思っている。やらないといけないからやるのではなく、単に興味があることを学習する方が楽しいし継続できると考えているからだ。
私の今の関心はバックエンド開発にあるので 2022 年に続いて学習を継続しようと思う。先にも書いた様に現職で利用されている Java を学習し、チャンスがあれば現職で経験することを目標にするのが賢い選択だろうと思う。しかし残念なことに Java よりも Rust に対する興味が大きくなっている。言語にこだわるべきでは無いと思うが、JavaScript ランタイムとして注目されている Deno が Rust で実装されるなど、フロントエンド系のツールが Rust で実装されていたりすることから、バックエンド開発以外にも活かせそうで注目している。
具体的には以下をやってみたいと思っている。これは後に変わるかもしれないし変わらないかもしれない。
- プログラミング言語 Rust に入門する
- Rust で Web アプリケーションを作る
Web アプリケーションは一応使える程度のものを作りたい。需要がなくても自分が使えればよいかな。
全くまとまりのない文章で恐縮だが、誰も見ることがないだろうから インターネッツ へ公開してみよう思う。終わり。