はじめからちゃんと作るは難しい

今日は何かシステムを作るときにしっかり学習して設計してからコードを書いてリリースだ!みたいなのを聞いて、それは難しいんじゃないかなーという気持ちになったりした。
学習と設計(どう作るか作戦を考える)は全体を俯瞰して整理するには良いんだけど、細かいところは見えてこなくって手を動かしてみると「あれ?これって難しいんじゃないかな?」ってなったりする。
一方で勢いでコードを書き始めると一貫性がなくて「結局これなにが作りたかったんだっけ?」みたいな塊ができてしまったりする。

たぶん全体と部分を行ったり来たりする必要があるんだなー。JIGとか見てるとそんな感じがする。コードを書きながらJIGで全体をみたりドキュメントを書いてみたりしていったりきたりしながら実験と学習して整理するのに活かす。みたいなのがよさそう〜

あ、あとレガシーだから作り直したいってのも聞いたけど、作り直すことでキレイになるかというとそんなことはなくて、今の複雑さに対しての問題を解決できる答えがないから困っているんじゃないかなー。それなら必要なのは作り直しじゃなくて改善かもしれない
新しいものは今ある問題を解決して、新たな問題をまだ受けてないからキレイに見えるだけですぐに汚れちゃうから、戦っている問題がわからないまま挑むのはあまり意味がないような気がするんだよなー。

はじめからちゃんと作るというのはできるといいけど難しい〜・・・!

リフレクションやメタプロ的な機能は避けような

リフレクションとかメタプロ的なものを触るのは楽しい。わかる、わかるよ。
もちろん知識としては知っておくべきだし、知っておくとフレームワークやライブラリの裏側が想像できてトラブルシューティングのときに便利だ。

メタプロ的機能の辛いところ

まず静的型な言語やIDEの推論がいいかんじに働く言語の場合、あるコードの呼び出し箇所が分からなくなってしまう。静的型の言語の場合はコンパイルエラーが出せなくなってしまうので、コンパイル時に防げた不具合をランタイム時まで持ち込んでしまう。コンパイラIDEに仕事をさせたいと思ったときにメタプロ的機能は邪魔になってしまう。

次に互換性の問題。privateメソッドを呼び出したりモンキーパッチなどで振る舞いを変えたりした場合は、ライブラリの意図から外れた使い方をしているので互換性が壊れてしまっても文句を言えない。大体のライブラリは破壊的変更をChangeLogあたりに書いてくれるが、モンキーパッチやprivateメソッドを呼び出して書き換えていた場合はコードの変更にもっと敏感にならなければいけない。簡単に壊れてしまう。
バグを見つけたならPRを投げたりしてコミュニティに貢献しよう。バグ修正を取り込んでもらえたら他のコントリビューターがいい感じに保守してくれたりいい感じにしてくれるしみんな幸せになれる。

判断基準

上に挙げた辛いところを受け入れた上でコードが宣言的になるほどのメリットがあるかを考える。大体の場合は不要なはず。
ロギングみたいな横断的関心事を扱うときにAOP的機能を使うことがあるのかなくらい。それでもFilterでなんとかなったりするけど。

結論としては、メタプロ的機能は楽しいけど保守するコードで使うなら相応の覚悟を持ってやってねということ。

ORMに感じるもやっと感

SpringBootでWebアプリを作るとき、いつもDBアクセスに何を使うか悩む。Doma?Mybatis?JPA?
個人的にはどれもしっくり来ていなくて、なんだかんだでDomaを使っている。何にもやっとを感じているのか考えてみた。

ドメインモデルへのマッピング

