読者です 読者をやめる 読者になる 読者になる

CureApp開発者ブログ

アプリで治療する未来を創造するCureApp, Inc. のエンジニアブログです

また濃密な無料ピザ会をやってしまった...

Schoo x CureApp

先日、ピザを食べながら、ベンチャー企業のCTO含むエンジニアと、とっても近い距離で 現場の話が聞ける会を開催いたしました。 いつものように、参加者の方とピザを食べながら語り合う濃密な会になりました。

共同でやったのは、今や飛ぶ鳥を落とす勢いで成長する、オンライン動画学習サービスを運営するSchooさん。

そして、アプリが治療する未来を創造するという、知る人ぞ知る医療系ベンチャー企業のCureApp。 場所は表参道駅からほど近い青山アクセラレーションセンターです。いつも場所をご提供いただきありがとうございます。

f:id:cureapp-dev:20160610092418j:plain

内容

いらっしゃったお客さんも合わせての座談会形式で行われまして、筋書きなしで進んでいきました。 ここではそのなかで面白かったテーマをピックアップしてみました。

1人のほうが、逆に判断が遅いケースもある

1人で全部決めるから意思決定が早い、というのは、ある意味では間違っている。 「壁打ち」「後押し」となるもう一人の視点があることで、意思決定がより早くなることもある。

全部捨てる覚悟で書け!(書くべきだった)

初期は、とにかくプロトタイプを作って事業を回すことが求められる。 初期と中期以降ではシステムに求められる内容も全然違ってくるのだから、 システムは総とっかえできるぐらいのものでいけばいいのかも。

広く浅く vs 狭く深く

大きな組織で働いていた時には、深くができる。 小さい組織だと、掘り下げる暇はなく、大きい組織に比べてクオリティには差がでてしまうかも。

大きな組織はなぜルールが多くなるのか

ルールというのは、言語化されていない規範の枠に収まってくれない人がいて、 はじめて言語化されるもの。 例えば「いろいろ更新」というコミットログをする人がいて、問題になって、 「やっぱりコミットログは丁寧に作ろう」となる。 そうやって、ルールがどんどんできていってしまう。

「社長には分かってもらえない」は違う!?

「社長はビジネスサイドで、開発サイドの事情など分からない」は、違う。 会社の利益のためにやるのであれば、お互いに会社のほうを向いて、説明しあえば分かるものだろう。

(とはいえ、そういう社長ばかりではない...との意見も...)

開発者は、なぜベンチャー企業で働くの?

  • 事業への共感
  • 影響範囲の大きさに対するチャレンジ精神
  • 働きたい人と働ける

チームの粒度、どのぐらいがいい?

two pizza ruleといって、2枚のピザで足りるぐらいの人数がいいと。 それ以上大きかったら、分けたほうがいい、人を増やすと情報共有のコストがすごい。

感想

SchooのCTO & エンジニアの方々の話が面白くて、自分自身が楽しめる会になってました。

これからも、CureAppは小規模でも濃厚なイベントを開催していくので、皆様、 これからもconnpassをチェックしてくださいませ!

DDDの勉強会 DDDConf01を開催しました!

さて、昨日5月11日、DDDConf01を開催いたしました。 場所はいつもお世話になっている表参道にある青山アクセラレーションセンター。 会場は満員で、いつにもまして大盛況で終えることができました。 お越しいただいた方、ありがとうございました。 トーマツのスタッフの方、遅くまで対応いただきありがとうございました。 f:id:cureapp-dev:20160512094659j:plain f:id:cureapp-dev:20160512094606j:plain

内容

今回ご登壇頂いたのは、DDDを社内で実践している4社。 弊社のほかasoview!さん、ChatWorkさん、UZABASEさんです。

asoview! 藤岡さん

www.slideshare.net

非エンジニア含め納得したユビキタス言語「チケットブック」がブレイクスルーとなり、より深いモデリングに到達したという 社内の事例をご紹介いただきました。言葉とGoogle画像検索結果がチーム全員の理解を助けることにつながったとのことで、DDD実践のメリットとしての良い例でした。

