patorashのブログ

方向性はまだない

Rspecで環境変数をstubしていたのが動かなくなった件(解決済)

小ネタです。rubocopのバージョンアップを行ったら、新しいルールで怒られが発生しました。

 C: [Correctable] Style/FetchEnvVar: Use ENV.fetch('FOO') or ENV.fetch('FOO', nil) instead of ENV['FOO'].

Correctableなので、bundle exec rubocop -Aで自動修正を行ったところ、ENV.fetch('FOO', nil)になりました👍

これでテストを実行したところ、思わぬエラーが発生😵

環境変数をstubしているテストが落ちる

環境変数によって、とある項目をチェックするかどうかを切り替えている処理がありまして、そのテストのために以下のようにしていました。

context '環境変数BARに0が設定されている場合' do
  before do
    allow(ENV).to receive(:[]).and_call_original
    allow(ENV).to receive(:[]).with('BAR').and_return('0')
  end

  it 'チェックされないこと' do
    # テストケース
  end
end

※この書き方は、以下の記事を参考にしています。

qiita.com

該当箇所のコードは、元々はこんな感じ。

if (ENV['BAR'] || '1') != '0'
  # チェックする
end

これが、rubocopのAutoCorrectによって、こうなりました。(カッコが多かったため、手でも修正しています)

if ENV.fetch('BAR', '1') != '0'
  # チェックする
end

これが全くstubされないように…😢

原因

これはstubの定義がENV['BAR']の場合にしか対応していないためです。この変更に対応するには、ENV#fetchの結果をstubしなければなりません。

解決方法

環境変数の参照の仕方のstubをfetch経由に変更します。

context '環境変数BARに0が設定されている場合' do
  before do
    allow(ENV).to receive(:fetch).and_call_original
    allow(ENV).to receive(:fetch).with('BAR', '1').and_return('0')
  end

  # 略
end

肝は、with('BAR', '1')のところで、with('BAR')だとstubされませんでした。きちんとデフォルト値まで渡さなければなりません。

まとめ

環境変数をちゃんとstubしているのに何故!?と5分くらい考えてしまいましたが、当然っちゃあ当然です😅 環境変数をfetch経由で受け取る修正をした際は、テストのstubも見直しましょう。

「現場で役立つシステム設計の原則」読書会 vol.8

第8回の感想です。前回の感想はこちら。

patorash.hatenablog.com

会社のブログのレポートはこちら。

tech.rhizome-e.com

8章 アプリケーション間の連携

  • 1つのサイトであっても、様々なシステムが連携しあっている。
  • アプリケーション間の連携方式。
    • ファイル転送
    • データベース共有
    • WebAPI
    • メッセージング
  • うちでよくやる方式なのはファイル転送だろう…。オンプレの環境との連携とかがあるので仕方ない面はある。WebAPI連携は多少やっている。自分の担当製品とその派生製品で、API連携している。WebAPIはGraphQLで作っていっているが、ちょっとわかりにくい。自分の実装が中途半端だからだと思う。知見が足りない。
  • 非同期メッセージングは、RailsでいえばActiveJobのようなものだろう。Redisなどを介して処理をキューイングして、取り出して実行するような形だ。JSのMVVMの仕組みも非同期メッセージングと言えるんじゃないだろうか?Pub/Subのようなやつ。
  • WebAPIの仕組みのところは、RESTful APIの話が中心。更新や削除もPOSTで行うのはRESTに反するので、ちょっとどうかなぁとは思った。
  • このURL設計やレスポンスの設計も、ドメインオブジェクトを軸として考えるとスッキリしたものが作れそう。ただ、ここまで細かくドメインオブジェクト単位で取得しようとすると、WebAPIへのリクエストが増えていって大変そうな気もする。ただ、よいAPIとして書かれていた「多様性を維持しつつ、組み立ての負担が増えすぎない適切な大きさの部品を用意すること」というのはわかる。
  • SwaggerUIの話も出てきた。前にチームの仲間と話したことがあるやつ。スキーマ駆動開発をするにしても、そこでドメインオブジェクトの設計が重要になりそう。
  • WebAPIが出力するJSONなりXMLも、Viewだから、関心事とドメインオブジェクトの不一致は起きそう。Railsだと、そこをjbuilderを使って関心事にフォーカスさせるのかなと思う。そうでなければ、to_jsonだけで成立するからだ。
  • 複雑な連携に取り組む。のところ。WebAPIを作るところをずっと課題にしているにも関わらず、要望対応やリファクタリングをしているので、なかなか進めていないのだが、ここにヒントがあった。コア機能と拡張APIと個別対応APIを分けて考えて、それぞれ完成させていけばいいかなと思った。
  • マイクロサービスの話もあった。マイクロサービスについての知見をあまり持っていないので、一概には言えないけれど、この本に書いてある通り、試行錯誤がし辛そうだなぁと思った。サービス単位の分離を上手にやらないといけない。対象業務への理解が不十分な場合はモノリスで作っておき、後々分けていくのがよさそう。マイクロサービスについての勉強もしていかなければならない。サーバーレス&マイクロサービスが今後の主流になっていくことは間違いないとは思うが、それはある程度サービスが枯れてからかなぁと思う。
  • 非同期メッセージングをアプリケーション間で使ったことはない気がする…。dRubyを使えば、もしかしたらできるのか?