ほとんどのWebアプリのドメインモデルは1:*のようなマッピングが存在していて、SQLの結果を親子関係を持った構造にマッピングしたい。
SQLはjoinなどで持ってくるとflatな構造なので頑張ってドメインモデルに詰め替える必要がある。これがとても面倒くさい。
Mybatisを使う場合、resultMapで関連のマッピングを定義する方法が用意されている。とはいえSQLXMLに書きたくないので、SQLの置き場としてはやっぱりDomaが好きだなーというお気持ち。
JPAはORMの都合をあまりドメイン層に持ち込みたくないのでなんだかなーということであまり使ってない。あと複雑なクエリは多かれ少なかれなんだかんだ書くことになるので、SQLを自分で書くタイプのほうがいいよなーって感じがする。

仮説:リレーションのマッピングができれば嬉しい?

基本的にDomaで事足りていて、あとはリレーションのマッピングをいい感じにしたいだけなのでは?ということはマッピングをするところだけライブラリにしてしまえば良さそう!
ということでKtMapperというライブラリを書いてみた。

github.com

レコードのオブジェクトをドメインモデルに変換するMapperをKotlinでDSLっぽく書けたら便利かもねという思想。

サンプルで書くとこんな感じ。

10連休終わるまでにもうちょっと素振りして検証してみよ〜

最近コードを書くときに考えていること

最近Rubyを書くことから少し離れてKotlinをメインに書くようになったので、最近どんな事を考えてコードを書いているか書いてみる。

ValueObjectに注目する

とにかくクラスをサボらず作る。そうすると大体はValueObjectになる。ロジックの置き場はとにかくValueObjectにおいていく(というか自然な場所を探すと大体そうなる気がする)
Listみたいなコレクションもクラスにラップする。区分の組み合わせから値を導き出すとかよくあるロジックだけど、抽象度がだいぶ低いので隠して名前をつけたい。
具体的な例で言うとOpenID Connectのresponse_typeとか。

response_typeの例

openid-foundation-japan.github.io

OpenID Connectの仕様によると、response_type(3種類の列挙値)は組み合わせ可能で、組み合わせによって選択されるフローがかわる。しかも存在しない組み合わせもある(!)
ResponseTypesにListみたいなのをもたせて GrantType grantType() みたいなメソッド生やすのが良いのかなーとか思ったり。

永続化層をできるだけ意識させない

RDBに保存するとかHibernateやMybatis使うとかそのへんをあまり意識させないようにする。infrastructure層から外にライブラリの都合を出したくないなー。ORMを置き換えたりデータストアを置き換えるのはそうそうないと思うけど、なんだか気持ち悪いや。
生やすメソッドはRDBのRepositoryならjava.util.Listっぽいかんじ。RedisみたいなKVSならMapっぽい感じのメソッドがあるとよさそう〜。あとfindByXXX系のfinder系のメソッド。
Transactionはapplication層でかけちゃうけどこれいいのかな・・・これってインフラ都合じゃね・・・?とか思わなかったり。

クラスの依存関係に気を配る

ArchUnitを入れてレイヤー間の依存関係、サブパッケージ間の循環依存に気を配れるようにする。テストが落ちたらなにかモデリングがまずそうなので考え直す。

値の範囲に気をつける

ValueObjectの取りうる範囲が分かっていると考えることが一気に減るので考えるのが楽になる。区分なら列挙だし、期間なら小さい日付から大きい日付の間、個数なら0以上、X以下になりそう。
システムのリプレースならGROUP BYやMIN/MAXをDBに打って値の取りうる範囲を見つける。まぁ大体変な値が入っていてDBのお掃除をするところから始まったりする。 GROUP BYを打ったら謎の区分が出てきて膝から崩れ落ちる回とかも発生しがちなので心を強く持つ。
BIG QUERYにデータが乗っているとかあれば、NOT LIKE '%XXX%' みたいな感じでバリデーションかけられるか調べてみても良さそう。BIG QUERYなら謎の技術でめっちゃ早い。indexとか存在しない世界なのでとりあえず殴っていける。

おわり

たぶんこんな事考えながらコードを書いたりしてる。ところでDDDの境界付けられたコンテキストの概念難しいんだけど、おれきゅーさんにおしえてくれませんか?😭