ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

読んだ : What Is An Engineering Manager?

最近エンジニアリングマネージャという言葉をよく聞くようになった気がするのだけど、自分の思っているエンジニアリングマネージャ像 *1 と全然違うエンジニアリングマネージャ像を語っている人もいる *2 ので、何かしら文献があったりしないのかなーと思って調べたところ、1997 年の paper を見つけた。

peer.asee.org

修士課程のエンジニアリングマネジメントのカリキュラムで使えることを目的とした paper らしく、なかなか興味深かった。 ので、面白かった点などを書き残しておく。

面白かった点とか気になった点とか共感した点とか

エンジニアリングマネージャは優秀なエンジニアでなければならない

There are two kinds of engineers in industry: individual contributors and managers. The managers must be competent engineers first on the ground that you can't manage what you don't understand. Ideally these people should be educated in two professions: engineering and management.

自分が理解してないものをマネジメントできるわけがない。 ド正論。

プロジェクト軸と機能軸

In this paper we will be discussing engineers and managers in the engineering department. These people may be employed on three kinds of engineering work (as viewed by engineering managers):

  • Projects
  • Programs (groups of related projects)
  • Functions (work that supports the projects, e.g., materials engineering, testing, failure analysis, etc.)

大きく分けて、プロジェクト軸と機能軸があるという話。 これに関することを最近よく考えていて、高いレベルの専門性の維持のためには機能組織がないと難しいのではないか、と思っている。 「Definition of a Function」 にて、マネージャの責務として以下のことが書かれている。

A manager responsible for

  • Maintaining a high level of personnel competence in the specialty
  • Equipment relevant to the specialty
  • Investigating new technology

今のうちの開発組織には開発支援 G という機能組織がある *3 のだけど、逆に言うとエンジニアリングの機能組織はそれだけしかなくて、開発支援 G が受け持つ分野以外について高いレベルで専門性を維持するための責任を持つエンジニアリングマネージャが (明確な役割としては) 存在しない *4。 プロダクト開発組織としてより強くなっていくためには、より高いレベルで専門能力の維持向上を図っていく必要があると思っていて、そのためには機能組織が大きな役割を果たすのではないか、と思っている。 (考え中。)

(こういう話に興味がある方が居たらぜひお話したいです! お気軽にお声がけください!!)

エンジニアとエンジニアリングマネージャのキャリアパス

f:id:nobuoka:20190221020749p:plain
Career Progressions

ざっくりとは 「まあそうだよね」 って感じの図ではあるけど、engineering management の先に general management があることや、マネジメントの梯子の最初が program management と function management である点が、なるほどー、という感じ。

マネジメントの梯子の方に project management がないのは、多分 (本 paper における) project management は、通常の (マネージャでない) エンジニアにも求められることだからなのかなー、と思った。

マネージャのおしごと

仕事の計画から体制づくり、プロセスの構築と組織への装着、結果の評価、それをもとにした再計画など。

f:id:nobuoka:20190221023419p:plain
General Tasks of Managers

この中で、エンジニアリングマネージャについての議論で話題にあがりやすいのは体制づくり (人の育成や組織マネジメント) についてだと感じている。 おそらく、仕事の計画などの点は他のマネージャとも共通の要素であるが、人の採用や育成、評価などはエンジニア固有の事情が多いからなのかな、と思う。 また、チームビルディングが難しい、ということも理由の一つなのかもしれない。

本 paper でも、エンジニアリングマネージャが扱わなければならない組織内部の複雑な点として、tiger team *5 の形成について多くの点 (採用や育成など) が挙げられている。

  • Building tiger teams
    • Recruiting the best people
    • Training them
      • Planning and providing engineering experience
      • Continuing their formal education
    • Providing facilities and tools
    • Demanding performance

Time-related thinking

11 の思考のモードが挙げられており、その中で特に Time-related thinking とそれが Innovation のプロセスで求められたときのエンジニアリングマネージャの義務についてなども書かれている。

最近社内で新規プロジェクトの進め方などを議論するときに、よく 「時間」 について話をしていたりするし、本 paper の内容もわかりがあるものだった。

おわり