雑感

今回参加していたメンバーが自分以外、Web APIを作ったことがない(作ったと思っていない?)、かつ、利用したことがないようだった。世界には様々なPublicなWeb APIがあるよってことをWeb APIまとめサイトを紹介しながら教えた。

Heroku CLIAWS CLIにしても、HerokuやAWSのWeb APIにリクエストを投げてるんだよって話をしたら、なるほどって理解してくれた感じに思えた。

Web APIを自前で作るにしても、外部に公開する場合には、SDKとか作って利用しやすくしたようがいいよねって話したら、「SDKって何ですか?」っていうメンバーもいたので、その辺りから説明した。見たこともないっていってたけれど、「いや、君のプロジェクトのGemfileにaws-sdkってあるやろ!?」と言ったら、「あ、ほんとだ」という感じで、ほとんど意識したことがなかった模様。まぁ実際に使う機会がないとわからないもんだろう。そういうのも知ってもらえるいい機会になったんじゃないかなぁと思う。

Web API設計のここ悩むよね~的な話はほぼできず(だってみんな作ったことがないし…)、今回はひたすら説明要員の役割だった。まぁみんながこんなWeb APIがあるのか~とか、マッシュアップアワードとかあるのか~って知ってもらえたと思うので、それで良しとする!!

「現場で役立つシステム設計の原則」読書会 vol.7

第7回の感想です。前回の感想はこちら。

patorash.hatenablog.com

会社のブログのレポートはこちら。

tech.rhizome-e.com

なお、この回の会社のブログのレポートの担当が私だったので、書きたいことはそちらにほぼ書いちゃってる…。

