はじめに

JJUG CCC 2025 Fallに参加させていただきました。 JJUG(日本Javaユーザーグループ)は、java技術の向上・発展、開発者の支援を目的とした任意団体で、2007年4月に発足しました。 現在では会員も約10000人に達ししており、とくにjavaの動きが活発になった2014年ごろから会員数も増加しています。 今回私は初参加だったのですが、会場は非常に賑わっており、年齢層も若い方からベテランの方まで幅広く多岐にわたっていました。 会場では様々なjavaやkotlin、またそれ以外にも非常に多岐にわたるセッションが開かれており、javaを業務で使用している人だけではなく、その他の開発言語を使用している方、開発に携わっていない方でも楽しめる内容のセッションも多いと感じました。 今回は私が参加したセッションについて、内容をまとめつつ感想を交えて体験記とさせていただこうと思います。

仕様がそのままテストになる!Javaで始める振る舞い駆動開発

こちらは@uutan1108さんの登壇でした。 Xの方で登壇資料も公開されています。

タイトルの通り、振る舞い駆動開発(Behavior Driven Development)をjavaで行う、という話をされていました。 テスト駆動開発から振る舞い駆動開発へと発展させることで、テストをテスターやプログラマーのためだけではなく、開発チームのプログラミングに携わらないメンバーとのコミュニケーションを支援するためのものへと昇華させることができます。 振る舞いを自然言語で定義しておき、それを使ってjavaのテストコードを書くことで、プログラマー以外にも理解しやすく、また仕様との照らし合わせも容易に行うことができます。

javaでこれを行うにはCucumberなどのライブラリがあるそうなので、少し調べてみようと思います。

現在業務でテストを行なっていますが、どうしても「何を目的としたテストなのか」がわかりづらいテストが多いです。そういったものをコードベースで改善できる可能性があるのは非常に魅力的だと思います。また、振る舞いを定義する際にはドメイン固有言語を使って振る舞いを定義するので、必然とドメインと強く結びついたクラスが作れるという良い副作用も期待できそうだと感じました。

登壇の最後に言われていたことですが、「BDDフレームワークを使うのがBDDではない、振る舞いを考え、開発チームで共有し、テストに落とし込むことがBDDである」という言葉通り、目的と手段を取り違えることだけは注意したいところです。

Java Virtual THreads, Kotlin Coroutines, Go Goroutinesとの比較

こちらはLINE YahooのTakamichi Wadaさん(Xのアカウントを見つけられませんでした...ごめんなさい)の登壇でした。

タイトルにある通り、JavaのVirtual Thread、Kotlin Coroutines, Go Goroutinesがどの様にして並行処理を実現しているのかに注目した登壇となっていました。 多数のリクエストに対してどの様にして対応するのか、その対応方法に着目されていました。 javaのVTやGoのGoroutinesでは、多数のリクエストに対して多数の軽量スレッドを作り、それを一つのOSスレッドにスケジューリングすることで、I/OブロッキングやOSスレッドの不足(C10K問題)を気にしなくて良くなる方法をとっています。 KotilinではクライアントからのリクエストやIOの完了などをリクエストとして通知するイベントループを採用していますが、これを同期的なコードで記述して、コンパイル時に継続オブジェクトで実行状態を保持する実装に変換することで非同期に実行することを可能にしているそうです。

最近学習しているRustでは、1リクエスト1OSスレッド方式をとっており、数万単位のスレッドを作成することは現実的ではありませんが、async/awaitを使用することで大量のリクエストに対する非同期処理を実現している様です。(非同期処理についてはまだ学習途中...)

こういった「一見似た動作に見える機能の言語ごとの実装の違い」というのは、それぞれの言語への理解を深める上で非常に重要だと感じました。 これらの違いの根底にはおそらくそれぞれの言語が抱えるパラダイムの違いがあるはずなので、標準ライブラリの使用や実装方法を覗いてみるとより深くその言語のパラダイムを理解することができそうだと感じます。 特にGoに関しては、並行処理を非常に簡単に実装できるので並行処理や非同期処理が気になっているのならGoで学習するのが良さそうです。

