コードの整理がちょっとわかってきたかもしれない

従来utilという適当な名前で呼ばれていたものがあった。
これは一般的にはアンチパターンとされるものだったが、しかしutilという名前をついつけざるを得ない場面がしばしばあった。ここに当てはまるものの本質は「当該プロジェクト内の依存先が無い、またはutilの中におさまるもの」ということだな。
厳密に徹底できるかというと難しいかもしれないけど、依存関係でコードを整理するというのが筋が良い気がしている。

オブジェクト指向でやっている事とかも、超極論・暴論すると「そのコード断片、どこに書きますか?」という事なので、どこにどうコードを書くのか、という事に課題がある。
単純に依存関係が増えると、どこに分類すべきかという事が少しずつ難しくなってくる。
CIに、正しくない依存関係(import等)があったら落とす仕組みを入れようかな

なぜこれで整理されたつもりになったか?というと、Webアプリケーションの場合はDBアクセスをしたりアクセスしたデータを入れる型を使う場合にはWebアプリケーション内の型定義を参照することになるので、そのためにutilに入るべきでない不適切な参照が発生するから。
そういうものを禁止すると、「必ずしも純粋ではないものの単一の目的に特化したutil関数」を自然に集めることができる。例えば、sftp接続してファイルを転送するとか、A社のAPIに単純に接続するとかは、副作用を伴うけれど単目的で、そういうのが自然と集まるようになる。(それを超えた処理をするもの、例えば「A社のAPIに接続して結果をDBに保存する」とかは、A社のAPIの接続情報とDB定義と両方の参照が発生するので自然に入れなくなる)
単一責務といえば、それはそうなのだけど、単一責務はプログラムの中で機械的に果たさせることができるものではない。一方で、参照/依存による整理はかなり明確で、アプリケーション/プロジェクトのコードの中で依存の向きが定義されていれば、その逆向きの依存が発生した時点で明らかに異常という事になる。
そのような意味で、依存関係のあり方を定義して、かつutil(または他の良い名前のものいくつかに分割)の中に内部依存が最小になるコードを配置するようにすれば、その部分については整理ができる。
※複数の依存をどこで解決するか、という事には課題が残っているが

昨日からめちゃくちゃリファクタしてて思ったけど、関数に分けるというのは単純に分岐の構造を視覚的にわかりやすくするという効果があるんだな
何を当たり前の事を言ってるんだみたいなところがあるかもしれないが
超当たり前の事ではあるんだけど、なんというか最近考えている「コードをどこに書くか」の文脈で捉え直す時に、分岐がまったくない関数と、分岐がある関数に分けるというのがあるな、みたいな事を思い

参照の仕方をモデルの方に定義した方がよい気がしてきたな
当たり前かもしれないが、条件まで含めてモデルの方に関数として定義して、条件を返却させる、みたいな考え方の方が良いかも的な
これをした時のデメリットは、最終的な取得条件が散らばること
それがすらっと書き下された状態で見られれば...
やっぱりコードが単一の形であるべきではないんだろうな、理想形としては

実装を意味的に追いかける/意味的な対応漏れを無くすためには、意味をモデルやサービスに固めた方がよい
しかし、障害調査の一次対応でとにかく吐き出されているSQLを把握したいとか、そういう場合に無力
モデルの方に使い方を書いておいて、実装は結局個別というのがやはり妥当なのか?まだ答えはない