7章 画面とドメインオブジェクトの設計を連動させる

  • 画面にロジックを埋め込むと複雑化してくるのはすごくわかる…。
  • 表示のためのロジックと業務ロジックを分ける意味では、ビューヘルパーを使ったり、デコレーターを噛ましておくといいとは思う。Railsの場合はビューヘルパーはグローバルメソッドのようになってしまうので、あまり好みではないけれど、契約による設計を重視した形にすれば、あまり散らからないかもしれない。定義場所は整理できるし。
  • 今作っているサービスだと、何でも入力画面になっている。関心事を分離することができれば、入力ステップ数は増えるものの、ドメインオブジェクトで整理できそうに感じた。なんでも入力画面だと、全部入力するのに時間がかかって疲れて離脱してしまうので、本当によろしくない。後で入力すればいいものまで、必須にする必要はない。
  • 最近感じているのだが、バックエンドのドメインオブジェクトとフロントエンドのドメインオブジェクトが流用できないのが歯がゆい。Rubyドメインオブジェクトを作り、さらにJSでドメインオブジェクトを作らなければならない。ほとんど似たようなものを、だ。そしてそれぞれをテストしなければならないのが面倒。共通で使えたらいいのにと感じる。
  • pixtaが次のシステム刷新でRailsをやめてTypeScriptのフレームワークを使うという話をしていた*1と記憶しているのだが、それはバックエンドでもフロントエンドでも同じドメインオブジェクトを使うためなのではないか?JSでは表現力がイマイチだが、TypeScriptだと型も列挙型も使えるので表現力もある。と読みながら思った。
  • 検索項目単位でドメインオブジェクトを作っていくと、扱いがやりやすくなりそう。
  • タスクベースのユーザーインターフェース、確かに最近多い。入力するものが限られるので、入力する側もサクッと変更できてよい。内部の設計はタスクベースに分けておくべき、とあったので、そうしておきたい。
  • どこに画面用ロジックを集めるか問題。論理的なビューに関してはドメインオブジェクトでいい、というのはわかる。しかし結局苦しんでいるところは物理的なビューだったりする。物理的なビューに関してはデコレーターに閉じ込めるようにして、論理的なビューはドメインオブジェクトに寄せるようにすれば、納得感はあるかなと思う。
  • 画面とソフトウェアを関係づける。わざわざBookSummaryクラスを作ってしまうのか。ぱっと見、データクラスじゃない?という感じがあるが…。まぁメソッドを定義してないだけか。これもまた、一覧と詳細では関心事が違うよということか。ついついRailsで考えがちなので、モデルで取ったデータをわざわざサマリ用のクラスに詰め替えるんかい、と思ってしまったが、普通に考えたら、データ取得用のクラスでgetSummariesを定義して、そのサマリ用クラスのインスタンスの配列を返せばいいわけだから、Railsじゃなければ、そっちのほうがスッキリするかなと思えた。
  • 画面もドメインオブジェクトで管理したい、となってくると、MVVMを使うようになっていくことになるのは必然。そりゃあReactやVueが流行るのもよくわかる…んだが、よくわかってない人がやってるのを見ると、画面と値が連動していて便利、止まりになっている気がする。
  • プレスリリース、リリースノート、利用者ガイドの内容がドメインオブジェクトに反映されているのが良いソフトウェアという話。言われてみれば、たしかにそうだなと思う。しかしこれら全ての整合性をとり続けるのは相当難しい。うちの体制だと、メンバーが少ないのでそちらのメンテに手が回りにくい。やはり、せめて1製品につき最低でも3人体制くらいにはしたいものだ。

雑感

物理ビューと論理ビューの分離どうするかというところを課題として考えているのだけれど、Decoratorに論理ビューを作るメソッドを定義し、ViewHelperに物理ビューを作るメソッドを定義していくように分けたら良いかなと思うようになった。という話を会社のブログで書いた。最近はこれを意識してRailsのコーディングしているのだが、どうにも気持ち悪いことが起こる。

気づいたらDecoratorでViewHelperのメソッドを呼んでしまっていたりする。link_toとか。リスト出力でリンクを出したいときなんだけれど、このときに渡したい引数は文字列の配列なので、リンクのHTML文字列を含んだ配列にしておきたいのだが、それを呼ぼうと思うと加工が必要になる。そこをどこでやるか?という話になるんだが、なんだかんだでDecoratorがしっくりきてしまう。

Decoratorはそもそも装飾するのが担当だから、それでいいんじゃないか?とも思うのだが、論理ビューを担当させるだけにしたほうがいいのでは?という結論になったにも関わらず、現実だと結局物理ビューも担当しちゃうようになるから、悩んでいる。

モデルに論理ビューのロジックを移動させて、Decoratorは物理ビューを担当させるようにしたほうが、スッキリするかなぁと思い始めている。Railsのモデルはドメインオブジェクトでもあるので、そのほうがスッキリするかなぁ。

この辺りで悩んでいるのでご意見求めたい。

「現場で役立つシステム設計の原則」読書会 vol.6

第6回の感想です。前回はこちら。

patorash.hatenablog.com

会社のブログのレポートはこちら。

tech.rhizome-e.com