エンジニアリングマネージャと一口に言っても、対象となるエンジニアリングの領域はいろいろあって、組織や人によってどういう役割を担うのかはだいぶ違ってくるんだろうなー、と思った。 また、エンジニアリングマネージャが担う役割のうち、仕事の計画と推進、結果の評価などの部分はプロジェクトマネージャ、人員配置などはラインマネージャ、という感じで役割を分割したりもできるのだろう。 GREE のブログにもそういうことが書かれていた。

*1:エンジニアリングマネージャとはエンジニアリングのマネジメントをする人である、という認識。

*2:単なるエンジニア組織のラインマネージャをエンジニアリングマネージャって呼んでるような人もいる。 「エンジニアリングマネージャーはエンジニアリングがわからなくてもよいのか」 というブログ記事とか。

*3:エンジニアをサポートするエンジニア~RMP内製開発の強さ・魅力~【前編】」 参照。

*4:同じ技術分野のエンジニア同士の勉強会などは行われてはいるが。

*5:特定のゴールに向かう専門家集団のことらしい。

読んだ : ユースケース駆動開発実践ガイド

ユースケース駆動開発実践ガイド (OOP Foundations)

ユースケース駆動開発実践ガイド (OOP Foundations)

ユースケースを出発点とする開発プロセスを学びたかったので読んだ。 ちなみにこの書籍が言っている 「ユースケース」 というのは Ivar Jacobson (イヴァー・ヤコブソン) によるものであり、Uncle Bob の Clean Architecture が言う 「ユースケース」 と (基本的には) 同じものである *1

結論から言うと、本書は個人的に学びの多いものであった。 私は 「DDD を実際の開発プロセスにどう取り込んでいくと良いか?」 「Clean Architecture でいうところの UseCases 層のメソッドって具体的にどういう粒度で書くことになるのか?」 といったところを知りたかったのだが、非常に参考になった。 また、ちゃんとした要求定義・要件定義などの経験がなく *2 て、要求定義を含んだ開発プロセスについても学びたかったので、その点を学ぶことができたのも良かった。

古い本ではあるが、開発プロセスを概念的に理解するという目的であれば現代でも十分に通用するものだと感じる。 不要な内容もそれなりにある *3 が、そこはいい感じに飛ばして読むと良いだろう。

内容 : 開発プロセス概要

本書で述べられている開発プロセスICONIX プロセスと呼ばれるものである。 大まかな流れは以下のようになる。

f:id:nobuoka:20190217132851p:plain
ICONIX プロセス

要求定義から始まり、実装・テストまで静的モデルと動的モデルをブラッシュアップしながら進めていく。 全体の流れを説明すると下記のようになる。

  1. 要求定義
    • 機能要求 : システムができることの定義。 プロジェクトの組織化によって開発チームが機能要求の抽出に加わるのか、顧客やビジネス分析者が機能要求を開発チームに伝えるのかが決まる。
    • ドメインモデリング : 問題領域からユビキタス言語を定義し、単語間の関連を表現する。
    • 振る舞い要求 : 紙芝居から始めて、アクター (ユーザー) とシステムがどう対話するかをユースケースとして記述する。
    • 要求レビュー : ユースケース記述が顧客の要望に応えているかレビューする。 ユースケースごとに行っても良い。
  2. 分析・予備設計レビュー : 個別のイテレーションごとに (= ユースケースの小さな単位で) 下記を行う。
  3. 詳細設計
    • シーケンス図の作成
    • ドメインオブジェクトに振る舞いを追加して、クラスモデルに昇華
    • 詳細設計レビュー
  4. 実装
    • コーディングと単体テスト
    • 統合テストとシナリオテスト

ICONIX プロセスの特徴はロバストネス分析にあるらしく、ロバストネス分析により 「ユースケースとシーケンス図の間のギャップが大きく、ユースケースとシーケンス図がうまく対応付けられていない」 という状況が起こることを避けられるらしい。

ユースケースについて

Jacobson の 「ユースケース」 と言っても、いろいろ流儀があるらしい。 本書では、ユースケースの記述として下記を重要と言っている。

  • イベントとその応答として、アクター (ユーザー) とシステムの対話を記述すること。 すなわち、ユーザーがどういう操作をするのかだけでなく、システムの挙動も記述する。
  • UI についても考慮する。 GUI プロトタイプや画面モックアップを使用する。
  • ドメインモデル中の言葉や、画面のようなバウンダリクラスの名前を使って記述する。
  • 代替コース (「雨の日」 のシナリオ) (バリデーションエラー時のシステムの応答など) を忘れないようにする。
  • ユースケース記述は叙述的にする。 要求自体をユースケース記述に含めたりはしない。 (要求は別に管理し、要求を満たすようにユースケースを記述すれば良い。)