良いコードの定義って難しいよねという話
例えばこの記事
概ね基本的な内容には賛同するものの、じゃあ本当にスタンプ結合とデータ結合ってデータ結合の方が良いのというと、そうとも言い切れない。
業務アプリケーションにおいては、データ項目を10個どころか、20個30個と扱うのが当たり前で、ひどいデータでは100個ぐらい必要になる場合もある。
そういうとき、10個のデータを構造的に保持しているスタンプを引数で渡すのと、それらをプリミティブに分解して10個の引数で渡すのと、どちらの方が効率が良いか?という事がある。引数が増えるとエラーが増加するというリサーチはある。(スタンプとデータという観点で調べたものか、といったことを把握しているわけではないので、単に引数が多いのがよくないかというと、それはなんともいえない)
また、このようなプログラムにおいて、例えばUserという10項目を保持するオブジェクトのうち使用する6項目のみをプリミティブに分解してデータ結合して、10箇所でこの関数を呼び出していたとする。その関数に、Userの既存項目だが引数でわたしてはいなかった7個目の項目を追加することになったとすると、データ結合にしていた場合は関数の内容の他に呼び出し箇所の10箇所で修正をしないといけなくなる。これがUserを渡すようにしていた場合は、関数の処理だけを修正すればよい(Userに正しく値が設定されている、という前提においては)。
Userの10個の項目が常にセットされているか、という問題は確かにあるのだが、DBから取得するデータをそのまま渡す場合などは"取得する項目を細かく制限していない限りでは"単にそのままUserを渡すだけで済むという事は非常によくあることで、その場合は私にはUserを渡すという設計が正解であるように思える(必ずしも関数の中で使わない項目をインターフェイスとしてのUserが含んでいるとしても)。
このような問題を考える時、「ミクロにみたこの関数の挙動の読みやすさ」だけを切り取れば、確実にデータ結合の方がわかりやすいと思うし、この関数にとって必要なデータ・必要でないデータが何かという事が明示されているとも言える。しかし、この関数を呼び出す部分も含めて考慮すると、毎回Userの内容をプリミティブな値に移送して展開する、という処理をするのか?という事が課題になる。
def print_user(id_, first_name, last_name, first_name_kana, last_name_kana, age):
print(first_name + ' ' + last_name)
...
# 正確に引数だけを使い切るが、それが本当にUser由来の項目であるという保証は
# 関数を呼び出す方で行わないといけない。
# TypeScriptなどでは、例えばid_の型としてUserIdなどといったものを定義して使う
# といった事もできるが、そうすると記述量は増える。
# つまり、読まないといけない量も増える。
def print_user(user: User):
print(user.first_name + ' ' + user.last_name)
...
# この場合は、user.first_nameなどが使われているので、User.first_nameの値を
# 変更した場合にどこに影響が出るのか、といった事を比較的調べやすくなる。
# ただし、print_userのシグネチャだけで何を使うのかという事まではわからない。
これらの"ある種の良いとこ取り"として、ラップしてしまうという方法もないわけではない。
def print_user(user: User):
print_user_inner(
user.id, user.first_name, user.last_name, user.first_name_kana, user.last_name_kana, user.age)
...
def print_user_inner(user_id, first_name, last_name, first_name_kana, last_name_kana, age):
print(first_name + ' ' + last_name)
...
# こうすると、print_user()の中で参照しているUserの項目が何か、ということは
# print_user_innerのシグネチャによってわかる。
# ただし、その中身の処理を見るにはprint_user_inner()の定義を参照しないといけない。
# 参照する引数が少ない場合には、このようにラップ構造にすると逆に無駄が多くなる。
この手の「良いコード」の断片的な定義は
「他の条件が同じであれば、概ね結合度が低い・凝集度が高い方がよいが、他の条件によって変わってくるし、上記のような変数の数といったファクターによっては素朴な結合度だけで評価することができない」
といった事を前提として読まないと理解ができない。

上で書いたことの難しさは、「読み手がどこまで情報を読み取れるか」という読解力みたいなことにすごく強く依存する部分がある。
例えば、シグネチャ定義をしなくても、関数の中身を見れば使用する変数の実態がすぐに分かる場合もあり、そのわかりやすさは厄介なことに人によって異なる。
私がよく思うのは、この理解力の水準を下げて考えると、とにかくインターフェイスを分けまくるという考えに行き着く気がするということ。ただし、そうすると個々の処理を間違いなく理解できても、結局全体で何をやっているんだっけというのが全然わからなくなるように思う。書いているときにはある程度分かったとしても、後で追いかけるときに、「個別のパーツとしてのメカニズムが分かっても全体の挙動が理解しにくい」「個別のパーツとして間違いにくい構造であっても全体の挙動が不明」といった感じになる印象がある。
これはただの印象なので、そうではない書き方も可能なのかもしれないが...