6章 データベースの設計とドメインオブジェクト

  • データベースには事実を記録する。事実にはNULLなどない。基本的にNOT NULL制約を付けなければならない。これは中国地方DB勉強会に参加したときだったと思うが、そう言われたので納得しており、それ以降はNOT NULL制約を必ず付けるようにしている。レビューのときにも「NOT NULL制約を付けろおじさん」になっている。
  • NULLが存在していいのは、外部結合(OUTER JOIN)や集約のときくらいなもの。文字列の場合は、デフォルト空文字でいいのでNOT NULLにする。
  • データの整理に失敗しているデータベース、めちゃめちゃ見に覚えがある…。今だったら絶対にこんなテーブル設計にしないと自信を持って言えるのだが、昔はそこまでデータベースの知識がなかったので、酷い設計をしていた。現在進行形で苦しんでいる。少しずつテーブル含めてリファクタリングしているが、既に稼働しているシステムのリファクタリングになるので、変更の影響度を調査しながらの修正になるので、修正にかかるコストが大きい。最初の設計時にレビューを受けたりして、ちゃんと見直しておくことが大事。
  • 特にRailsの場合はgem由来のテーブルがあったりして、それが制約が緩々だったり、ドメインオブジェクトになっていなかったりするケースもありそう。本に出てきたテーブルの都合が反映されたコードになっていたり等。
  • ここに書いてあるNOT NULL制約、ユニーク制約、外部キー制約などは基本的なものなので、出来る限り使うこと。データベース側の機能のチェック制約やenumなどを使うと、更にデータを守ることができる。
  • コトに注目するデータベース設計の話。楽々ERDレッスンの話を思い出しながら読んだ。記録のタイミングが異なるデータはテーブルを分ける、というところ。マジでそれ!と思った。今の仕様がそうなっていなくてかなり辛い。コトを記録するという発想がないと、モノに関連するもので一緒くたに詰め込んでしまおうと考えてしまいがち。ドメインオブジェクトの考え方を持っていたら、せめてドメインオブジェクトと一致するテーブルを作ったりしていたのだろうが、当時はそんなことは知らないのでできなかった。
  • コトの記録ではUPDATE文は使うべきではない、というのは、なるほどと思った。取り消しを記録する。元データ、取り消しデータ、新データ。INSERTだけで実現可能。例えば、予約テーブルがあったとして、予約をキャンセルされたら、予約取消テーブルに外部キーとして予約IDを持たせてINSERTするということだろうか。
  • カラムの追加はテーブルの追加をする、のところ。これくらい大胆な発想のほうがいいんだろうか。確かにそのほうが副作用がないけれど、テーブルが増えるし、JOINが大変になりそうな気もするんだが…。プログラムへの影響は少ないのはそうだけれど。事実を記録する観点だと、そっちのほうがいいのかな。たしかに途中から追加すると、NOT NULL制約が付けられなかったりする。データの関連付け処理を全て終えた後にNOT NULL制約を改めて付けたりしていたけれど、それだとNOT NULL制約が存在しない期間があるので、気を遣うことがある。
  • 状態の参照。コトの記録を徹底すれば、状態の算出は可能。動的に出力するのはパフォーマンス的に辛い場合もあるので、この辺りはRailsだとカウンターキャッシュを使ったり、DBのマテビューを使ったりしている。
  • DELETE, INSERTを使ったほうがいいケースの話。ロジックがシンプルになる。記録の同時性に違反するから、という方針を持っておくと、UPDATEで対処するべきデータかどうかの判断に使えるのでよさげ。
  • 残高更新は同時でなくてもいい、一か所でなくてもいいという考え方は、柔軟性が生まれる。事実はDBに保存し、状態はKVSに持たせるようにすると良さそう。状態の参照は頻繁に行われると想定すると、KVSのほうが向いている。
  • オブジェクトの設計とテーブルの設計。コトを記録するテーブルとドメインオブジェクトがほぼ1対1に対応することがある。しかし、似て非なるものという意識を持っておくべき。ドメインオブジェクトとデータベースのアクセスは、基本的に疎結合にしておいたほうがいい。そうでないと、互いに引っ張られ過ぎる。ドメインオブジェクトには業務ロジックを、データベースには事実の記録を。関心事が異なる。しかしそうなるとやはりRailsだと難しい。RailsActiveRecordドメインオブジェクトでもあり、データベースへのアクセス手段でもあるからだ。
  • Railsを使うんなら、それを分かったうえで、「それはそれ、これはこれ」と割り切っていくのが、現段階ではよさそう。