思ったことなど

アジャイルなプロセスに適用できるのか?

本書ではできると書かれている。 私自身の実感としても可能だと感じた。

上流工程でのモデリングを、コーディングの前に少しだけ (1 回にひとつのユースケースに対してだけ) 行うのか、すべてのユースケースをコーディング前にモデリングするのかの判断は、あなたに任されてい ます。 ICONIX プロセスを利用する場合、プロジェクトに応じてアジャイルに行う (短いイテレーションによって、短期間で連続的にリリースする) ことも、ウォーターフォール的に行う (まずすべての要求を記述し、設計を行い、それからコードを書く) ことも可能です。

ユースケース駆動開発とドメイン駆動設計 (DDD)

ユースケース駆動開発」 と聞くと DDD とは全然違う概念のように思うが、実際のところはドメインモデリングをするし、動的モデルをユースケース記述からブラッシュアップしていくのにあわせて静的モデル (ドメインモデル) をブラッシュアップするという開発プロセスなので、ある意味 DDD (の一部?) を包含しているように思う。 DDD で難しいのは 「ドメインオブジェクトにどういう振る舞いを割り当てるのか」 というところ *4 だと私は思っているのだが、ICONIX プロセスではユースケース記述中に出てくる動詞がドメインオブジェクトの振る舞いの候補となるので、DDD をやりやすいと感じた。

つまり、ドメインモデルを使う側 (ユースケース側) の記述をもとにドメインモデルに振る舞いを割り当てられるわけである。 さらに言うと、そうすることでアプリケーション上のユースケースのステップに相当する処理がユースケース記述に近いものになり、実装を理解しやすくなるという利点もある。

ドキュメントを書くべきなのか?

ICONIX プロセスではドキュメント (ユースケース記述やドメインモデル、ロバストネス図やシーケンス図など) をしっかりと作り、コードの変更に合わせてドキュメントを更新していくという教えになっている。 個人的には、ユースケース記述とユビキタス言語についてはドキュメント化はした方が良くて (= 非開発者とも共有するため)、それ以外の設計用のドキュメントについては開発チームの練度によるかなーという気はする。 ユースケース記述とユビキタス言語から直接設計・実装に入れる場合も多々ありそうなので、必要に応じてで良さそう。 ロバストネス図やシーケンス図をしっかりと書くことでユースケースドメインモデルのブラッシュアップに効くというのはわかるので、それを明示されたモデル上でやるのか、頭の中でやるのか、実際のコード上でやるのか、みたいな話になりそう。

Clean Architecture でいう UseCases との関係

冒頭でも述べた通り、Clean Architecture におけるユースケースも Jacobson のユースケースがもとになっている。 『Clean Architecture 達人に学ぶソフトウェアの構造と設計』 には、ユースケースについて 『ユーザーとエンティティのインタラクションを支配するアプリケーション固有のルールを記述したものである』 と書かれており、基本的には本書のユースケースと同じものを表している思って良いだろう。 ただ、ユースケースの例を見るとシステムの挙動しか表していなかったり、『ユースケースは、ユーザーインターフェイスについては記述していない』 と書かれていたりして、やや差異はあるようだ。 ここら辺はユースケースの書き方の流儀の違いなどによるものだと思われる。

UseCases 層の実装として、『ユースケースはオブジェクトである。アプリケーション固有のビジネスルールを実装した関数を1つ以上持っている。』 と書かれている。 これをそのまま受け取ると、1 ユースケースに対して 1 つの UseCase クラスを用意し、ユースケース記述におけるシステムの応答のステップのひとまとまりを 1 メソッドとして実装する (ユースケースに複数の応答がある場合などは複数メソッドを定義する) という感じになりそう?

とはいえ Android だと非同期処理とか画面遷移とかで 1 ユースケースを 1 クラスで実装するのは設計的にやりにくい気もするので、「UseCases 層のメソッドはユースケース記述の粒度で実装する」 ぐらいの感じで設計すれば良さそう。

ユースケース記述に技術的な内容を含めて良いか