DDDとOOPの交差点

こちらは、Amano Gakuさんの登壇でした。 タイトルにある通り、DDD(ドメイン駆動設計)とOOP(オブジェクト指向プログラミング)の交差点を見つけるという内容でした。

OOPは実装スタイルの一つです。クラスやオブジェクト、継承やポリモーフィズムといった要素を元に変更容易性の高いコードと生産性の高いコードを記述するために使用されます。 対してDDDは、複雑なビジネス要件をシステムに反映させる、ドメイン理解と共同作業の方法論のことです。 両者は若干次元は違うものの、目指すところは変更に強い設計、プログラミング、といったところとなっており、そこをDDDとOOPの交差点と呼んでいました。 この二つを効果的に相互に作用させることで、より良い設計、コードがかけるのではないか、という内容が話の主題でした。

話の中で、プリミティブ過多、setter地獄、貧血ドメインといったありがちなDDDのアンチパターンをもとに、どの様に設計するのかを語られていましたが、非常にわかりやすく、DDD初学者にもわかりやすい内容だったのではないかと思います。

今の開発現場では、〇〇CommonService抽象クラスを継承した〇〇Serviceを継承した、画面IDServiceの実装である画面IDServiceImplといった、非常に複雑で素晴らしい()クラス改装をしています。このあと出てきますが、増田さんが訳されている「実践ドメイン駆動設計」といった本を読んでから現場に入ったので、コードを見たときにがっかりしたのを覚えています。 そういった問題に対して、どの様にアプローチをしていくのかが、初学者向けに語られており、非常にわかりやすくためになりました。

DDDとOOPは同時に語られることも多く、またOOPの解説書やjavaの解説書を読んでも、DDDと書いてなくてもDDDの考え方が元になっている様な記述も多く見受けられます。 そういった面でもこの二つの異なる概念の交差点を見つけて、設計や実装に活かしていけたら良いと思います。

エラー処理の選択肢を増やす ~ try catchから始めて段階的に型安全へ ~

こちらは、@yoheiyohei4さんの登壇でした。 タイトルにある通り、try catchだけではなく、他のエラーハンドリングの選択肢を見てみよう、という内容でした。

try catchは非常に単純で記述しやすいです。それと同時に使用方法をしっかりと考えないとエラーの原因を握りつぶしてしまう危険性も併せ持っています。 そこで、Either<Error, Success>といった、結果を値として返す型の利用を考えてみます。 こうすることで、成功時と失敗時の処理を分けることができますし、エラーの種類によって処理を変えることも容易になります。 Rustで言うとResult型ですね。やっぱりnullで値を返したり、システムエラーや復旧不可能なエラー(ユーザー側に知らせる必要がないエラー)の場合以外でExceptionを出して処理してしまうのは勿体無い、と言うことを言っていました。

ただし、これも銀の弾丸ではなく、何でもかんでもEitherで返せばいい、と言うことではありません。システム設計において、銀の弾丸はあり得ません。 そもそも失敗があり得ない処理をEitherで返す必要はありませんし、システムエラーなどで処理を中止する場合に詳細なエラー原因などは要りません。 こういったどちらの選択肢を取るかの指標と言ったものも教えていただきました。

レイヤードアーキテクチャで考えるとわかりやすいです。 ドメイン層は複雑な業務ロジックが含まれているので、それぞれの処理によるエラーをEitherを使って豊かに表してあげると、非常に処理がわかりやすくなります。 また、アプリケーション層やプレゼンテーション層に関してはEitherとExceptionを組み合わせることで、APIの接続エラーなどにはExceptionを、ユーザー側のエラーなどに対してはEitherを使ってそのままHTTPレスポンスに変換してあげることで、柔軟な設計を提供できます。 インフラ層では基本的にエラー原因の理由も限られるし、そのエラー原因に関しては対して興味がないことが多いのでExceptionで例外処理をしてあげるだけで良いでしょう。 と言った具合に、どの層でどんな処理をするのかの指標を持っておくと、エラーハンドリングをどうやって実装するか考える時の一助になりそうです。