雑感

データベースの章なので他の参加者に比べて感想が多くなってしまった😅 みんな、あまり制約に気を遣った経験がなかったとのことだった。最近気を遣い始めたというメンバーもいた。後輩氏は私が「NOT NULL制約を付けろおじさん」になっているのでレビューで散々指摘されてきたことと、去年、楽々ERDレッスンを読むように促していたこともあって、この辺りに関してはできるようになってきていると思う。

参加メンバーのプロジェクトでは、状態の導出でパフォーマンスが悪化しているケースがあるということだった。状態テーブルを作ったほうがいいんじゃない?とか、カウンターキャッシュなり、マテビューを使うとかで対処できるよって話をした。

状態に関してはKVSに持たせるとかでもいいと思う、という話をしたのだが、そもそもKVSとは?というメンバーがいたので、そのあたりも説明した。「Redisとか普段使ってるんちゃうの?」と言ったら、「多分使ってると思うんですけど意識して使っているわけではないからほぼ分からないのと同じです」とのことだった。うちのプロジェクトだと、ActiveJobのキューの管理に使っていたりするわけだが、まぁそういう感じで、入れてるけれどActiveJobでよしなにしてるという感じなのだろう。

「まぁでもAWS使ってやっていくんなら、ElasticCacheとか使うことになると思うよ」とか、「Azure使うんでも似たようなものはあるはずだから、その辺り調べて使えるようなら使っていったらパフォーマンス改善するよ」という話をした。「KVS自体が頭になかったので考えもしなかったのですが、たしかに良さそうですね。調べてみます」と言ってもらえた。

UPDATE文を使わないケース、どういう時?という話も出た。予約の取消の場合、取消をお願いした記録が残ってないとマズくない?というケースでディスカッションした。あと、家を建てようとしたときの打ち合わせで決まっていったことは常にINSERTしているなぁと思った、という自分の実体験の話をした。

「こういうテーブル設計について勉強するにはどうしたらいいんですかね~?」と言われたが、後輩氏が「それはやっぱり楽々ERDレッスンを読んでほしいですね」と発言してくれた。頼もしくなってきていて嬉しい限り😂

楽々ERDレッスンの感想記事はこちら。

patorash.hatenablog.com

「現場で役立つシステム設計の原則」読書会 vol.5

第5回の感想です。前回はこちら。

patorash.hatenablog.com

会社のブログのレポートはこちら。

tech.rhizome-e.com

5章 アプリケーション機能を組み立てる

  • アプリケーション層がサービスクラスとモデルになるのかな?(Rails脳)
  • 複数のモデルにまたがるような複雑なビジネスロジックをサービスクラスにするもんだと考えていたのが、そうでないことに気付かされた。つまり、アクションでやっていることは、殆どがサービスクラスにしたほうがよいことだ。
  • サービスクラスでドメインモデルを組み合わせて処理するけれど、複雑になりやすい。原因の1つに、プレゼンテーション章の関心事に振り回されるって書いてあったが、これはアクションでやっていることがまさにそうであるので納得。
  • サービスクラスが複雑になるということは、ドメインモデルに成長の余地があるということ。サービスクラスに業務ロジックを書くくらいなら、ぎこちない名前でもドメインモデルを作ること。これは意識しないとすぐにロジックをサービスクラスに書いてしまいそう。というか現在が絶賛書いている状態…。リファクタリングしたい!
  • サービスクラスも、小さいサービスクラスを作っても良い。登録系と参照系に分けるのが基本というのはわかりやすい。登録系は副作用があるけれど、参照系は副作用がないので、変更を加えたときにテストしなければならない対象を考える範囲が減るのはいい。
  • 例外を発生させてるのが、どこでも使えるのでいいなと思った。
  • あと、契約による設計の話と防御的プログラミングの話が出てきた。これはプリンシプル オブ プログラミングを読んだ際に感銘を受けたので、チームのコーディングルールとしても、契約による設計を取り入れている。でもやっぱり同時実行の可能性を考えてチェックを入れたりはするんだなと思ったので、その辺りは見直したほうが良いかもしれない。
  • サービスクラスにシナリオクラスという名前をつけるの、イケてる。業務シナリオでプレゼンテーション層から分離できるから、テストしやすくなるのが最高。
  • データベースの都合から分離する、のところ。Railsはかなり分離しづらい。まぁできなくはないけれど、そういう視点を得たエンジニアでないと難しそう。メソッドにしていくくらいしかないだろうか?