例えば 「キャッシュ」 の扱いなど。 個人的にはユーザーから検知できるものであればユースケースに含めるべきなきはしている。

例えばブラウザでスーパーリロードするとキャッシュがあっても再読み込みする、みたいな場合、ユーザーからキャッシュの存在が見えるわけなので、ユースケース記述に含めるべきな気がする。 キャッシュの有無がユーザーから見える挙動に全く影響を与えない、本当にパフォーマンスのためだけのキャッシュだったらユースケースに含めない方が良さそう。

関連

目線合わせシートとユースケース記述

うちのチームでは、新機能開発などの施策を行う際にデザイナの人が 「目線合わせシート」 というものを作ってくれている。

UI のプロトタイプやユーザーの行動などがまとめられている。 うちのチームでユースケースを使うとしたら、目線合わせシートをもとにユースケースを抜き出してユースケース記述をしていく、という感じにするのが良さそう。

*1:書籍 『Clean Architecture 達人に学ぶソフトウェアの構造と設計』 において、「ユースケース」 が Ivar Jacobson によるものであることが述べられている。

*2:Web 業界だと明確な要求定義がないことが多いように思う。

*3:UML についてだったり、モデリングツールの使い方だったりの説明などがある。

*4:戦略的には境界付けられたコンテキストをどう分けるかというところが一番難しいと思っているけど、ここでは戦術的な話をしている。

仕様変更が想定される場合の Fizz Buzz のドメインモデリングについて

前の記事 「Fizz Buzz と税率とタイムゾーンの話 (ドメインレイヤとアプリケーションレイヤの話、あるいは時間変化する値をモデリングする話)」 でもちょっと言及した下記のついーと。

変化しやすい箇所を依存関係の外側に定義するというのはクリーンアーキテクチャの教えとして正しいのだけど、ドメインユースケースを捉えるという意味では「仕様変更されやすそうならユースケース」 というのは本質的ではなく、その点については賛成できない。

一方で「仕様変更されやすそうなときはどうするの?」 というのは良い問題提起だなーと感じた。 というわけで 『今後 「4 の倍数なら huzz が返る」 的な仕様追加が容易に想像できる』 場合に自分だったらどうするか考えてみる。

自分だったらどうするか?

想定される変化に今のドメインモデルでは対応できない、ということは、ドメインモデル上で本来変更されやすくなっているべき箇所 *1 が変更されやすくなっていないということだ。 おそらくドメインモデルに改善の余地があると思われるので、ドメインを捉えなおしてドメインモデリングをやり直したい。

「4 の倍数なら huzz が返る」 的な仕様追加が容易に想像できる、という感じであれば、Fizz Buzz 問題を下記のように捉えなおすことができそうである。

条件と文字列の組 (「設定項目」 と呼ぶ) が複数与えられる。 数字を数え上げていき、その数字がいずれかの設定項目の条件に合致する場合はその設定項目の文字列を表示すること。 合致するものがない場合はその数字をそのまま表示すること。 ただし、合致する条件を持つ設定項目が複数ある場合は、それらの文字列をすべて連結して表示すること。

ここで、設定項目は次のとおりである。

  • 条件 : 数値が 3 の倍数; 文字列 : Fizz
  • 条件 : 数値が 5 の倍数; 文字列 : Buzz

問題を上のように捉えなおすことでドメインモデルも変化する。 以下のような感じである。

class FizzBuzzConfigItem(
        /** この条件が真になる場合に [expression] が出力に含まれる。 */
        val predicate: (Int) -> Boolean,
        /** 条件が真の場合に出力される文字列。 */
        val expression: String
)

class FizzBuzzCalculator(private val config: List<FizzBuzzConfigItem>) {
    fun calculate(inputNumber: Int): String = run {
        val expressions =
                config.filter { it.predicate(inputNumber) }
                        .map(FizzBuzzConfigItem::expression)
         if (expressions.isEmpty()) {
            inputNumber.toString()
        } else {
            expressions.joinToString("")
        }
    }
}