ディスカッションテーマは、「DDDにドキュメントは必要なのか?」。 詳細なドキュメントは不要という大枠は各パネラーで一致していましたが、 コードそのものが仕様なのか?ということには両論ありました。コードには大枠の意図が入らないと加藤さんは指摘しました。 その他、

  • 自動ドキュメントで担保
  • テストケースで担保
  • モデル図だけは作る

といった話題がのぼりました。

ChatWork 加藤さん

集約を適切に決めないとデッドロックが発生する事例を紹介していただきました。 集約の境界を決めるうえでは、

  • writeがどこに多数発生するのか
  • 削除した際に集約内モデルは同時に削除されるようなものなのか
  • 集約の制約条件の整合性をどのレベルで担保するか

等考慮すべきポイントをわかりやすく挙げていただきました。

ディスカッションでも、他のパネラーがどのように集約を扱っているかという話題になりました。 無限なデータと有限なデータで分ける、などの例が出ました。 UZABASEさんの発表も集約レベルでキャッシュすることの話で、 「集約」は今回の勉強会での重要なワードとなりました。

UZABASE 杉浦さん

newspicksのBtoB広告配信サービスを遅延なく行うという段階でDDDを採用し、 高速化につながったという事例を紹介していただきました。 データを集約レベルでキャッシュすることで、すべてのビジネスロジックをローカルに完結して適用可能となり、 高速化につながったと。トランザクション境界と集約は単位としてほぼ同一だ、という話でした。

ディスカッションは、「チームにいかにDDDを浸透させるか」。

  • Pull Requestのフィードバックによる教育
  • 非エンジニアを含めたワークショップ

などが挙がりました。ワークショップの例としては、加藤さんから「自動販売機のモデリング」の例がありました。 皆、「釣り銭」という概念に気づかないけれどそこがないとサービスを完遂させることはできず、さらによいUXを提供するには 釣り銭効率という概念を知っている必要がある、など題材としてとても興味深いものでした。

CureApp 鈴木

DDDの考え方に基づいて1から実装した時に、ビジネスロジック以外のコードを書く手間が大きかったため、 それをまとめてbase-domainというライブラリに落とし込んだという話をしました。

  • factoryにおいて、入れ子になった集約などのモデルを生成するロジックが複雑になりがち
  • repositoryの中でインフラ層のコードが書かれすぎる
  • クラスの呼び出し方法を統一しないと依存関係が複雑になる

などの問題を解決するためのJavaScriptライブラリです。インフラ層の切り分けができること、またUniversal JSにこだわり どのプラットフォームでも動くように設計しています。

ディスカッションテーマは、「DDDに向いた言語は?」。 クラスという概念で書き通したいということからJavaを推すという観点や、言語はステージに合わせて変えてよいという意見がありました。 スピードが求められるフェイズではコンパイルの要らない言語での高速なイテレーションが重視され、 安定・継続・大規模を目指すフェイズでは型がある言語は開発を助けるだろうと考えています。 弊社は、ビジネスロジックはプラットフォーム依存しないべきという理由からJSを採用していますが、 今後はSwiftC#などの言語にも注目です。

まとめ

DDDに興味のある方々が集まって雑多にDDDのトピックを話すことができました。 今後は、もっともっと深い部分を議論できるような熱い場になるといいなと思います。 自身も勉強になり、とても刺激的な場でした。

メンバーが増えるので、開発方針をまとめました。

4月から、新たに3名のエンジニアがCureAppに加わります! 今まで、チームとしてどうあるべきか、というのもあまり考えずに走ってきました。 ということで、ここで一度振り返って、どういうチームでありたいか、ということをまとめてみました。

長くて暑苦しくなってしまうものですね。 これを、初版としてどんどん改良していきます。


CureApp開発チームの目指すもの、それは

それぞれが別な得意領域を持ちながら広くシステムを理解し、開発に参加できるチーム

社員は、どのプロジェクトにアサインされても戦力になるようにする。

そのために、

  • マルチプラットフォームな言語を選択する (まずはJavaScript)
  • 皆が思う「良いコード」が同じになるように、設計思想を統一。
  • 同期/非同期コミュニケーションにより情報を共有。
  • 確かな方法で技術選定し、優れた技術を共通して使う。
  • 全システムを俯瞰できるよう、専門領域(ドメイン)への理解を深める。
  • 複雑な手順を自動化する。

