kbigwheelのプログラミング・ソフトウェア技術系ブログ

プログラミング・ソフトウェア技術系のことを書きます

PofEAA感想 はじめに~3章

はじめに

HTMLよりも優れたフロントエンドを望むためリッチクライアントが求められる

内容が古いため今の実情と乖離しているところもある

その理由は、パフォーマンスに関するアドバイスは、現実のシステム構成でパフォーマンスの測定を行ってみるまでは、当てにならないからである

現在になるまでこの点は一切変わってねえなあ。だからカオスエンジニアリングなど生まれたんだろうが

第1章

編集途中で消えて萎えたのでなし

第2章

レコードセットが何を指すのかわからん。 単なるRDBMSの行の集合ではなさそう。 → P36

レコードセット、つまりデータベースのテーブルの性質を持つ、テーブルと行で構成された包括的なデータ構造

第3章

3.2、ユニットオブワークという名前でDBデータ(レコードデータ)の書き込みを含めたオンメモリ上でのキャッシングの話をしているけど、 今の横にスケールさせるアーキテクチャではキャッシング機構が1サーバで完結しないので微妙な気がするね。 現代ではもっとシンプルなキャッシュとトランザクション手法が取られるんじゃないかなあ。

3.3、joinを使って複数のテーブルのデータを一気に返す方法、それはできるけどさすがに悪手だろw DBへのクエリ回数が膨大なシステムだとそうやって最適化する方法はあるかもしれないね。最終手段だろうけど。

3.4.1のオブジェクト型とリレーショナルデータベース間の関係情報の保持の仕方についての説明は非常によくわかる。 しかしその後の一意マッピングやレイジーロードといったアプリケーションサーバ内での頑張りすぎるキャッシング手法はあんまりイケてない。というか現代でやったら顰蹙モノの複雑さだと思う。

LOBに関してDB自体がその内包データを理解できないという欠点は、現代ではJSON型としてOracle, Mariaなど各プロダクトが限定的ながら解決しつつある感じ。

3.4の継承とRDBの問題については継承をそもそも否定的だからこの手法もややこしくなっているのだと思う。 たしかに説明されている3パターンは現実に取りうる解だが、そもそもオブジェクト指向とリレーショナルデータベースをうまいことマッピングするのがORマッパーなり各種DBライブラリの役割なわけで、継承という非常にオブジェクト指向らしい特徴をリレーショナルデータベースに持ち込む事自体が相当な悪手じゃねえかなあ。 しかしそれを考えるとオブジェクト指向RDBインピーダンスが発生しているという指摘は重い。DBの接合部分に全体の1/3のコーディング労力が割かれているというのもあながち大げさではあるまい。

3.5.1、相変わらず技術書の英訳はガバガバ。

The extra step only pays for itself when you have many commonalities, so you should use it when you have similar but annoyingly different physical data stores.

追加のステップは、多くの共通性がある場合にだけ有効なので、類似性はあるが異なる物理的なデータを格納する場合は追加のステップを使用するべきである。

data storesのstoresを動詞として約してしまっている。実際には名詞。 以下が適当か

追加のステップは多くの共通性がある場合にだけ有効なので、類似性はあるが異なる物理的なデータストアを2つ以上使用する場合に追加のステップを使用するべきである。

3.7 データベースのコネクションプールは本質的に状態を持ってしまっている(使い回されるコネクションが前回の利用の影響を受けていないことが保証できない。また接続されて最初のクエリとその後のクエリで挙動やレスポンスタイムなどが変わることもある) = immutableでない ことからあんまりよくないかも。サーバレスフレームワークが前提のアーキテクチャではコネクションプールは期待できないし、各リクエストの独立性を高めるためにも毎回コネクションを張り直す方式が取れるならそのほうが良い。またこの本自体にもそう書かれていた(意外)。

新しい接続の生成速度が速くなっているなら、プールする必要はない。

This advice leads to a couple of issues: making sure you have the connection everywhere you need it and ensuring that you don't forget to close it at the end.

この方法では、2、3の問題が生じるので、必要であれば接続し、終了時には忘れずクローズするべきだ。

ひどい訳。以下が適当。

この方法には次の2つの問題がある: あなたがコネクションを使うところすべてでそれを持つ(実質的に作る = コネクションを張る?)必要があることと最後にそれを確かにクローズしないといけないことだ。

しかしこれだけ面倒くさいRDBオブジェクト指向型言語の間のパターンを多数見せられると両者の間のインピーダンスの高さを実感する。 RDBを使うのって今は当然だけど本当にそうか?オブジェクト指向型言語でいいのでは? まあでもそれMongoDBか。

いいねが2,3個しか付かなさそうなニッチな情報でも、それでもQiitaにそれを書くべき理由

これで困ってるの日本で自分を含めて2,3人じゃねーかって情報がときにある。 そういうことに限って大きなブロッカーで数時間、場合によっては日単位でタスクを遅らせることがある。

そういった情報はQiitaで記事にしてもいいねはほとんど2桁行かない。

だが、逆に言うとその情報がウェブ上にあれば数時間、あるいは数日節約できる人間が日本に数人はいる。

上のはどれも確実に数時間単位で作業時間を節約してくれた記事である。 特に最後のそれはこの記事を見つけなければ解決が極めて難しかった可能性が高い。 僕はこれらのような記事を書いてくれるエンジニア同輩にとても感謝している。