これで通常の Fizz Buzz に対応することも 「4 の倍数なら Huzz」 という仕様を追加することも容易になった。

        // 3 の倍数なら Fizz、5 の倍数なら Buzz を返す。
        val fizzBuzzCalculator = FizzBuzzCalculator(listOf(
                FizzBuzzConfigItem({ it % 3 == 0 }, "Fizz"),
                FizzBuzzConfigItem({ it % 5 == 0 }, "Buzz")
        ))

        // 3 の倍数なら Fizz、4 の倍数なら Huzz、5 の倍数なら Buzz を返す。
        val fizzHuzzBuzzCalculator = FizzBuzzCalculator(listOf(
                FizzBuzzConfigItem({ it % 3 == 0 }, "Fizz"),
                FizzBuzzConfigItem({ it % 4 == 0 }, "Huzz"),
                FizzBuzzConfigItem({ it % 5 == 0 }, "Buzz")
        ))

「3 の倍数なら Fizz」 というような設定項目をどこに定義するかは難しいところだが、カスタマイズ性がそこまで高くなくて良いなら (仕様変更がたまにであれば) ドメイン層の中にべた書きしてしまって良いだろう。 もしユースケースごとに異なるのであればユースケース層に定義すると良いし、ユーザーごとに変更する必要があるならそもそもコード上に定義するのではなくてストレージに保存することになるだろう。

コード全体は fizzbuzz_configurable.kt においてある。

ここまで書いたけど

ドメインレイヤの API が変わらないなら別に実装が変わっても困らないので、必ずしも上のようにカスタマイズ性を高める必要はない気もする。 それよりも、Fizz Buzz をさらに抽象的にして 「数字を数え上げて、文字列を出力するゲーム」 みたいに捉えて、ドメインモデルとしては数字から文字列の変換のインターフェイスと実装を別に提供して切り替えやすくする、みたいな感じにしても良さそう。

(追記) ここでは事前に仕様変更について書いていたが、「2019-02-16 UseCase とは何か | wada811.com」 に書かれている通り、問題領域に変更が入ってからドメインモデリングしなおすというのでも全然問題ないと思う。

実際の開発におけるドメインモデルの変更

実際の開発においてもドメインモデルの再構築が必要になることはままある。 ドメインモデルの再構築が必要だと気付くのは大体開発の途中なので、再構築するのはちょっと大変だったりする。 だけど場当たり的な対応をするとどんどん崩壊していく (繰り返すうちにどんどん変更が難しくなっていく) ので、必要になったらしっかりドメインモデルの再構築をしていく方が良いと思う。 きれいなドメインモデルを保っていれば変更もそこまで苦ではないはず。

ちなみに前の記事で時刻によって状態が変化する場合のドメインモデリングについて書いたが、これは今開発している製品でもともと時刻に応じた状態を持っていなかったところに時刻に応じた状態を持たせるというドメインモデルの変更を行う必要があって、そのときにいろいろ考えたときのことをベースに書いた。 (具体的に言うと、人と組織の所属関係としてもともと 「所属していない」 「所属している」 「所属していた」 という状態しかもっていなかったところに、本来は 「所属期間」 という概念が必要だったということがわかってドメインモデルを変更した。) そのときはもともとのドメインレイヤの設計がめちゃくちゃだったのですごく苦労したのだけど、きれいなドメインモデルを作って実装に落とし込めていれば多分そこまで苦労せずにドメインモデルを変更できたはず。

*1:変化が想定されているということは、ドメインモデル上でも変化しやすくなっていた方が良いはず。

Fizz Buzz と税率とタイムゾーンの話 (ドメインレイヤとアプリケーションレイヤの話、あるいは時間変化する値をモデリングする話)

Twitter で見かけて面白そうだったのでちょっと考えてみた。

Fizz Buzz のロジックをドメインレイヤ (Entities 層) とアプリケーションレイヤ (Use Case 層) のどちらに書くか

(実際に作るアプリケーションにおいてはいろいろ考慮して設計することになると思うけど) Fizz Buzz の問題を素直に捉えると 「3 の倍数のときは fizz が返る」 というのは問題領域に存在するロジックなので (例えば、ソフトウェアを実装せずに人が手で Fizz Buzz を書きだす場合でもこのロジックは使われる)、自分だったらドメイン層に書くなーと思った。

(文字列を返すドメインサービスにするのか、バリューオブジェクトを定義するのか、みたいなところは場合によって分かれそう。)