「そのコード、本当に追いかけやすいの?」
これはすごく大事にしたい。
普通に書く限りではトップダウンに読んでいく方が読みやすいのだが、「ドメインのコードは具体的な内容に依存しないようにする」という事を徹底しようとすると、ドメインのコードを読んでいても"実際の流れ"は読み進められないようになってしまう。
ドメインのコードの上から下に、あるいは関数呼び出しをたどる、という読み方が非常に難しくなる。
DIは、しばしば単純な"コードに具体的に書かれた"関数呼び出しではないディスパッチみたいなものを使ったりしていて、結局いまどこでどう実行されてるの、みたいな事がわからなくなったりする。
ただし、一方でエンタープライズレベルのアプリケーションでDBを変更したいといった話自体はわかるので、DBの変更をしやすくするためには例えば"保存する"ということは抽象化しておいて、"保存する"という事の具体的な実装AとBを切り替えられるようにする、みたいな事はある。
このような考え方の場合、ドメインロジックを実装するというのは「パターンを作る」または「レールを作る」というような事で、それを例えばMySQLを使って実現する箇所についてはこのようなこと、Redisを使って実現する箇所についてはこのようなこと、...みたいに「パターン的に」あるいは「レールに沿って」記述したものが呼び出されるようにする、というのはある。
でも、それは本当にデバッグしやすいのか?というと、初期のweb app.フレームワークでスタックトレースが意味不明であったように、スタックトレースは深くなるし、ある種の場面ではデバッグしにくくなる部分が確実にある。
「読めば普通にわかるコード」を捨てて「工業製品」にするのか?みたいな事をすごく感じる。
これは、コードの均一性などとはまた違った話として。

「依存する/しない」というのは、超具体的には「import文をかく/かかない」という事なんだよな。
これを明示すると、とりあえず主張の内容としては(私には)わかりやすい。

これはある意味では、ドメインロジックの圏を作って、実際に動くプログラムはところどころその圏から別の圏(みたいなもの?)の中で動いていて、依存が関手(みたいなもの?)で表現される、みたいな事とも言えなくはない
(私はこういう言い回しあんまり好きではないんだが)

コードの整理の進捗。
デバッグ容易性と、想像性/概要把握性みたいな概念がある。
例えば、メンバ変数を自作型にすると、その型からメンバ変数の意味を想像することができる場合があるが、実際のデータ構造は参照しないとわからないので、定義を元にして実態を掴むタイプの読み方をする場合には不向き。
メソッドの場合は、定義を詳細に追わなくても、実態がわからなくても使えるが、データクラスのようなものは実態がわからないと使いこなせない。
あーーーー
わかったかもしれない。
メソッドとデータクラスで、求められるわかりやすさの質が違うんだ。
つまり、メソッドを呼ぶ/抽象的な意味で何らかの結果を得る場合にはディープであればあるほどよいのだが、webアプリケーションを書いていると実際にはプレゼンテーションが必要になったりアプリケーション間連携が必要になるので、プレゼンテーションしようとするとそのデータクラスの構成要素が何かという事を把握してプレゼンテーションにマッピングする必要があり、そのためにはドメインプリミティブとしてどうなっているかではなくて言語実装的なプリミティブとしてどうなっているかが重要で(もちろん文字数とか入る文字とかの情報もあった方がいいんだけど、最低限それが何かみたいな事がぱっとひと目でわかるように書いてあってほしい)、プレゼンテーションをするには本質的にshallowな対象を扱わざるを得ない。