雑感

参加者のほとんどが業務でRailsを使っているので、「Railsで考えると、こう」というふうに置き換えて考えてしまいがち(自分含めて)。他のアーキテクチャを見たことがない人は言葉で混乱しているみたいでした。 対照的に、.Netを使っている参加者は、業務で使っているのが3層アーキテクチャと、本に載っているJavaのコードに近いので、よくわかるとのことでした。

この本を読めば読むほど、RailsではないDDDをやりやすいフレームワークのほうが大規模開発には向いてるだろうなぁ~とふつふつと感じてます。まぁRailsはOne Person Frameworkだからなぁ~という話をしたりしました。

world.hey.com

あとは契約による設計についてと防御的プログラミングに関する認識について意見交換したり等…。引数の型が違ったら例外を上げるコードを書いてるという話があったので、それ契約による設計じゃなくて防御的プログラミングのほうやでって言ったり。

契約による設計は契約があるのだから、そもそも引数に異なる型の値を与えるのは、基本的に呼び出した側の責任となるはず。メソッド側で型が違うかどうかを毎度毎度チェックしてたら大変じゃんっていうのが私の意見というか契約による設計に対する認識。その代わり、ドキュメントをしっかり書いて、どの型を与えるべきかを明示しなければならないので、yardをしっかり書くことっていうルールにしてるよって話した。

だいたい、こんな話してたと思う。

  • 「とはいえ1行、型のチェック入れるだけじゃないですか?」
  • 私「だったら引数がある場合に全部のメソッドに対して型チェック入れてくの?って話になるので、面倒じゃない?」
  • 「うーん、たしかに…。しかしRubyだと実際入れられちゃうじゃないですか。そこどうするんですか?」
  • 私「だから、契約なんだから、そんなことをする人は契約違反でしょ?」
  • 「いやまぁ確かにそうなんですけど、実際入れられちゃうじゃないですか」
  • 私「そこをめっちゃ気にするんならRuby使うべきじゃないんじゃない?Javaなら起きないよ」
  • 「うーん、そうですねー…」
  • 私「この本に載っているように、canWithdrawメソッドのような可能かどうかのチェックを入れて、そもそも呼ばれるのを事前に防ぐべきだと思うよ」
  • 「なるほど」
  • 私「同時に呼ばれたりして副作用が起きかねない場合は、本の中のwithdrawメソッドでも例外上げてたりするから、そうするべきかなぁ」

意見交換したり、認識の確認とかできたので有意義な会となりました。

「現場で役立つシステム設計の原則」読書会 vol.4

第4回の感想です。前回はこちら。

patorash.hatenablog.com

記事の投稿日付は連続になってますが、読書会自体は週に1度の開催です。

会社のテックブログのレポートはこちら。

tech.rhizome-e.com