アプリケーション層 (Use Case 層) にはアプリケーションとしてどう使われるかに応じたロジックを書く。 例えば 「1 から指定された値までの Fizz Buzz をコンソールに表示する」 だったらコンソールにアウトプットするための処理を書く *1 し、例えば 「1 秒ごとに Epoch 秒に応じた Fizz Buzz の値をどこかに送る」 みたいなアプリケーションだったら Epoch 秒を取得してどこかに送るみたいな処理を書くことになる。 (個人の見解。)

『Clean Architecture』 によると

Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)

Clean Architecture 達人に学ぶソフトウェアの構造と設計 (アスキードワンゴ)

Uncle Bob によって書かれた書籍にはエンティティについて下記のように書かれている。

エンティティは、企業全体の最重要ビジネスルールをカプセル化したものだ。エンティティは、メソッドを持ったオブジェクトでも、データ構造と関数でも構わない。企業にあるさまざまなアプリケーションから使用できるなら、エンティティは何であっても問題はない。

企業が存在せず、単一のアプリケーションを作成しているだけなら、エンティティはアプリケーションのビジネスオブジェクトになるだろう。それは、最も一般的で、最上位レベルのルールをカプセル化したものである。

ユースケースについては下記。

ユースケースのレイヤーのソフトウェアには、アプリケーション固有のビジネスルールが含まれている。ここには、システムのすべてのユースケースカプセル化・実装されている。ユースケースは、エンティティに入出力するデータの流れを調整し、ユースケースの目標を達成できるように、エンティティに最重要ビジネスルールを使用するように指示を出す。

これに照らしてみても、Fizz Buzz の数値から文字列を生成するロジックは問題の最上位レベルのルールなのでエンティティで、入出力回りなどをユースケースに記述する、って感じになりそう。

税率

という Fizz Buzz だけの話だったらブログに書くほどのものでもないのだけど、税率の話 (というか時間変化する状態のモデリング) については最近考えていたこともあったので吐き出しておく。

(税率とは関係ないけど、*2 上のついーとのリプ先に 『仕様追加が容易に想像できるならばUseCaseにします』 って書かれてるのは本質的ではない話で、ちゃんと問題領域をとらえてドメイン層を定義すべきでしょ、って思った。)

この話って、抽象的には 「時間変化する状態をアプリケーション上でどう扱うか」 という話 (ではなかった)*3 で、時刻が変化したときに状態がどう変化するか (例えば 2019 年 10 月までは税率 8 % で、2019 年 10 月からは税率 10 % になる) という部分も含めてドメインモデリングするのがうまい方法なんだと思う *4

つまり、ドメイン層には 「指定の商品について、指定された時刻における税込み価格を計算する」 みたいなロジックを定義して、アプリケーション層の方でそのロジックに現在時刻を渡してやることで、その時点での税込み価格を計算する、みたいな感じになる。

時間変化のモデリングの例 : Date and Time API におけるオフセット時間の時間変化

時刻によって変化する値のモデリングの例として Java の Date and Time API (JSR 310) がある。 Date and Time API は 「時刻やタイムゾーン」 を問題領域とする API であり、時刻についてのドメインモデルとして捉えて良いだろう。

このドメインにおける 「時間変化する値」 としてはタイムゾーンごとのオフセット時間がある。 タイムゾーンごとのオフセット時間は時刻によって変化するが、それがうまくモデリングされて、きれいな API が提供されている。

最近あった身近なオフセット時間の変化としては、平壌時間における 2015 年と 2018 年のオフセット時間の変化がある。

協定世界時 (UTC) より9時間進んでおり (UTC+9)、大韓民国(韓国)で使用されている韓国標準時 (KST) や、日本で使用されている日本標準時 (JST) と同じである。2015年8月15日から2018年5月4日まではUTC+8:30と定められていた。

平壌時間 - Wikipedia

こういうオフセット時間の時間変化も Date and Time API の実装が面倒を見てくれるので、API を使う側はオフセット時間の時間変化について知らずとも任意の時刻の指定のタイムゾーンでの現地時刻を取得することができる。

/** 渡された時刻を平壌時間での [LocalDateTime] にして返す。 */
fun calculatePyongyangLocalDateTime(instant: Instant) =
    instant.atZone(ZoneId.of("Asia/Pyongyang")).toLocalDateTime()