このような人々に報う方法を考えたとき、いいねを押して感謝を伝えることもよいが何より良いのは 自分ができる範囲で似たように困るかもしれない、日本全国で2,3人の後続のために記事を書くことではないかと思うのだ。

だから、ニッチな情報でもQiitaへ記事を書くべきである。 初出の情報であれば素晴らしいが既存の情報などを整理したり情報を付け加えるだけでも良い。 きちんと調べて正しいと思ったことを書くことは重要だが、正直なところ間違った情報や推測ですら十分に貴重だったり価値があったりする。 間違ったり古くなった情報はコメントや修正リクエストで直していけばいいのだ。

1年遅く生まれたあなたのために、ニッチな情報でもQiitaへ記事を書こう。

「マイクロサービスアーキテクチャ」感想

O'Reilly Japan - マイクロサービスアーキテクチャ

おすすめ度: ★★★★☆

良かった。やんごとなき理由により非自主的に読み始めた本だったが想像していた数倍の価値がある本だった。 マイクロサービスの基本などが抑えられているのもよかったが、何よりマイクロサービスベースの組織やそれを支える体制など組織論的なところまで踏み込んでいるのが良かった。 それがことごとくそのときの会社の体制と正反対だったことは皮肉だったが。 かなり思想的に影響を受けたし、考えを補強してくれた点も多かった。

僕がDIを否定する理由

予防線

私が開発当初から関わったシステムでコード的に最大規模のものはScalaで書いた検索システムの2万行ほどのもので、あとは保守開発や1万行以下のサービスばかりです。 なのでその程度のへなちょこエンジニアの主張なんざ聞くに値しないという主張は一理あるかもしれません。 ですのでもっと大規模なシステムでは以下の理屈は通用しないというようなことはあるかもしれません。

きっかけ

これを見てそういえばScalaを始めたときcake patternなど勉強したことを思い出した。 今はTagless Finalというものがあるのか、なるほどと思いつつこの手のものをそういえば使わなくなったのはなぜか思い返したところ、これがDI技術であることが理由だった。 そういえばある時からDIは根本的に良くない気がして使わなくなったのだった。

そもそもDIとは

猿でも分かる! Dependency Injection: 依存性の注入 - Qiita など今はウェブ上に非常に理解しやすい説明があるので好きなやつを見たらいいと思う。

僕流に解釈して説明すると以下のようになる。

関数、プログラム、アプリケーション、サービスのどの単位でも必ずセットになるものがある、入力と出力だ。 そしてテスト*1とは特定の入力に対して期待した出力であるかを試すものである。例外はない。

基本的にテストするときは対象の外側から入力を与えてやる。関数であれば引数、クラスであればコンストラクタ引数、APIであればクエリパラメータやヘッダである。 しかしDI技術はこの原則を無視する裏技を提供する。直接テスト対象のプライベート変数(内部変数)へ値を注入(Inject)できるようにするのである。

僕が思うDIの欠点

直接テスト対象のプライベート変数(内部変数)へ値を注入(Inject)するDIは次の欠点を生み出す。

  1. (コンストラクタ)引数以外は不変であるというクラスの原則・直感を破壊する
  2. テストにおける入力の範囲が曖昧になる
  3. 範囲が曖昧になった結果、本来不変であるまたは不変にできるような値もDIで注入できるようにしてしまう
  4. 入力の変数が増えた結果入力のパターンが爆発的に増加してテストが書きづらくなる

ただ、以上のような欠点をDIを使っている人に何度かぶつけてみるとそれらは一理あるものの以下の要因により必要悪としてDIは使われているという反応が多かった。

  1. コンストラクタで変数へセットするオブジェクトを組み立てているとそこをモックすることが困難になる
  2. 多数の入力(=依存性)があるクラスではそれらをコンストラクタで決まった順番に渡すことは苦痛になりやすい

これらの反論もまた一理あるものであるが、どこか納得出来ないものを抱えてなかなかDIを積極的に使うことができなかった。

解決のヒント

DIについて調べたり軽く使ってみつつ数年経ったのちに読んだ2つのアイデアが解決の糸口になった。

一つは "TDD is dead. Long live testing." 一番最初に読んだ日本語の記事は忘れてしまったけど、この要約など良い。どうやらテスト駆動型開発は死んだようです。これからのCI 重要なのはTDD is deadといいつつ実際にはユニットテスト的な粒度の小さいテスト駆動での開発の否定で、機能テスト・統合テストなどと呼ばれる粒度の高いテストをちゃんとしましょうということが要点(だと僕は捉えた)。

もう一つはMicroservice。 これの良いところ悪いところいろいろあるが僕が目をつけたのはインターフェイスがWeb APIとして定義されること、中身が十分に(Microservice architectureという本によれば、たしか1,2週間で書き直せる、だったかな)シンプルであることだった。

この2つのアイデアを組み合わせて僕は以下のように今は考えている。

結論

  1. テストはAPIそのものに対して行われ、個別のクラス・関数に対しては書かない。さらに各リクエストがAPIサーバ内で相互に影響を与えない*2のであればモッキングが必要なのはAPIサーバが依存するDB、memcahceまたは外部のAPIサーバなどになる。つまりDIによる強引なモックの注入が必要なくなり、ひいてはDIも不要になる。

*1:ユニットテスト結合テストなど粒度を問わない

*2:DBサーバやmemcacheなど外部の永続化層で間接的に影響し合う分には問題ない