4章 ドメインモデルの考え方で設計する

  • 要件定義のタイミングで色々と質問をしてドメインモデルを設計する上でのヒントを聞き出すことが大事だなと思った。
  • ドメインモデルの作り方。やはり全体と部分を行ったり来たりしながら徐々に洗練させていくものだよなぁと改めて思った。設計通りにはうまくいかないことが多い。業務フローをまとめて、そこから俯瞰して何が重要そうかを見つけていく。
  • ドメインモデルは独立した部品でなければならない。部品同士を組み合わせていくのはいい。
  • 業務の関心事は、ヒト/モノ/コトで整理できるとあった。これは楽々ERDレッスンに書いてあったことに通じるなと思った。楽々ERDレッスンではリソースとイベントとなっていたが、まずイベントを洗い出せという話だったかと思う。この本でも、コトを整理の軸とする、とまとめに書いてある。ついついヒト/モノのリソースだけに注目してDB設計しがちなので、そこは気を付けるポイントとなるだろうなと思った。
  • 業務ロジックにもパターンがある。パターンを認識しておくと、応用が効く。たしかに、大体がこの4パターンかなと思う。
  • 業務を表現するのがドメインモデルなので、名前がより具体的になっていく。そのため、どこに何が定義されているのかを探しやすい。抽象度を高めることが汎用的なプログラミングには大事だけれど、ドメインモデルについては全くの逆。暗黙知を明らかにしていくという役割を担っているということか。暗黙知の言葉が、クラス名やメソッド名になっていくとわかりやすいコードになりそう。
  • 開発者が業務の用語を正しく理解して使うことが重要。そうすれば、ドメインモデルが作りやすくなる。
  • 関係をわかりやすく図で表現してあると全体を俯瞰しやすい。UMLで表現できるものばかり。
  • その業務分野について興味をもって学ぶというのが大事。プログラムだけ上手くなろうとしても片手落ちで、我々は業務知識を学ばなければならない。会計ソフトを作っている会社のエンジニアが簿記の資格をとったりする話もある。自分の場合は、弊社の社長や営業さん達がお客さんとの会話で得られたものを定例会の時に教えてくれたりしたので、それがよかった。客先にいって質問する機会も何度かあったが、その時にも、どういうところを重視しているのか?などを質問できて学べることがあった。会話能力が基本スキル、というのはそうだろうなと思う。
  • ある意味、ズケズケと聞いて認識を合わせていくようにしないと、全然違った時のリカバリが難しい。プログラムだけならまだしも、信用が落ちる可能性もある。なんで聞かなかったの?とか、なんで確認しなかったの?とか。
  • ドメインモデルの設計とは、より良い回答を探し続けること。改善し続けること。
  • やはり終わりはない。常にリファクタリングし続けていく必要がある。プログラムもだけれど、自分自身も学び続けなければならないんだなぁと改めて思った。

雑感

ドメイン知識を付けながら、プログラム能力も上げていかなければならないので、テクニックだけに絞って勉強しててもいかんよねという話をしたり、コミュニケーション能力大事だよねっていう話をしたりしてました。 現在の開発メンバーは、わりかし大人しい人が多く、あまり質問しなかったりするので、会話を引き出すためのキッカケを作る練習とかはしたほうがいいんじゃないか?って話したかなと思います。

例えば、感想のところに書いた「営業さん達がお客さんとの会話で得られたものを定例会の時に教えてくれた」という話1つとっても、営業さん達が能動的に開発チームにフィードバックをくれるわけではありません。もう議題がないかなというあたりで「そういえばこの前、操作説明会をされてたと思うんですが、お客さんの反応ってどうでした?」とか、「この前追加した機能について、お客さんから何か反応ありましたか?」とか、聞いてみるわけです。そうすると、「あー、あれは喜ばれてましたよ」と言ってもらえたりするので。

ネガティブフィードバックはこちらから言わなくても言ってもらえるんですが、ポジティブフィードバックはなかなか聞けなかったりするので、こちらから聞いていく姿勢も大事です。

「要件定義に関しては、どういうふうに要件を聞き出していけばいいんでしょうか?」という相談があったので、チェックシートを作っておくといいんじゃないか?という話をしたり。お客さんにチェックシートを記入してもらうわけではなく、こちらが質問したかどうかのチェックシートとして活用すると、抜け漏れを防げるんじゃないかな?会話の糸口にもなるんじゃないかな?と。

日々勉強して、リファクタリングを繰り返しながら正解に近づけていくのが王道だなと、改めて思いました。

楽々ERDレッスンについて触れましたが、こちらも感想は記事にしてあるので、そちらもどうぞ。

patorash.hatenablog.com

「現場で役立つシステム設計の原則」読書会 vol.3

第3回の感想です。前回はこちら。

patorash.hatenablog.com

会社のブログのレポートはこちら。

tech.rhizome-e.com