fun main() {
    val pyongyang2015 =
        calculatePyongyangLocalDateTime(Instant.parse("2015-01-01T12:00:00Z"))
    val pyongyang2017 =
        calculatePyongyangLocalDateTime(Instant.parse("2017-01-01T12:00:00Z"))
    val pyongyang2019 =
        calculatePyongyangLocalDateTime(Instant.parse("2019-01-01T12:00:00Z"))

    println(pyongyang2015) //=> 2015-01-01T21:00
    println(pyongyang2017) //=> 2017-01-01T20:30
    println(pyongyang2019) //=> 2019-01-01T21:00
}

(問題領域に税率を含み、時間の概念も持つアプリケーションを開発するのであれば) Date and Time API におけるタイムゾーンと同様に、税率を時間変化する値としてドメインモデル上で定義すると、ドメインモデルを使う側からはいい感じに税率の時間変化が隠蔽されて良い感じになるのではなかろうか。

ドメインとかユースケースとか

最近 「ドメイン」 とか 「ユースケース」 って言葉を聞くことが多いのだけど、人によって解釈が全然違ったりするなー、ということをよく感じる。 そもそも明確な定義みたいなのもないと思うのだけど、個人的には下記の書籍を読んだことで理解が進んだので、「ドメイン」 とか 「ユースケース」 についてよくわかんないけどなんか書籍を読みたい、って人は下記みたいな書籍を読むと良さそう。 (私は DDD に関する本はいろいろ読んだけどユースケースについて言及している書籍は下記の 1 冊しか読んだことがないので、他におすすめがあれば教えてください!)

ユースケース駆動開発実践ガイド

ユースケース駆動開発実践ガイド

この書籍では、要件定義からアプリケーションの実装までの開発のライフサイクルを扱っている。 まずアプリケーションが扱う問題領域から重要な言葉を抜き出してドメイン辞書を作る。 それからユースケース (アクター *5 とシステムの相互作用についての記述) を記述する。 ドメイン辞書は静的な設計の出発点で、ユースケースは動的な設計の出発点。 ここから初めて実装に落とし込むまでの流れを説明する、という感じ。

で、この書籍で説明されているユースケースと Clean Architecture でいうユースケース層の関係としては、ユースケース記述に書かれている文章と同じぐらいの粒度でユースケース層のメソッドを実装する、って感じになる。

なので、単純なユースケースについては 『ユースケース図の吹き出しで出てくるぐらいの粒度のものがその名の通り1UseCaseというイメージ』 となるはず *6。 実際のところは、画面をまたがるユースケースがあったり、イベント駆動な設計になっている場合もあったりするので、1 ユースケースユースケース層の 1 メソッドや 1 クラスに相当するということはなくて、複数メソッドに分散したりするはず。

*1:実際のコンソールへのアウトプットを行う処理自体はさらに上のレイヤから渡すべきで、アプリケーション層では抽象化された出力操作を行う。

*2:下の注釈に書いたとおり、関係あるということに気づいた。

*3:Fizz Buzz の話の流れで書かれていたので、この文を書いていた時には 「Fizz Buzz の入力数字を時刻、出力の文字列を税率と見立てて、税率の時間変化をどう扱うか」 という問題提起なのかなーと思ったんだけど、今読み返すと 「仕様追加が容易に想像できるならばUseCaseにします」 への応答だったんだな、と思った。 文脈が読めてなかった……><

*4:もちろんアプリケーションに求められるものによって税率などをどこで扱うのがいいかは違ってくるとは思っていて、例えば 「表示時にシュッと税込み価格を計算して表示すればいいだけで、ドメインロジックとして税率を扱う必要はない」 みたいなアプリケーションだったらプレゼンテーション層に税率計算の処理を入れちゃってもいい話だし。

*5:システムを使う人やモノ。 ユーザー。

*6:ユースケース図の吹き出しが、1 つのユースケース記述に相当する。

GraphQL サーバー on Kotlin ことはじめ (DroidKaigi 2019 に参加して GraphQL について学んだ)

DroidKaigi 2019 にスポンサー枠で参加しました。 弊社では Android アプリエンジニアをはじめとして各分野のソフトウェアエンジニアを募集しております。 人生を豊かにするプロダクトの開発や、大きな企業でのソフトウェア開発をいかに改善していくかといったところに興味がある方はぜひお声がけください! いっしょにやっていきましょう!