マルチプラットフォームな言語を選択する

言語の統一は、 - コードの共有 - 習熟スピードの速さ - 思考切替えのスムーズさ

に理がある。 なんといってもプラットフォーム間でのビジネスロジックの共有という 究極のDRY (Don't Repeat Yourself)が実現可能だ。

なぜJavaScriptか ~言語の比較~

マルチプラットフォームに利用できる言語を独断で比較してみた。

言語 プラットフォーム多様度 人気 将来性 ライブラリの充実 手軽さ 堅牢性
JavaScript × →▲
Java
Swift
C#

利点

なんといっても近年のマルチプラットフォームといえばJavaScriptの存在が際立つ。 Webブラウザ、Node.jsはもちろん、「ハイブリッド」型のスマートフォンアプリ開発(Titanium, React Native, PhoneGapなど)、またElectron、Google App Scriptなど、JavaScriptで開発できないプラットフォームはほとんどない状態だ。 そしてそれがまた人気を加速させる。ES2015の登場など、まだまだ言語として 進化を続け、将来性もある。優れたJavaScriptのエコシステムであるnpmはここ5,6年で爆発的に普及した。 背景にはJavaScriptの手軽さ、柔軟さがあるだろう。

克服可能な、欠点

JavaScriptの唯一(しかし無視できないほど大きい)欠点。 それは言語そのものに堅牢性が備わっていない、"柔軟すぎる"ということだ。 だが近年はクラスの登場、モジュールの仕組みの統一などで 大規模開発に対応できる言語として進化を遂げた。 もともとの手軽さ、スピード感を利用しながら、 チームとして設計思想を揃えることで堅牢なJavaScriptを目指す。

Swiftの可能性

iOSアプリをネイティブサポートするオープンソースの言語、Swiftは、 Linuxでも実行可能となった。 2010年代のモダンな思想からくるOOPと関数型のハイブリッドのような 柔軟かつ堅牢な言語は、将来的な選択肢となる可能性はある。 注目を続ける。

設計思想を統一する

原則としては以下の3つだ。

  • プログラムの分け方に関する共通理解を持て
  • テストは将来のためにするものだ
  • 変数名、ディレクトリ構成、コーディングスタイルに、理由を持て

分け方 = 適切なインターフェイスの設定

モジュール、ファイル、クラス、メソッドなど、すべては切り分けられ、 その切り分け方こそが設計の真髄と考えている。

分けたときに、分かれた2つの間の情報のやりとりが「インターフェイス」である。 分け方を決めることは、インターフェイスを決めることと等しい。

つまりインターフェイスを決める事が設計の真髄である

分け方に関しての絶対的な正解はないが、メンバー同士で分ける感覚を揃えたい。 よって、下記に私の分け方のポリシーをいくつか記した。

汎用モジュールの分離

CureAppのサービスとしては汎用的すぎる実装だ、と思ったらそこを汎用的なモジュールとする。 例えばbase-domainは、モデルの生成や取得に関わる基本的なロジックを担う。

ビジネスロジックの分離

アプリ、ビジネスロジック、インフラ の3層を正しく分離すること。 ビジネスロジックは、

  • 表示形態に依存しない
  • インフラの構造に依存しない(Repositoryはその橋渡しとして限局的に依存あり)
  • 非エンジニアでも理解可能

ものだ。

外部のインターフェイスに依存する層を限定する

たとえばCureAppのサービスはLoopBackという外部モジュールに依存しているが、 「LoopBackならではのAPI」はドメイン層に限定している。 さらにdomainのなかでもRepositoryに限定している。 これによって、 - LoopBackがおかしい - LoopBackをやめたい

といったときに、修正範囲を限定的にすることができる。

privateとpublicは明確に。

publicなものが「インターフェイス」だ。 また、誰に対してpublicか、例えば Javaでいう"packageレベルのアクセス制限" も必要だ。 例えば「domainというモジュール内部では使用されるAPIだが、 外部からは呼び出されるべきではない」というAPIもある。

JavaScriptにアクセス修飾子がないのは致命的な欠陥だが、 自動ドキュメントでカバーすることを徹底する。

データとロジックの分離

データを増やし、ロジックを減らす。 なぜなら、ロジックにはバグがあるが、データにはバグがないからだ。

ユニットテスト

テストは、仕様変更の際の動作保証にもなる。

仕様にこそテストを書け

仕様 ≒ インターフェイス 外からどう見えているか。

"こう使われると、こうなる"ことの証明なのだ。

private methodのテストは、カバレッジのために必要なら書く

privateなメソッドは、検証したい一方で、

  • 他のpublicなメソッドを呼ぶだけでカバレッジが満たされる。
  • のちに変更したときにテストが落ちる = テストに振り回される現象

が発生するため、慎重に。

domesticなサービスのレポジトリのテストは積極的に日本語で書いていけ

日本語で書くことで、非エンジニアにも仕様がわかる。

it('PM 3:00にpush通知が配信される', ()=> 

これでいい。

コミュニケーション

  1. CTOは、情報共有のための時間をつくれ!
  2. メンバーは、CTOの情報共有を促し、遠慮するのは義務違反と思え!
  3. 情報共有は1:1でせず、皆が見えるところでしろ!

情報共有は、現実の会話やチャットなどの同期的なものと、 issue、コメント、ドキュメントなどの非同期的なものがある。

同期的コミュニケーション

会話やチャットのこと。

とにかく、遠慮しないことが大事! 受ける側は、相手が必要と思っているのだから、快く情報を共有する。 そして、そのサマリを非同期コミュニケーションツールに反映してほしい。 他の誰かも必要と思っている可能性が高いから。

非同期的コミュニケーション

  • github issues (waffle.io連携) に作業内容を共有
  • Qiita:TeamやDocBaseなどの社内情報共有ツールに日報やハマりどころを書く
  • READMEなどのドキュメントを書く
  • ソースコードにコメントを書く
  • Google DriveやCacooなどに資料的な情報を書く

これらが、非同期的コミュニケーションだ。 場合によってはチャットツールも非同期的コミュニケーションといえる。

大事なのは、 これらは決して特殊業務ではないよということだ。

コーディングしながらコメント書いて、 README更新して、 ハマりどころをDocBaseに共有して、 Google Drive上のスプレッドシートを更新して、 一日の終りに日報を書く。

これが、一連の業務だという認識を持てば、 非同期的コミュニケーションが活性化され、情報の局在化が防げる。

技術選定

汎用的なツール、モジュールの使用は、各プロジェクトで統一する。 そのほうが、理解が速い。

悪い例

  • プロジェクトAではtestにvowsを使っている
  • 僕は主にプロジェクトBでmochaを使っている
  • テストの書き方違うのか、調べないと...。

技術選定する上でのポイント

前提として、github上にあるものを利用する

上から順に大事。

  1. 地雷が踏まれているか。(スター数、Web上の露出、issue)
  2. APIの筋の良さ。 特に、シンプルさ。
  3. メンテナが多いか
  4. 一般的な(少なくともEarly Adapterの)開発者に馴染みがあるか
  5. 実績
  6. ライブラリ自体のメンテナンサビリティ (≒実装のよさ)

JavaScriptで採用すべき技術

以下はざっくり思いついたものを挙げた。(2016年3月時点)

  • Node.js
  • npm
  • babel
  • browserify
  • gulp
  • Promise
  • WHATWG Streams
  • mocha
  • power-assert
  • React.js
  • moment

技術の移行

とはいえ、採用した技術が"腐る"ことはある。 その場合は、技術を移行していく必要がある。 ただ、すでに動いているものを置き換える作業は、 事業としての優先度が高くないことを自覚するべきだ。

移行のための良いパターンは、

  • 新しい技術に移行した記事などをあさり、ベストプラクティスを探る
  • 新しいプロジェクトで導入して、ハマってから移行する

情報を収集する、試す

ブログやtwitterfacebookなどのメディアを活用することで、 情報を集めることができる。 さらに気になった技術を試せば、より感触がつかめるだろう。

個人的には、その活動は、業務時間に意図してやらなくてもよいと考えているが、 禁止するわけでもない。有用な情報があれば社内で共有していく。

業務時間にどこまでやるの?

「CureAppの業務」とはなんだろう。 上記のような情報収集や新技術を試す部分なども業務と解釈すると、 経営陣は短期的には苦しい一方で、長期的には良い投資にもなりうる。

よって、CureAppは、経営的な観点から、業務時間外に趣味で情報収集したり 新技術を試したりしてくれる人材を優先的に確保する。

監視するのは誰の得にもならない。 「よいプロダクト、よいサービスをつくる」という共通の方向を 向いて、自己判断する。 そのうえで価値観が経営陣と合わなければ、 そこはすり合わせていくことになるだろう。

自社製OSS

  • CureAppとして必要
  • 汎用化できる
  • 既存のライブラリで適切なものがない

という場合には、自社でのライブラリ開発も推奨される。 この活動は、CureAppが技術力を持った会社であるというイメージ向上にも繋がる。 慣れも必要だが、CTOにノウハウがあるので聞いて欲しい。

専門領域(ドメイン)への理解

  • 変数名や日本語は意外に大事。表をつくれ。ユビキタス言語で会話できるように。
  • 鈴木が医療知識との架け橋になるので、迷ったら必ず相談する。
  • ドメイン専門家と話したり本を読んだりして、ドメイン知識をつける。

通常業務として知識をいれてくれ、というわけではないが、

  • 疑問を持つ
  • わからなければ鈴木がいるから活用する
  • よりよいプロダクトになるよう、提案する

といったことが肝要だ。 特にプロダクトへの提案は歓迎だ。 医学的な視点だけでなく、UX的な視点で提案することで、 バランスのとれたプロダクトに近づくことができる。

自動化

開発の自動化

業務のうち、コーディング以外の部分で、 煩雑な手順を伴う作業はいくつも存在する。

  • コンパイル
  • デプロイ、リリース
  • テスト
  • ドキュメント

npm scriptsやgulp、gruntなどがかなりの部分を自動化できる。 リリース作業に関しては、CureApp製のOSSである node-circleci-autoreleaseを利用する。

各プロジェクトでの手順を統一するとよい。

運用の自動化

CureAppがCureAppたるのは、インフラにおいてではなく、 そのサービス、プロダクトにおいてである。 インフラに関しては、良い技術選定の上で、 自動化できるものを選んでいくとよい。

REST APIの自動化:LoopBack インスタンス生成の自動化、簡素化:Heroku、Engine Yardなど アプリケーション層のスケール自動化:AWS Lambdaなど DB層のスケール自動化:DynamoDB, mLabなど

もう一度

CureApp開発チームは、それぞれが別な得意領域を持ちながら広くシステムを理解し、開発に参加できる、というものを目指している。

そのために、 - マルチプラットフォームな言語を選択 - 設計思想を統一 - コミュニケーション - よい技術選定 - 専門領域への理解 - 自動化

を実践する。

無料でピザを食べられる濃密な座談会を開催しました。

なりゆき

ご好評いただいたMedTechConf01。 医療ITベンチャー3社、それぞれの特色を活かした発表ができました。

この様子はCodeIQ magazineにも取り上げていただきました。

codeiq.jp

上記のイベント、とても反響ありまして、その後何人の方とお会いすることができました。 「イベントいけるやん!」と味をしめたCureApp人事部隊は、

「弊社の技術セットにくわしく興味のある方に、より濃密に話をしたい」と思うに至りました。

MedTechConf02

そこで2/24(水)、限定10人で青山スタートアップアクセラレーションセンターにて開催しました。 トーマツさん場所の提供ありがとうございます。

雰囲気: 座談会ですねこれ

f:id:cureapp-dev:20160226114935j:plain

無断キャンセルの波に揉まれて、参加者は少なかったのですが、

  • 「JSというゆるい言語で大規模開発やるとはどうなってるんだ?」
  • 「JS好きだけどDDDをJSでやるなんて!」

という疑問がおありなコアな方々がいらっしゃいまして、 エンジニア座談会のような感じで進んでいきました。 途中でピザも登場して、皆さん大満足でお帰りいただきました!

内容

マルチプラットフォームJSについて

  • ビジネスロジックってプラットフォーム固有のAPI使わないけど、データ層へのアクセスだけちょっと問題。
  • データ層へのアクセスをHTTPレイヤにするのはいいよ。
  • superagentはまだ有能
  • fetchはこれからの技術
  • parse.comが終わるなか、JS製のOSSLoopBackは期待大。

DDD(ドメイン駆動設計)について

  • DDDのfactoryって、JSONから復元するところってただの単調作業で辛い。
  • DDDのrepositoryのCRUDも、割と処理が定型化している。
  • base-domainはそういったDDDの定型処理をしてくれるフレームワークで、ビジネスロジックの記述のみに集中できた。
  • Railsライクなフレームワークの「モデル」だと、DDDのサービスのような 複雑な振る舞いを記述する場所がなくて、コントローラが肥大していたよね。
  • メソッドの行数は少なく!ただドメインエキスパートが考える概念と粒度を揃えることも大事。
  • ドメインに各アプリケーションが依存する設計だと、ドメインのバージョン更新とか、異なるバージョン間の併存に対応するのが辛くなってくるのでは? -> 正直辛い。open/close原則などで運用するべきだが...。

その他

  • 保守性は、型で縛るか、テストや自動ドキュメントで縛るか、は好みもあるが、CureAppはテストと自動ドキュメントで、今のところ規模大きくても大丈夫。
  • 結局DDDっていうよりテスト書くことが大事なのかも。自動ドキュメントも。
  • IE対応どうなのよ: 弊社はSauceLabsを利用して自動e2eテスト。
  • callback地獄は追いにくいから保守性にも悪。Promise使おう。
  • CoffeeScriptが負債になる時は来る、が、そのときは変換後のコードから伸ばしていけばよい。
  • 綺麗に書くのがいいのかザクザク進んでいくのがいいのか、は経営の考え方にもよるけど某ECの会社が1年間60億をリファクタリングのみに費やしてしまった事実は、石橋を叩いて渡るプラクティスの重要性を示しているのではないか。

というかんじでした!

感想

こんな濃密な時間が過ごせるとは自分も思っていなくて、 予想外に自分が楽しいイベントでした。 今後もゆるく勉強/意見交換していく場として開催していきたいです!

functionのbindメソッドのシンタックスシュガーがJSの保守性をより高める

要約

JSで保守性を高めながら複数人で中規模以上の開発をするというのは今までは至難の業だったが、 ES6+の登場でやりやすくなってきている。

さらにそれを推し進めるのが Function Bind Syntax · Babel obj::methodというシンタックスシュガーだ。 これで関数の階層化を防ぐことができるとともに、 無駄なクラスを作る必要もなくなり、見通しがよくなって保守性が高くなる。

introduction

JSで中規模以上の開発はできる

弊社CureAppはJavaScriptの会社。 今まではCoffeeScriptを用いたOOPによって保守性を高めて開発していたが、 ES6+を使うことでも保守性を高めた開発ができるということがわかってきた。

JSで保守性を高めた開発をするためには、変数の状態がどこからでも変わり得るという恐ろしい事態を避けることが大事だ。

JavaScriptで、別のスコープに変数を共有する方法

別のスコープに変数を共有する方法は3つだ。

  1. 引数
  2. this
  3. 外のスコープの変数の参照

1. 引数

function scopeA() {
    let foo = 123
    scopeB(foo)
}

function scopeB(x) {
    console.log(x  + 1)
}
scopeA() // 124

2. this

class Foo {
    constructor() {
        this.foo = 123
    }

    scopeB() {
        console.log(this.foo + 1)
    }
}
new Foo().scopeB() // 124

3. 外のスコープの変数の参照

const foo = 123
function scopeB() {
    console.log(foo + 1)
}
scopeB() //124

ここで以下の問題を上記3つの方法それぞれで解いて比較してみよう。

例題

export function onRequest(req, res, options) {
}

あなたはこういう関数が外部から呼び出されるモジュールを作っている。 reqresは複雑な構造を持ったオブジェクトだ。

例えば、req.headerを見て、適切な値かどうかチェックする処理をいれたい。

1 引数で渡す

export function onRequest(req, res, options) {
    const isValid =  isValidHeader(req.header)
}

function isValidHeader(header) {
    // なんかの処理
}

okこれでいい。 では、optionsの内容によってisValidHeaderの挙動を変えたいということが出てきたら?

export function onRequest(req, res, options) {
    const isValid =  isValidHeader(req.header, options)
}

function isValidHeader(header, options) {
    // なんかの処理
}

これは皆さんお気づきの危険シグナルだ。プロジェクトが進むと引数はどんどん増え、 ほとんどの関数が微妙に違う4つぐらいの引数を持っている状態になる。 引数名もargs, params, config, optsと微妙に違うものが乱立して辛い状態になっていたりするのだ! 恐ろしい。

2 クラスをつくる

class RequestHandler {
    constructor(req, res, options) {
        this.req = req
        this.res = res
        this.options = options
    }

    init() {
        const isValid = this.isValidHeader()
    }

    isValidHeader() {
        this.req.header // これでreqがとれる
        this.options // これでoptionsもとれる
    }
}
export function onRequest(req, res, options) {
    new RequestHandler(req, res, options).init()
}

保守性も高いだろうが、ちょっと大げさだったりする。 そしてプロジェクトが進むと、すべての雑多な処理がこのクラスのメソッドにぶら下げられて、 「なんのクラスなのか分からん」状態になる。

3 外のスコープに置く

let req, res, options;

export function onRequest(_req, _res, _options) {
    req = _req
    res = _res
    options = _options
    const isValid = isValidHeader()
}

function isValidHeader() {
}

ああ、これはやっちゃだめだー!これで地獄をみたプロジェクトはたくさんあるはず。 例えば複数回このモジュールを呼んだとき、プログラマの脳がstack overflowする。

ということで、3つの方法、いずれにも欠点があった。

新しいsyntax obj::method で解決

ここでobj::methodを使ってこの問題を解いてみよう。

export function onRequest(req, res, options) {
    const subject = { req, res, options }
    const isValid = subject::isValidHeader()
}

function isValidHeader() {
    this.req.header // これでとれる
    this.options // これでとれる
}

これすごい!

解説

subject::isValidHeader()は、 「subjectをthisにして、isValidHeaderを実行してね」という意味。

昔は isValidHeader.bind(subject)() などという書き方をする必要があった。 このbindはもちろん便利だったが、トリッキーに見え保守性はあまり高くなかったように思っていて、 大きなプロジェクトでは敬遠される機能だった(と私は思っている)。

しかしsubject::isValidHeader()を見給え。 これは、「subjectをthisにしてるんじゃないかな?」というのが分かりやすいではないか!

  • 引数渡しと違い、渡すものが増えてもコードを書き換える必要がない
  • クラスより簡潔に書ける
  • 外のスコープを汚さない
  • 読みやすい

4つをクリアしたこの記法、チームでの開発ではぜひ実践したい。

株式会社CureAppはチームで簡潔で保守性の高いJavaScriptを書きたいエンジニアを募集しています。

CircleCIでリリースタグを自動生成する node-circleci-autorelease

いよいよ明日からiOSプロジェクトのビルドが有料になるCircleCI。 皆さんは活用していますか。CureAppはすべてのプロジェクトでCircleCIを活用しています。

そのなかで共通して欲しい処理をライブラリ化しましたので今回ご紹介します。

git commit -m 'release 1.2.3'
git push origin master

これで v1.2.3タグが発行されるようになる node-circleci-autorelease です。(nodeプロジェクト限定)

インストール

npm install --save-dev node-circleci-autorelease

プロジェクト内で上記コマンドを実行し、インストールします。 package.jsonのscriptsにいくつかコマンドが追加されます。

CircleCIにgithubのwrite権限を付与する

Continuous Integration and Deploymentを参考に、 githubにpush出来る権限をCircleCIに与えておきます。 これで準備は完了です!

circle.ymlの作成

npm run circle

これでcircle.ymlが作成され、

masterブランチの最新コミットが 'release X.Y.Z'という形式である場合に、 'vX.Y.Z'というタグを作ってくれます。

基本は以上ですが、カスタマイズしてみましょう。

hookを利用

post-dependencies

例えば、 「テストの前には毎回JS, CSSをbuildしたいが、バージョン管理はしたくない。しかしリリースタグには含めたい」 という要件があるかと思います。

babelやcoffee、scssなどのプリコンパイルをしたいが、それは管理したくないよと。

そういう場合、package.json

"scripts": {
    "post-dependencies": "grunt build"
}

こんな感じにビルドコマンドをpost-dependenciesに書くと、

CircleCIのdependenciesの最後に、つまりテストを走らせる直前に、このコマンドが走ります。

pre-release

一方、テストが走った後、リリースタグ発行前に実行したいコマンドがあれば

"scripts": {
    "pre-release": "grunt build"
}

のようにします。

.releaseignore

release前に、.gitignore.releaseignoreに差し替えられます。 たとえばspecなどの、リリース用には必要ないファイル/ディレクトリを指定しておくとよいでしょう。

gh-pages

github pagesも作ることができます。

この際、

"scripts": {
    "gh-pages": "grunt yuidoc"
}

のようにすると、gh-pagesブランチを生成する直前にgrunt yuidocが実行されます。

特定のディレクトリのみ公開したい場合には、 package.jsonに下記のように記述します。

{
  "node-circleci-autorelease": {
    "config": {
      "create-gh-pages": true,
      "gh-pages-dir": "doc"
    }
  }
}

より詳しい使い方は README にあります。

チェックしてみてください。

JavaScriptでDDD(ドメイン駆動設計) その2 ドメイン知識を分離する

ドメイン知識を分離する

ドメイン知識とは、関心領域の知識のことです。 例えば弊社の扱う領域は医療 / 医学です。

「患者さんに運動療法を提案したい。ただ、 高度の腎機能障害、心不全徴候がある場合は提案しないようにしたい。」

という要件があったとします。 悪いコードの例は、

if (patient.sex is 'male' and patient.sCr >= 2.5) or (patient.sex is 'female' and patient.sCr >= 2) or patient.BNP > 100
    notify '運動は控えましょう。'
else
    notify '運動しましょう。'

重要なドメイン知識が手続きの一部に埋まってしまっています。 そうではなく、

if patient.exerciseAllowable()
    notify '運動しましょう。'
else
    notify '運動は控えましょう。'
class Patient

    ###*
    運動をしてもよいか
    @method exerciseAllowable
    @return {Boolean}
    ###
    exerciseAllowable: ->

        return not @hasCKD() and not @hasCHF()


    ###*
    慢性腎臓病であるか
    @method hasCKD
    @return {Boolean}
    ###
    hasCKD: -> 
        if @sex is 'male'
            return @sCr >= 2.5
        else
            return @sCr >= 2

    ###*
    (検査値による)慢性心不全であるか
    @method hasCHF
    @return {Boolean}
    ###
    hasCHF: ->
        @BNP > 100

このようにして、医学・医療に関する知識=ドメイン知識 を分離します。

対話できる

こうすると、医師(=ドメインエキスパート)は仕様を把握し、

「違う、全然診断基準が違うし、最新のガイドラインに基づいたものにしてほしい!」

などと言ってくれて、最終的によりよいものができていきます。

前者の実装だと医師と対話する土台に立つことができません。

再利用可能

ドメイン知識を分離させることで、ロジックは再利用可能です。

慢性腎臓病の有無で食事指導の内容を変えたい

というときに、もちろん先ほどのメソッドを使って、

if patient.hasCKD()
    notify '生野菜を控えましょう。'

とできます。

JavaScriptは再利用の幅が広い

様々なプラットフォームで動作するJavaScriptでは、 ドメイン知識を様々なプラットフォームで利用できるようになります。

ドメイン知識は、基本的には論理であり、プラットフォームに依存しません。 (インフラ層とやり取りするレポジトリは例外です。ここをisomorphicにする方法もいずれご紹介します。)

どの環境でも同じように、ビジネスロジックを利用できる、というのがJavaScriptの強みです。

次回、第3回目は、「EntityとValueObject」です。

CureAppはDDDで医療を変えるソフトウェアエンジニアを募集しています!