3章 業務ロジックをわかりやすく整理する

私が書いていた箇条書きのメモ。

  • データクラスと機能クラスの話。真っ先に思い浮かんだのは、Fat Controllerである。モデルに機能を書かないでコントローラーのアクションに処理をまとめてしまうせいで、再利用できないようになってしまうやつ。やはり悪手として紹介されていた。どこに業務ロジックが定義してあるかがわかりにくくなる。
  • そして、共通化の手法として、汎用クラスの話があった。UtilsやCommonだが、これらもよくないとは直感的に思っていたが、やはりそうだった。同じような処理がたくさんできて、使い分けがわからなくなるというのは、ちょうど前回の読書会の感想で参加者メンバーの1人が言っていた話と同じだなと思った。
  • 前にいた会社(12年以上前)だと、メンバー全員がオブジェクト指向を全然理解してなかったので共通化処理を継承元のクラスにガンガン追加していくというやり方を取っていたことを思い出す。当時はテストを書く習慣もなかったので、今考えると相当恐ろしい。
  • Railsのモデルはデータと業務を寄せていくところなので、まさにここに書いてあることに合致する。しかし、現状のプロジェクトだと値オブジェクトを使ってないから、まだまだ半分正解っていうところだろうか。
  • メソッドは必ずインスタンス変数を使うっていうのはよい指針だと思う。
  • クラスが肥大化したら分けるというのも、値オブジェクトを導入するいいタイミングっていうことなのだろう。共通で使えるパーツが増えていくと、再利用性が増すのでとてもよい。
  • パッケージにまとめる話。クラスを宣言するときにpublicって付けなかったらパッケージ内のみのスコープのクラスになるという話だったかと思う。他のところで通用しない、プロジェクト単位では再利用できない業務ロジックをそのパッケージ内に収めるという意味ではよさそう。これはたぶんRubyではできない気がする。パッケージはないけれど、それと似たようなことができるのが、モジュールで名前空間を作ることなので、そこは積極的に利用していきたいところだが、コードがかなり冗長になるので、やりすぎには気をつけたい…。Javaだと、importでパッケージを指定するので、あまり冗長にならない。
  • 3層とドメインモデルの話があったが、この3層をきれいに分けるパターンへのアンチテーゼみたいなのがRailsで、Railsだとデータソース層とドメインモデルが一体化したのがモデルになっている。そのおかげで高速に開発できる反面、複数のドメインモデルを使ってデータソースに保存する場合に、どこのモデルにロジックを書くべきかに困る。そして、どれかのモデルに書いてしまうと、そのモデル同士が密結合になってしまう。そこそこ大きなRailsアプリになってくるとぶつかってしまう壁みたいなものだ。この本を読んでいるとRailsの良いところと悪いところが同時に見えてきて面白い。

雑感

大体のメンバーが、Fat Controllerの話に似ているのでビジネスロジックはModelに持っていくやつ、という感じのことを言っていたように思います。そこら辺は同じチームの後輩氏には最初のほうで叩き込んであるので、普段からちゃんとできているなぁと思います。他のチームも多分そのあたりは大丈夫なはず…。

しかしまぁ、ドメインオブジェクトを意識したコードにはまだできていないので、まだまだ半分正解っていうところです。

最近、自分の書くコードはこの本の影響を受けていて、コトを意識したドメインオブジェクトを作る等して、コントローラーから更に処理を剥がしてドメインオブジェクトもしくはサービスオブジェクトにして関心を分離し、テストしやすいように変えています。コントローラーはそれを呼び出すだけ(新しく作ったところだけですが)。

ここまでテストしてあると、わざわざシステムテストで確認する必要も感じなくなりました(JSが絡むフロントエンド側のテストは別だけれど)。System Spec(いわゆるブラウザテスト)は止めて、APIのテストと同じくRequest Specで済ませました(権限チェック等によるリダイレクトのテスト等はしておきたかったので)。

CommonとかUtilsとかを作るのは、ドメインオブジェクトを作ることから逃げているなぁと考えるようになったので、どこかのタイミングでリファクタリングで撲滅させておきたいと思います。