f:id:nobuoka:20190210163903j:plain
DroidKaigi 2019

それはともかく DroidKaigi、様々なセッションがあって素晴らしかったですね。 運営、スピーカー、スポンサー、そして参加者の皆様、ありがとうございました。

私個人としては、GraphQL についての知見を得られたのが大きな収穫でした。

本記事について

本記事は、GraphQL サーバーを Kotlin で立てるにあたって、仕組みを学んだ軌跡を残すものです。 GraphQL 自体の初心者が、Kotlin で GraphQL サーバーを実装する際には参考になると思います。 本番環境で使える技術とかを紹介しているわけではないのでご注意ください。

DroidKaigi 2019 と GraphQL と私

GraphQL についてはこれまでも雰囲気では知っていたのですが、はてなid:takuji31 さんと id:funnelbit さんの発表で改めて GraphQL の良さを感じました。

ところで、上記の発表では主にクライアントサイド視点での GraphQL の話が主で、サーバーサイドの実装がどうなるのかが非常に気になるところです。 クエリのパースから実際のデータの取得処理まで、素朴に実装しようとすると難しそうに感じます。 懇親会のときに id:takuji31 さんと id:funnelbit さんに聞いてみたところ、「サーバーサイドの実装者ではないのでそこまで詳しくはないが、言語ごとにいい感じのライブラリがあるから、割と何とかなるっぽい」 とのことでした。

ということで GraphQL の Kotlin サーバーに詳しい人に教えを請いたい〜、と思っていたら shiraji さんが導いてくれて gfx さんや taka さんたちから知見を吸わせてもらえました。 神か! (ありがとうございました。)

知見

自分で動かして試してみる

上記のようなページを見ることでなんとなく Kotlin での GraphQL サーバーサイド実装をどうすれば良いのかが見えてきましたが、Spring Boot を使った上記の例では便利ライブラリが詳細を隠ぺいしてくれているので細かな理解ができませんでした。 というわけで自分でも手を動かして試してみます。

graphql-java を試す

graphql-java は GraphQL のスキーマやクエリのパース、バリデーション、実行といった機能を提供するものです。 HTTP のインターフェイスは提供しません。 Kotlin で GraphQL の挙動を試したい場合は、まずは graphql-java を生で触るのが良いでしょう。 下記のドキュメントを見ながら進めると良いと思います。

外部 IO 無しで簡単に試せるので、最初のとっかかりとしては非常にやりやすいと思います。 私は下記のようなコードを書いて試していました。

まだリソース間に関連がある場合などは試せていません。

HTTP インターフェイスとつなぎこむ

GraphQL のサーバー・クライアント間の通信プロトコルについては、(おそらく) 仕様上は何も定められていなさそうです。 公式サイトに、HTTP での通信についてベストプラクティスとして書かれています。

これに従ってサーバーサイドの HTTP のエンドポイントを実装し、graphql-java とつなぎこむ、という感じにすれば良さそうです。 もともとの GraphQL のクライアント・サーバー間のやり取りがシンプルなので、HTTP のエンドポイントとしての実装もシンプルになります。

細かなエラーハンドリングなどはできていませんが、Ktor で実装すると以下のような感じになりました。

GraphiQL を使ってみる

ここまでくると自分のサーバーで GraphiQL も提供したいですよね?

GraphiQL の JS ライブラリは、npm パッケージとして配信されています。

自分で HTML ファイルや CSS ファイル、JS ファイルをビルドすることもできますが、既にビルドされている JS ファイルを使って簡単に GraphiQL のエンドポイントを生成することもできます。 使い方の例は GraphiQL のリポジトリに含まれています。

上記 HTML ファイルを自分のサーバーから配信すれば、GraphiQL が提供されます。 ただし、依存するリソースである graphiql.css と graphiql.js をどうにかする必要があります。 (上記の HTML そのままの場合は、これらのファイルも同じサーバーから配信する必要があります。)

今回は手軽に試したかったので CDN で配信されているものを使うようにしました。 下記 CDN で graphiql.css と graphiql.js が配信されています。

これを使うように書き換えた HTML をサーバーから配信すると、GraphiQL を使えるようになります。


おわり

というわけで初心者が GraphQL を学んでいる軌跡でした。 ここに書かれていない知見等ありましたら是非教えてくださいませ〜〜。