個人的に思ったのは、RustやScalaなどはOptionやResultをネイティブでサポートしています。逆にnullやExceptionと言ったものを(基本的には)扱いません。 こういった言語を扱っている人からするとおそらく当たり前であり、逆に違和感がある内容だったのかもしれません。 RustやScalaを一通りやってみたので、非常にスラスラ内容が入ってきて理解しやすかったです。 Exceptionとの使い分けに関しては、今後少し意識してみようと思いました。

乱雑なコードの整理から学ぶ設計の初歩

こちらは、@masuda220さんの登壇でした。 このかたの書籍は「ドメイン駆動設計をはじめよう」、「現場で役立つシステム設計の原則」ともに読ませていただき、今回のJJUG CCCで一番楽しみにしていたセッションの一つでした。 タイトルにある通り、乱雑なコードをどうやって整理されたコードにしていくのか、と言ったことを通して、設計の初歩について語られていました。

「ソフトウェア設計における整理されたコードとは、変更が楽で安全なコードのことである」 個人的にはこれが全てだと思いました。 このことを目的にしながらコーディング、および設計を行うことで必然と整理整頓されたコードが生まれるはずです。

非常にためになる内容が多かったので、全てを書いてしまうと長くなり過ぎてしまうので、私が特に重要だと思った点をピックアップして説明します。

整理整頓のやり方を身体で覚える 何度も繰り返し実践をしましょう。パターン化された設計や、手が勝手にショートカットコマンドを打ち始める様になるまで実践あるのみです。 私に一番足りないところかもしれません。 本を読んで理解した気になっているのと、手を動かしながら理解を深めるのはやはりレベルが違います。 これを機に、実際のコードを少しずつリファクタリングすることを覚えていく必要がありそうです。

その変更に価値はあるのかを考える エンジニアとして、気になるコードは修正したくなってしまいます。しかし、先ほど言った通り、目的は「安全に変更できるコード」を作ることです。将来変更されないコードを変更することはこれに当てはまりません。 また、「ビジネス的にその変更によって何か価値が生まれるのかを考えられる様にならないと、半人前のエンジニアだ」と言う言葉をいただきました。

小さな変更は積極的に行う これは「Tidy First?」に書かれていた内容に似ています。 小さい変更とは、「コメントアウトされたコードを消す」、「動作していないコードを消す」、「変数名や関数名を変えてみる」、「改行を入れる」と言った実際の動作に影響を及ぼさないが、即効性のある変更のことです。 実際「Tidy First?」では、改行を入れることが最も小さい変更だと言う話があります。 こう言った変更は積極的に行うべきだし、間違いだとしても巻き戻せばいいのだから積極的に行って然るべきです。

他にも具体的にどのような部分に「乱雑さ」を感じるかと言った話をしてくださいましたが、詳しい内容はご本人様のXに登壇資料も上がっているのでそちらをご覧ください。

個人的に、すごく新しい学びがあったかと言われると、本の内容を踏襲した内容が多かったためそうでもないのですが、ご本人から直接講義をしてくださるとやはり理解が一段階深まった気がします。 そして実際に手を動かしてコードを書いてみたいという気持ちが湧いてくるセッションでした。(セッション終わりに、「めっちゃコード書きたくなった」と会話している参加者の方もいました)

やはり私には実践が足りていないので、自分でコードを書いて、運用、保守まで行って初めてその設計が正解だったのかわかると思うので、どんどん実践経験を積んでいきたいと思います。

最後に

今回参加したセッションは以上でした。(時間の関係上最後まで参加できませんでした... この後にも非常に楽しみなセッションがあったのですが非常に残念です。) 次回は春だと思いますが、また参加したいと思える、有意義な時間でした。 ただ、インキャすぎて誰にも話しかけられず、一緒に行く人もいなかったので次は誰か誘って、誰かに話しかけて仲良くなってくるくらいの気持ちで行こうと思います。 皆さんも良いJavaライフを!!!