FOLIOからfreeeに転職します

2019年1月20日付で株式会社FOLIOを退職し、2019年1月21日付でfreee株式会社に入社します。

f:id:itohiro73:20190121080854j:plain

FOLIOで何をしてきたか

FOLIOでは2017年4月にテックリードとしてジョインし、その後Head of Engineeringへと名称が変わり、その後病気による休職を経て復職し、シニアソリューションアーキテクトとして働いてきました。 たった1年8ヶ月ほどの在籍でしたが、非常に密度の濃い日々を過ごしてきた気がします。

今回はせっかくなので生々しい話も含めて在籍中の思い出を語ってみたいと思います。

FOLIOとの出会い

FOLIOを知ったのは2017年の2月頃、この記事を見つけたのがきっかけでした。

newspicks.com

前職の先輩であるbitFlyerのCEO加納さんのこのようなコメントをみて、プロダクトリリース前でのこの期待値はすごいな、面白そうだな、と思った記憶があります。

f:id:itohiro73:20190120165104p:plain

さっそくコーポレートサイトのトップページにあった「採用に興味がある方はこちら」のフォームから応募しました(意外に思われる方も多いかもしれませんが、立ち上げ期のスタートアップに入りたいのであれば、このやり方が一番手っ取り早いです。)。

面談ではCEOとCDO、そして全てのエンジニアたちと話をしました。とにかく皆優秀なエンジニアたちで、話をしていてほんとうに面白かった。話をしている中で、βリリースに向けての課題は山積みで、とくに証券会社の複雑なシステムを作り上げる上で、エンジニアリング上の課題を理解しつつビジネス側の要求を整理し、期待値を調整しつつ開発プロジェクトを推進していけるようなシニアな人材が当時おらず、自分がそこにフィットしそうだと感じました。

初日でほぼ気持ちはかたまり、2日目の面談で条件面をすり合わせ、オファーをいただきその場で承諾しました。

βリリースに向けた戦い

当時は2017年の5月8日がβリリース日として設定されていました。私の正式入社日が5月17日の予定だったのですが、正直そんな悠長なことを言っていられないくらい開発が切羽詰まっており、2017年の4月6日から有給消化期間を利用して開発のお手伝い(という名の怒涛のプロジェクトマネジメント)を始めます。

思い出深いのが4日6日の初出社日。とにかく現状把握のため、βリリースに関わるエンジニア全員と1 on 1をしました。そこでわかったことは惨憺たるものでした。

  • そもそもの機能要件からして未確定なものがある
  • インフラ人員がアプリケーション開発を手伝っており、肝心のインフラ整備が追いついていない
  • 開発スケジュールのつくりかたも積み上げ式の見積もりではなく、5月7日ありきの逆算型でつくられている
  • その他細々した問題が山積み

Webサービスとはいえ、お客様のお金を預かる証券会社のサービスです。たった1日のヒアリングでしたが、正直1ヶ月後にβリリースを控えた開発状況とはとうてい言えない肌感を得ました。もうこの時点で既にデスマーチの様相を呈しており、エンジニアたちも疲弊している状況でした。早速CEOをつかまえて1ヶ月後のβリリースは非常に危険な状態であることを伝えます。が、このとき自分のコミュニケーションの浅はかさに気づきます。

自分としては当然危険な状況であることが肌感としてわかったのでそれをそのままCEOに伝えたのですが、CEOにしてみればそのころはバーンレートも急激に上がってきた段階で、ビジネスプランのフィージビリティも投資家からガツガツつめられたりするような情勢の中、安易なスケジュール遅延は到底受け入れられません。初日に来たばかりの新参者のたかが肌感で、「1ヶ月後のリリースは無理だ、遅らせる必要がある」と言われても、はいそうですかと言えるわけがないのです。

私としてもこの時点では開発の責任者を請け負っているので、安易にデスマーチを受け入れるわけにはいきません。それこそ無理な開発スケジュールで開発を強行することによってビジネスが破綻してしまう可能性さえあるのです。初日からガチバトルです(健全なバトルだったと思います)。

とにもかくにも、CEOからは現状の精査をもう少し解像度の高い形でしてくれとの要請を得て、その日はおさまりました。

ここから怒涛の日々が始まります(*注:入社前)。

3日後には開発の現状をまとめたプレゼンを経営陣と開発PM陣にし、今の開発状況で技術的負債がどのような形で蓄積されているのか、このまま1ヶ月後のリリースを見据えて開発を続けた場合にどのようなネガティブインパクトが起こりうるのかを切々と語りました。

経営陣にはある程度納得してもらったところで、今度は10日後の取締役会(実態は投資家全員が参加する株主総会)で投資家たちにスケジュール変更の提案をするように要請されます。

さらに怒涛の日々を過ごし、見積もりを精緻化させ、取締役会に備えます(*注:入社前)。

取締役会では、開発現状のポジティブ面ネガティブ面の両面をうまく見せながら、積立方式の見積もりでゼロバッファーで見積もった場合に6月19日が最短のスケジュール(ただし達成確立は50%以下)、そこをマイルストーンとして置いて開発を進めると結果として2週間後の7月3日のリリースを目指すのがおおよそ70%ラインというお話をしました。

いろいろ突っ込まれるかとヒヤヒヤしていましたが、投資家達はおしなべて理解を示してくれて、このスケジュールで進めていこうという合意がなされました。

しかしながら、この時点では「絶対無理なデスマーチ」が、「普通のデスマーチ」に変化しただけなので、全く油断ができません。ここからもエンジニア達とともに開発にいそしむ怒涛の日々が始まります(*注:入社前)。

その後5月17日の入社日も無事迎えることができ、エンジニアたちとβリリースに向けて走り続けます。

結果としては、達成確立70%の見積もりラインを4日だけ超えて無事に7月7日にβリリースを迎えることができました。(せっかくのリリースなので、7月7日の七夕かつゾロ目がよいのではという判断なので実際はもうすくし早く開発は終えてました)。これに関しては、見積もりとほぼ違わずにリリースできたというサクセスストーリーでもあるのですが、無理な見積もりを強行せざるを得なかったというバッドストーリーでもあるので両面から語り継いでいきたい話ではあります。

https://story.folio-sec.com/n/n6576f573e5b6

非常に産みの苦しみの大きい最初のリリースでしたが、ただでは転ばないのかFOLIOのエンジニア達のすごいところ。チームとしてβリリース後も大小さまざまなプロジェクトを経験してきましたが、まっちゃらはそれらから学んだ見積もりの手法の重要性を理解し、下のような素晴らしい資料をつくって社内での勉強会を開いてくれたりしました。

matsu-chara.hatenablog.com

とにかくFOLIOのエンジニアたちはさまざまな困難を踏み抜いても、それを自分たちの血肉にしていく強さを持ち合わせていると感じます。とても貪欲でかつ知的好奇心に溢れる技術集団で、自分としても今後も見習っていきたいなと思います。

証券会社としての業務システム「PORT」という概念の導入

もともと証券会社でエンジニアとして12年間働いていた経験から、証券会社におけるバックオフィスのシステムの重要性は身にしみて理解していました。証券サービスを提供するにあたっては、口座開設のワークフロー、顧客や自己ポジションの管理、現金残高の管理、コンプライアンスによる売買審査等々、さまざまな証券業務を支えるバックオフィスシステムを開発・運用する必要があります。さらに、バックオフィスシステムのユーザーは社内の同僚であり、これらの業務システムを協力して大切に育てていきたい性質のものだという考えを持っていました。

バイモーダル戦略で言うところのSoE(System of Engagement)がFOLIOWebサービスやモバイルアプリに相当するシステムであり、SoR(System of Record)がこれらバックオフィスシステムに相当します。

FOLIOにジョインした当時は、リリース前でそもそも社内の証券業務部の人員も存在しない時期だったため、経営陣もクリエイター陣もとくにこの辺の肌感や重要性が共有されておらず、バックオフィスの業務システム自体が(意図せず)軽視されていました。開発見積もりにおいても単に「管理画面」と呼ばれ、非常にモチベーションの低い対応がなされていました。

このような状況を改善するためバックオフィスの業務システムの名付けからスタートすべき、と言う話をして(*注:入社前です)、「PORT」という名前が付いたと言う話を下のブログに書いています。 itohiro73.hatenablog.com

ここからさらに、シンプルにSoEとSoRの2分割では足りないよーといろいろ掘り下げてくれたのが現バックエンドエンジニアリードのむらみんです。詳しくは下のブログをご覧ください。

t-and-p.hatenablog.com

クリエイターブランディングチームの導入とScala人材採用へのインパク

私がFOLIOにジョインした2017年4月当時は、全社員が20人そこそこ、半分の10人ほどがクリエイター(デザイナー+エンジニア)と少人数で、証券会社をつくりあげていくにはとにかく人手が足りない状況でした。今後組織を拡大していくにあたって、目先の開発のみに注力するだけではなく採用を強化するためにも、クリエイターのブランディングを確立していくことは非常に重要だという認識を持っていました。デザイナーに関してはCDOである広野氏が当時でも十分すぎるくらいのブランディング力を発揮していましたが、当時はまだ確立していなかったFOLIOのエンジニアのブランディングも強化していきたい想いがありました。

フロントエンドエンジニア、バックエンドエンジニア、インフラエンジニア(現在はSREと呼んでいます)、アプリエンジニア等々さまざまな職能で組織を拡大していかなければいけない中で、一番採用に関してチャレンジングであった領域が、バックエンドを支えるScalaエンジニアでした。Scalaエンジニアはとにかく人材市場規模が小さく、まだ知名度のないFOLIOにとっては採用をしかけていくのが非常に大変だったのです。

当時モヤモヤと思い描いていたブランディング戦略としては、バックエンドエンジニアの何名かがJavaのコミュニティ内である程度の訴求力をもっていたため、Scalaユーザーも一定数いるJavaコミュニティでの認知を増やしていければいずれScalaコミュニティにも波及し、Scalaエンジニアの採用へとつながるのではないかという漠然とした考えを持っていました。

漠然とはしていましたが、持てる力はすべて出し尽くそうという考えのもと、入社直後からアクセル全開で攻めていきました。

とれる戦術の順序としては、『Javaコミュニティでの認知の拡大』→『技術的な実績づくり』→『Scalaコミュニティでのさらなる認知の拡大』です。

当時ほぼ同時期に入社したJava女子部部長であるよこなさんとともに、まずはバズりそうな入社エントリーをダブルで出稿します。

ihcomega.hatenadiary.com

itohiro73.hatenablog.com

この時点でJavaコミュニティに対しては、「おっ?FOLIOってなんだ?」という小さな認知の拡大をもたらせた気がしています。

この後のJJUG CCCで話したのが、技術的にも非常に面白いテーマであるテンポラルデータモデルとReladomoのお話。これが見事にバズります(407はてブ)。これは狙ってもなかなかできるものではないので、自分でもビックリしました。 b.hatena.ne.jp

そして、ここで奇跡が起きます。

なんとScalikeJDBCの開発者である瀬良さんが興味を持ってくださいました!

FOLIOでは証券会社として履歴にまつわる機能要件が非常に多く、もともとReladomoをScalaから使えるようなライブラリーをつくっていきたいと考えていました。しかし、上記のようにβリリースに向けての多忙に見舞われる中で、自分で開発に携わるのは相当厳しい情勢でした。そんな中、瀬良さんに手伝っていただければScalaのラッパーを書けるのでは!と盛り上がります。

速攻DMで連絡し、いろいろとお話をさせていただいたのちに業務委託として「ReladomoのScalaラッパーをOSSとして開発」していただくことになりました🎉

このころ、Java/Scalaだけにとどまらず、フロントやインフラ、アプリなどの様々なエンジニアリング領域、そしてクリエーター全体でのブランディングを底上げするために、「クリエイターブランディングチーム」を立ち上げます。メインのリードは前述のよこなさんです。ブランディングチームの戦略について、CEOを巻き込んで重要性を理解してもらうことにも注力しました。

人事部とも協力して、社内のエンジニアが働いている様子のWantedlyの記事化にも取り組みました。

そうこうしているうちに、秋口のScala関西Summitが近づいてきます。

ここで、Scala関西Summitの2日前にScalaエンジニアの紹介記事をぶちこみます。 www.wantedly.com

これが功を奏し、Scala関西Summitに参加するエンジニアたちにFOLIOの名前をほのかに認知させることができました。そして、Scala関西Summit当日、瀬良さんと共同でreladomo-scalaのセッションで登壇し、OSSのリアルタイム公開に踏み切ります。

www.wantedly.com

Reladomoの持つ技術的な面白さに相まって、OSSをリアルタイムで公開すると言う試みでこのセッションがいい感じにバズりました。

そして、なんと、ここでの懇親会で採用につながったのが、先述の、現在FOLIOでバックエンドのリードをつとめるむらみんです。

2017年のScala関西Summitで採用された彼が2018年の同イベントで登壇するという流れもできたので、非常にポジティブなブランディングサイクルになっています。

www.wantedly.com

さて、Scala関西Summitでの直接採用は一人だけですが、ブランディング施策のインパクトとしてはこれにとどまりません。

時系列でみてみると、私がジョインした2017年5月からScala関西Summitが開催された2017年9月までのバックエンドエンジニアの採用は0人なのですが、2017年9月から2018年8月までのバックエンドエンジニア採用実績はなんと9人リファラル等も含まれるので単純なブランディングだけのインパクトは計れないですが、間違いなく効いてきています。

最初は漠然とした戦略として始めたものが、様々な偶然も重なった上で実際にブランディングの結果として目に見える形で成果が現れたのは非常に感慨深いものがあります。

ここで紹介した施策は主に私が関わっていたScalaにまつわるものですが、それ以外の領域でもクリエイターブランディングチームはエンジニア達の対外活躍を支援するとりくみをしてくれています。例えばアドベントカレンダーFOLIO Advent Calendar 2017 - QiitaFOLIO Advent Calendar 2018 - Adventar)、証券会社が2年連続で完走するのは非常に珍しいと思います。今後もFOLIOの技術領域の動向をウォッチしていきたい次第です。

エンジニアの組織のブランディングを確立していく上で重要なのは、対外的な訴求とともに対内的な訴求も強めていくということだと思っています。つまり、外部の人への認知を拡大するための施策をとるだけでなく、社内のエンジニアたちも自分たちの持つ技術力や開発力に自信を持って、もっとこの環境で働きつづけたい、自分の知り合いにも勧めたいと思えるような、正のフィードバックループが働くブランディングをしていくことが重要だと思います。そのためにも、DX(Developer Experience)の改善や社内勉強会の充実、OSSや外部コミュニティへの貢献の促進等、環境を整えていくことががとても大切になってくると考えます。この辺は今後関わっていくであろう様々なエンジニアリング組織でも突き詰めていきたいところです。

なぜ辞めるのか

FOLIOのビジネスはものすごく面白く、意義のあるものだと思っています。日本の個人の金融資産が約1800兆円あるといわれており、そのうちの半分以上である約960兆円が現金・預金が占めています(参考)。これまで様々な有識者や金融機関が「貯蓄から投資へ」を声高に叫んできましたが、この個人金融資産の半分以上を占める現金預金比率はここ20年以上変わってきていません。

フォリオのようなワクワクする、楽しく投資をするサービスが広まることで、今まで投資を選択肢に入れてこなかったような大勢の人たちが投資をしはじめる。それによって今まで循環してこなかった何兆円もの資金が投資に流れ、結果として経済を活性化させるという世界観は、ほんとうにエキサイティングなものであり、証券会社にエンジニアとして長年勤めてきた身としては今後はこのビジネスにコミットし続けていくものだと考えていました。最初の会社では12年働いたので、漠然と次の12年はFOLIOへコミットし続けていきたいなと考えていました。しかし、世の中そううまくはいきませんでした。

ここから急に重い話題になります。

2017年の11月、様々な複合要因から「抑うつ状態」と診断され、会社を休職しました。ほんとうに様々なストレス要因がからみあって引き起こされた病気だったため、ひとつずつストレス要因を解決していく必要がありました。

そこからは地獄のような日々が始まります。

最初は12月には復帰できそうな情勢でいたのが、復帰直前に再発、その後最悪な産業医面談による病状の悪化(詳細は省く)、リハビリによる回復等の紆余曲折を経てなんとか回復し、2018年の4月にようやく復職します。

この頃には当初の要因となった問題はほとんど解決されていたのですが、ひとつだけ大きなストレス要因が解決できていませんでした。これに関しては、もはや自分で解決できる問題ではなかったため、カウンセリング等を通じてその問題に対する見方を変えるしか方法はありませんでした。

休職中はベッドから起き上がれないくらい体が動かないような日々もあったのですが、復職後は身体への影響はもはやほとんどなく、仕事や日常生活は普通にこなせる状況まで回復していました。はたから見たら普通となんら変わらないように見えたと思います。

しかし、上記のひとつだけ残っていたストレス要因への対処がまったくうまくできず、精神的にはまだヤバい状態が続いていました。

どれだけヤバかったかというと、2018年のおおよそ5月〜9月くらいのTwitterの下書きがこんな感じで残っています。

f:id:itohiro73:20190120170732j:plain
復職後にも精神的に苦しんでいる様子がTwitterの下書きから伺える

下書きにとどまっていて、実際にはツイートしていないのがポイントですね。これはつぶやいたらアカンという葛藤がみえる。

このような精神状態は、もはやカウンセリングでは全く改善されず、問題に対する見方も変わる気配がありませんでした。このころから環境を変えざるを得ないのではないかという考えが自分の中で大きく占め始めます。

とはいえ、とにかくFOLIOの目指す世界観、FOLIOの同僚たちも仕事も大好きだったので、どうにかしてこのままFOLIOでの仕事を続けらないか模索していました。自分の精神状態を悪化させている明確な要因はFOLIOの環境下に存在しており、これは私にとって非常にセンシティブな問題なので公表できないのですが、取締役を含む数人にのみ共有していました。会社は私の抱えている問題に対して真摯に対応してくれており、相当な時間をかけて解決に向けて協力してくれていました。

しかしながら、このように周りの支えもあったのですが、残念ながら復職から半年経ってもどうにも改善できない状況を鑑みて、退職を決意せざるをえませんでした。

自分としてはあまりにもつらい決断で、最初に退職の申し出をした際も、社長の部屋でわんわん泣いてしまいました。実はこの際に一度強く引きとめられており、一旦保留としていたのですが、数週間ほどぐるぐると終わりのない考えを繰り返し、それでもにっちもさっちもいかない状態になり、やはり最終的に退職を決断した、という流れになります。

【2019年1月30日追記】 ここに書かれている事は、私個人に特有の事情によるところが大きく、FOLIO自体には何ら問題はありません。むしろ問題は私の内面の方にあると言えます。FOLIOはとても良い会社で、前述のようにとても面白いビジネスモデルを持ってます。同僚も優秀で働いていて楽しいですし、特にエンジニアにとっては、複雑なドメインと技術領域にチャレンジできるとても面白い職場だと思います。私も自分の抱えている問題が解決できたらいつか戻りたいとも思える素敵な場所なので、そこのところは勘案しつつ読んでいただけると幸いです。

大丈夫なの?

現在はFOLIOでの最終出社日から約1ヶ月経っており、ストレス要因からは離れてゆっくり休むことができました。

今となってはそれまでの精神的な苦悩がほんとうに嘘かのように消え去っており、現在は毎日を晴れ晴れしい気分で過ごしています。新しい職場での新しい仕事に対しても、ものすごくポジティブなワクワク感を持っていて、環境を変えるということのインパクトの大きさを身にしみて感じております。

freeeとの出会いとこれから何をするのか

実はfreeeとの出会いは約4年半前にさかのぼります。当時ちょっと外の世界をのぞきにいっていた時期があって、Wantedlyを通じて当時まだ2,30人くらいの規模感だったfreeeのオフィス(当時は麻布十番と赤羽橋の間あたりにありました)に遊びに行かせてもらいました。CEOの佐々木さんとは中小企業のバックオフィスを強化したいというビジネスの話で盛り上がり、その後お話を聞かせていただいたCTOの横路さんとは技術的な話でもりあがったものの、「伊藤さんがフィットしそうなのはもうちょっとfreeeが成長して組織的な課題が出てきた頃な気がしますね」というお言葉をいただき、自分もそんな気がするなーという感覚をもってその場はお開きになった覚えがあります。

その4年後である2018年、Wantedly経由で再度お声がかかり、久しぶりにキャッチアップをしませんかというメッセージがとどきました。五反田のオフィスに遊びに行かせていただいて、その際は基盤エンジニアの求人でお話しさせてもらったんですが、面白そうなポジションではあったものの特にお互いピンと来る感じではなく、またkeep in touchしましょうねという流れになりました。しかし、その1ヶ月後くらいに「freeeの新規部署・組織開発チームの担当者を募集!」という別のポジションで求人が出されているのをネットでたまたま見つけました。これは非常に面白そうと思い、再度何人かの経営陣やエンジニアとお話しさせていただいて、エンジニア組織が拡大していく中でのさまざまな課題感を聞かせてもらいました。なんたる偶然か、まさに組織が成長したことによる課題が4年越しでいろいろと顕在化してきたところで自分が色々と貢献できそうな仕事です。こちらは順調に選考が進み、無事内定をいただくことができました。

freeeで取り組むのは「開発組織企画」という新しいチームで、エンジニアの組織的な課題を解決していくポジションになります。全く新しいポジションなので裁量をもっていろいろおもしろい取り組みができそうで、今からワクワクしております。

自分はかねてから、自分自身が普通の人の1.5倍のパフォーマンスを出すよりも、チームの皆がそれぞれ1.2倍のパフォーマンスを出せるような環境を整備してあげることで、10人の組織であれば12人分、100人の組織であれば120人分、といったスケーラブルな形で大きなインパクトをもたらす仕事に興味をもっているので、このポジションは非常に面白いことができるのではないかと思っています。

FOLIOでも取り組んでいたようなエンジニアのブランディング活動も、組織を拡大して強くするために必要な施策だと思うので、freeeでも同様な取り組みを牽引していければと思っています。

今後のもろもろに、ご期待いただければと思います。

Wish List

ほしい物リスト、置いておきますね🙏

www.amazon.co.jp

Scala関西Summitでブロックチェーンについて話してきました #scala_ks

昨年に引き続き、Scala関西Summitに参加してきました。

今年もFOLIOがスポンサーしており、自分含め二人で登壇!

自分のセッションではScalaで簡易ブロックチェーンを実装してみる話をしました。ブロックチェーンの基本的な部分の解説と、その実装例をライブコーディングを交えて紹介しました。

高嶋さんによるうれしいフィードバックもいただき、Day2の打ち上げの際もご本人から「Scala以外の技術的領域に興味を広げられてよかったです」といううれしいコメントをいただきました。感謝。

Day1の終盤では去年のScala関西Summitで引き抜いた(?) id:mura-_-miFOLIOスポンサーセッションとして登壇していたので、巣を立つヒナをみる親鳥のような感覚で見ておりました。おかげさまで本セッションは多方面に好評をいただいており、自分ごとのように嬉しい限りでした。いやー、むらみん入ってなかったらほんとにFOLIOのバックエンドチームは今ほど体系だった組織に成長してなかったであろうと思うので、本当にScala関西Summitが与えてくれた出会いには感謝しております。

togetter.com

Day2のアンカンファレンスではとーますさんのAirframeのハンズオンに参加したり、IntelliJハンズオンのつまみ食いをしたり、おえさんのsbtタスクグラフのお話を聞いたり、Akkaに入門したりしました!とってもためになったし楽しかった!

大阪最後の夜は何食べようかなと思ってたら、Day2の打ち上げをするということで、参加させていただきました。 2次会では噂には聞いていた、待望のイタリアバルSCALAに初めて行ってきました。美味しかったし楽しかった!

とにかく最初から最後まで熱量高く、楽しい2日間でした。

いろいろとモチベーションが上がったし、来年もまたぜひ参加したいと思える素敵なイベントでした!!

ReladomoにEclipse Collectionsをサポートする変更をコントリビュートしました

Reladomoとはゴールドマン・サックス社がOSSとして公開しているJavaのORMで、型安全なクエリ言語やバイテンポラルデータモデルのサポートなど、エンタープライズグレードの機能を持つORMフレームワークです。

詳しくはこれらのスライドをご覧ください。

www.slideshare.net

www.slideshare.net

さて、こちらのReladomoですが、データベースから取得したコレクションを、同じくゴールドマンサック社が同社初のOSSとして公開したGS Collectionsに変換する機能をもともとサポートしていました。

ReladomoでGS Collectionsを使用するコード例
// select * from PERSON
PersonList people = PersonFinder.findMany(PersonFinder.all())

//苗字を取得
List<String> lastNames = 
        people.asGscList().collect(Person::getLastName);

//猫を飼っている飼い主を取得
List<Person> catOwner = 
        people.asGscList().select(person -> person.hasPet(PetType.CAT));

//PetTypeごとに飼い主をグルーピング
MutableListMultimap<PetType, Person> peopleByPetType = 
        people.asGscList().groupByEach(person -> person.getPets().asGscList().collect(Pet::getPetType));

しかし、ご存知の方もいるかもしれませんが、GS Collections自体は現在開発が終了しており、Eclipse Foundationへ移管されたEclipse Collectionsという新たなプロダクトとして生まれ変わっております。

Eclipse Collections - Features you want with the collections you need. (日本語ページ)

こちらのEclipse Collections、GS Collectionsと上位互換の機能を持つのですが、パッケージの構成が異なり、実態としては異なるライブラリ依存になります。ですので、これまでReladomoがサポートしているGS CollectionsをそのままEclipse Collectionsとして使用することはできませんでした。

今回の変更では、新たにEclipse CollectionsをサポートするAPI asEcList() を追加し、もともとのasGscList()がdeprecated(非推奨)となりました。本機能はReladomoの17.0.0より使用することが可能です。

これにより、Reladomoそのものが持つ強力なDBクエリー言語に加えて、DBから取得したコレクションをインメモリーで操作することもEclipse Collectionsの機能で扱うことができ、データ操作の選択肢の幅が広がります!

ReladomoでEclipse Collectionsを使用するコード例
// select * from PERSON
PersonList people = PersonFinder.findMany(PersonFinder.all())

//苗字を取得
List<String> lastNames = 
        people.asEcList().collect(Person::getLastName);

//猫を飼っている飼い主を取得
List<Person> catOwner = 
        people.asEcList().select(person -> person.hasPet(PetType.CAT));

//PetTypeごとに飼い主をグルーピング
MutableListMultimap<PetType, Person> peopleByPetType = 
        people.asEcList().groupByEach(person -> person.getPets().asGscList().collect(Pet::getPetType));

その他変更の詳細はこちらのPRをご覧ください。

github.com

Reladomoでは、1年後の2019年3月にはGS Collectionsのサポートは終了する予定です。ですのでReladomoとGS Collectionsを併せて使用している方は早めの移行をおすすめします。

移行ガイドはこちら。

reladomo/GSC_TO_EC_MIGRATION_GUIDE.md at master · goldmansachs/reladomo · GitHub

ReladomoとEclipse Collections、最強の組み合わせでぜひ楽しい開発をしてみてください。Enjoy Happy Development!!

【第三回】経費申請のフローで学ぶCamundaの基本(BPMN実行編)

前回は、Camunda Modelerを用いて「経費申請」フローの簡単なBPMNモデリングをしてみました。第一回と第二回までの作業が終了したプロジェクトがGitHubにあがっているので、今回はここからスタートします。

今回は、前回作成したBPMNモデルをCamunda上で実行可能な状態にし、申請、承認、確認に必要なフォーム(html/JavaScript)とService Taskで実行するJavaコードを実装し、実際に使える業務アプリケーションとして動作させてみましょう。

まず先に業務要件をモデリングしてからアプリケーションの実装を行うということで、モデル駆動開発とも呼べる流れになっています。この方法論も楽しんでもらえればと思います。

前回作成した経費申請のBPMNモデル

f:id:itohiro73:20180115112911p:plain

BPMNを実行可能にする

まずは前回作成したBPMNモデルをCamunda上で実行可能にする必要があります。「経費申請」プールを選択して、右側に現れるメニューからExecutableという項目にチェックを入れれば実行可能になります。

また、Process Idにはわかりやすい名前をつけておきましょう。今回はExpenseApplication としました。

f:id:itohiro73:20180115185002p:plain

ちなみに、Camunda ModelerでつくるBPMNモデルは普通に考えてCamunda上で実行するんだから、デフォルトで実行可能にしてくれよ、っていうissueがあがっていて、どうやら次のバージョン(v1.12.0)にマージされているようです。リリースはよ。

フォームの作成

BPMNのワークフローを実行するにあたって、人間による作業や確認をするためのフォームが必要になってきます。

今回の経費申請フローでは、下記のようにそれぞれのイベント・ユーザータスクのBPMNコンポーネントに対応する、3つのフォームを作成します。

BPMNコンポーネント フォーム名 ファイル名
f:id:itohiro73:20180115165914p:plain 経費申請フォーム apply-form.html
f:id:itohiro73:20180115165959p:plain 経費承認フォーム approve-form.html
f:id:itohiro73:20180115174804p:plain 経費確認フォーム confirm-form.html
src/main/resources/static/forms/apply-form.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>経費申請フォーム</title>
</head>
<body>
<h2>経費申請フォーム</h2>
<form role="form" name="expenseForm"
      class="form-horizontal">

    <div class="form-group">
        <label class="control-label col-md-4"
               for="expenseReceiptUpload">領収書のアップロード</label>
        <div class="col-md-8">
            <input type="file"
                   id="expenseReceiptUpload"
                   cam-variable-name="expenseReceipt"
                   cam-variable-type="File"
                   cam-max-filesize="10000000"
                   class="form-control"/>
            <div class="help-block">経費の領収書の画像を選択してください</div>
        </div>
    </div>

    <script cam-script type="text/form-script">
            var fileUpload = $('#expenseReceiptUpload');
            var fileUploadHelpBlock = $('.help-block', fileUpload.parent());

            function flagFileUpload() {
              var hasFile = fileUpload.get(0).files.length > 0;
              fileUpload[hasFile ? 'removeClass' : 'addClass']('ng-invalid');
              fileUploadHelpBlock[hasFile ? 'removeClass' : 'addClass']('error');
              return hasFile;
            }

            fileUpload.on('change', function () {
              flagFileUpload();
            });

            camForm.on('submit', function(evt) {
              var hasFile = flagFileUpload();

              // prevent submit if user has not provided a file
              evt.submitPrevented = !hasFile;
            });

    </script>

    <div class="form-group">
        <label class="control-label col-md-4"
               for="detail">内容</label>
        <div class="col-md-8">
            <input cam-variable-name="detail"
                   cam-variable-type="String"
                   id="detail"
                   class="form-control"
                   type="text"
                   required />
        </div>
    </div>

    <div class="form-group">
        <label class="control-label col-md-4"
               for="amount">金額(円)</label>
        <div class="col-md-8">
            <input cam-variable-name="amount"
                   cam-variable-type="Double"
                   id="amount"
                   name="amount"
                   class="form-control"
                   type="text"
                   required />
        </div>
    </div>

    <div class="form-group">
        <label class="control-label col-md-4"
               for="expenseCategory">経費種別</label>
        <div class="col-md-8">
            <select cam-variable-name="expenseCategory"
                    cam-variable-type="String"
                    id="expenseCategory"
                    class="form-control">
                <option value="TRAVEL" label="旅費交通費"/>
                <option value="MEAL" label="接待交際費"/>
                <option value="EXPENDABLES" label="消耗品"/>
                <option value="OTHER" label="その他"/>
            </select>
        </div>
    </div>

</form>

</body>
src/main/resources/static/forms/approve-form.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>経費申請フォーム</title>
</head>
<body>
<h2>経費申請フォーム</h2>
<form role="form" name="expenseForm"
      class="form-horizontal">

    <p>経費情報を確認の上、承認してください</p>

    <div class="form-group">
        <label class="control-label col-md-2">経費領収書</label>
        <div class="form-control-static col-md-10">
            <input type="hidden" cam-variable-name="expenseReceipt"/>
            <img src="{{ camForm.variableManager.variable('expenseReceipt').contentUrl }}" class="img-responsive">
        </div>
    </div>

    <div class="form-group">
        <label class="control-label col-md-2">内容</label>
        <div class="col-md-10">
            <input cam-variable-name="detail"
                   type="text"
                   name="detail"
                   readonly="true"
                   class="form-control" />
        </div>
    </div>

    <div class="form-group">
        <label class="control-label col-md-2">金額(円)</label>
        <div class="col-md-10">
            <input cam-variable-name="amount"
                   type="text"
                   name="amount"
                   readonly="true"
                   class="form-control" />
        </div>
    </div>

    <div class="form-group">
        <label class="control-label col-md-2">経費種別</label>
        <div class="col-md-10">
            <select cam-variable-name="expenseCategory"
                    cam-variable-type="String"
                    name="expenseCategory"
                    disabled="true"
                    class="form-control">
                <option value="TRAVEL" label="旅費交通費"/>
                <option value="MEAL" label="接待交際費"/>
                <option value="EXPENDABLES" label="消耗品"/>
                <option value="OTHER" label="その他"/>
            </select>
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-10 col-md-offset-2">
            <label>
                承認しますか?
                <select cam-variable-name="approved"
                        cam-variable-type="Boolean"
                        id="approved"
                        name="approved"
                        class="form-control">
                    <option value="true" label="承認"/>
                    <option value="false" label="却下"/>
                </select>
            </label>
        </div>
    </div>

</form>

</body>
src/main/resources/static/forms/confirm-form.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>経費申請フォーム</title>
</head>
<body>
<h2>経費申請フォーム</h2>
<form role="form" name="expenseForm"
      class="form-horizontal">

    <p>経費情報を確認の上、タスクをCompleteにしてください</p>

    <div class="form-group">
        <label class="control-label col-md-2">経費領収書</label>
        <div class="form-control-static col-md-10">
            <input type="hidden" cam-variable-name="expenseReceipt"/>
            <img src="{{ camForm.variableManager.variable('expenseReceipt').contentUrl }}" class="img-responsive">
        </div>
    </div>

    <div class="form-group">
        <label class="control-label col-md-2">内容</label>
        <div class="col-md-10">
            <input cam-variable-name="detail"
                   type="text"
                   name="detail"
                   readonly="true"
                   class="form-control" />
        </div>
    </div>

    <div class="form-group">
        <label class="control-label col-md-2">金額(円)</label>
        <div class="col-md-10">
            <input cam-variable-name="amount"
                   type="text"
                   name="amount"
                   readonly="true"
                   class="form-control" />
        </div>
    </div>

    <div class="form-group">
        <label class="control-label col-md-2">経費種別</label>
        <div class="col-md-10">
            <select cam-variable-name="expenseCategory"
                    cam-variable-type="String"
                    name="expenseCategory"
                    disabled="true"
                    class="form-control">
                <option value="TRAVEL" label="旅費交通費"/>
                <option value="MEAL" label="接待交際費"/>
                <option value="EXPENDABLES" label="消耗品"/>
                <option value="OTHER" label="その他"/>
            </select>
        </div>
    </div>

    <div class="form-group">
        <div class="col-md-10 col-md-offset-2">
            <label>
                承認情報:
                <select cam-variable-name="approved"
                        cam-variable-type="Boolean"
                        id="approved"
                        name="approved"
                        disabled="true"
                        class="form-control">
                    <option value="true" label="承認"/>
                    <option value="false" label="却下"/>
                </select>
            </label>
        </div>
    </div>

</form>

</body>
BPMNコンポーネントとフォームの紐付け

これらのフォームをそれぞれのイベント・タスクと結びつけるために、BPMNコンポーネントを選択肢、右メニューのFormsタブにあるForm Keyという項目に、対応するフォームのファイルパスをembedded:app:に続けて入力します。

f:id:itohiro73:20180115174130p:plain

embedded:app:forms/apply-form.html
embedded:app:forms/approve-form.html
embedded:app:forms/confirm-form.html

以上でフォームに関する設定は完了です。

Exclusive Gatewayの分岐条件の設定

次に、「承認」タスクで承認者がタスクをCompleteした後に、ゲートウェイにおいて承認済みか否かの分岐判定が必要になります。

「承認済み?」のExclusive Gatewayから伸びている「いいえ」と「はい」のフローコンポーネントそれぞれに、「承認」タスクでフォームで入力されたapproved変数を用いた条件式を設定します。

「いいえ」の分岐

f:id:itohiro73:20180115182800p:plain

f:id:itohiro73:20180115183128p:plain

「はい」の分岐

f:id:itohiro73:20180115183236p:plain

f:id:itohiro73:20180115183322p:plain

Service Taskの実装

「経費記録」のサービスタスクでは、今回は単純に経費情報をログに出力してみましょう。

f:id:itohiro73:20180115165141p:plain

Service Taskは実装方法を選択する必要があるので、「経費記録」のService Taskの詳細に、今回はJava Classとして下記のように入力しておきます。

f:id:itohiro73:20180115164728p:plain

Javaの実装としては、経費種別のラベリングのためのenum Category.javaと、サービスタスク ExpenseRecorder.javaを以下のように作成します。

src/main/java/sample/Category.java
package sample;

public enum Category {
    TRAVEL {
        @Override
        public String toString() {
            return "旅費交通費";
        }
    },
    MEAL {
        @Override
        public String toString() {
            return "接待交際費";
        }
    },
    EXPENDABLES {
        @Override
        public String toString() {
            return "消耗品";
        }
    },
    OTHER {
        @Override
        public String toString() {
            return "その他";
        }
    }
}
src/main/java/sample/service/ExpenseRecorder.java
package sample.service;

import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import sample.Category;

import java.util.logging.Logger;

public class ExpenseRecorder implements JavaDelegate {
    private final Logger LOGGER = Logger.getLogger(ExpenseRecorder.class.getName());

    @Override
    public void execute(DelegateExecution execution) throws Exception {

        LOGGER.info("内容: " + execution.getVariable("detail"));
        LOGGER.info("金額(円): " + execution.getVariable("amount"));
        LOGGER.info("経費種別: " + Category.valueOf((String)execution.getVariable("expenseCategory")));
        LOGGER.info("承認済み?: " + (((Boolean)execution.getVariable("approved")).booleanValue() ? "はい" : "いいえ"));
    }
}

BPMNの配備とプロセス設定

作成したBPMNはsrc/main/resources直下に配備しておきましょう。

src/main/resources/expense_application.bpmn

次に、src/main/resources/META-INF/processes.xmlを作成し、経費申請BPMN(expense_application.bpmn)をデフォルトで読み込むように設定します。

<process-application
  xmlns="http://www.camunda.org/schema/1.0/ProcessApplication"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

  <process-archive>
    <process-engine>default</process-engine>
    <resource>expense_application.bpmn</resource>
    <properties>
      <property name="isDeleteUponUndeploy">false</property>
      <property name="isScanForProcessDefinitions">false</property>
    </properties>
  </process-archive>

</process-application>

ここまででBPMNはCamundaで自動的にロードされ実行可能な状態になりました。試しにSpring BootからCamundaを立ち上げてみましょう。

$ gradle bootRun

立ち上がったら http://localhost:8080/ にアクセスし、demo/demoでログインします。

f:id:itohiro73:20180116142223p:plain

Applications項目にあるTasklistをクリックします。

f:id:itohiro73:20180116142346p:plain

Tasklistが開き、現状ではリストは空の状態です。

f:id:itohiro73:20180116142650p:plain

さぁ、ここから経費申請フローを動かしてみましょう。

経費申請フローのデモ

今回は、承認者が承認する条件分岐で経費申請フローを試してみます。また、簡単のために申請者承認者経理の役割ははすべてdemoユーザーで実行します。

CamundaにデプロイされたBPMNのプロセスを起動するために、Tasklistの右上のメニューにあるStart Processをクリックします。

f:id:itohiro73:20180116142619p:plain

ポップアップで現れるメニューからExpenseApplicationをクリックすると、経費申請のプロセスが開始します。

f:id:itohiro73:20180116142859p:plain

この時点で「経費申請フォーム送信」イベントがトリガーされ、イベントに紐づけられたフォームが出現します。

f:id:itohiro73:20180116143715p:plain

適当な領収書の画像ファイルを選択し、必要な項目を入力し、Startボタンを押します。

f:id:itohiro73:20180116144114p:plain

ここで一旦Tasklistのページをリロードすると、承認タスクが現れます。

f:id:itohiro73:20180116145113p:plain

この承認タスクをクリックしてみると、今度は承認タスクに紐づけられたフォームが出現します。

f:id:itohiro73:20180116145031p:plain

demoユーザーにアサインするために、右上のClaimボタンを押すと、

f:id:itohiro73:20180116145307p:plain

承認タスクがdemoユーザー担当になります。

f:id:itohiro73:20180116145337p:plain

これで承認できるようになったので、ドロップボックスを「承認」のままCompleteボタンを押してみましょう。

f:id:itohiro73:20180116145624p:plain

すると、今度はTasklist経理担当の確認タスクが出現します。

f:id:itohiro73:20180116150004p:plain

以上で経費申請のワークフローが経理の確認待ちという状態まで遷移しました。

閑話休題)Cockpitでの実行中のプロセスの確認

デモの最中ですが、ここでいったんCockpitという機能を紹介したいと思います。

CamundaにはCockpitというダッシュボード機能がデフォルトで備わっており、デプロイされたBPMNや、現在実行中のプロセス、現在タスク完了待ちのユーザータスクなどが確認できます。

試しに上記のデモの途中の、経費を承認後(つまり、経理の確認待ちの状態)に、実行中のプロセスの状態をCockpitで確認してみましょう。

Cockpitにアクセスし、Running Process Instancesをクリックします。

http://localhost:8080/app/cockpit/default/#/dashboard

f:id:itohiro73:20180116150227p:plain

実行中のプロセスが表示されるので、ExpenseApplicationを選択します。

f:id:itohiro73:20180116151046p:plain

すると、下のように現在①プロセスが「確認」タスクのところにとどまっていることが可視化されます。

f:id:itohiro73:20180116152236p:plain

このように、Cockpitは実行中のプロセスをモニターするのに便利な機能を提供します。

確認タスクの実行により経費記録のサービスタスクをトリガー

さて、デモに戻ります。

上のCockpitでも確認できるように、最後に残るユーザータスクは経理の確認のみで、そのあとはサービスタスクである経費記録(ロギング)がトリガーされるはずです。

さっそく「確認」タスクを完了してみましょう。

Tasklistに戻り、「承認」タスクの場合と同様に「確認」タスクをClaimし、demoユーザーにアサインします。

f:id:itohiro73:20180116153128p:plain

f:id:itohiro73:20180116153156p:plain

今回のタスクは確認するのみなので、特に何も編集する必要なしにCompleteボタンを押します。

f:id:itohiro73:20180116153336p:plain

「確認」タスクをCompleteするとすぐに「経費記録」のサービスタスクが実行されるので、Spring Bootのログを確認してみましょう。

2018-01-16 15:41:35.131  INFO 16948 --- [io-8080-exec-10] sample.service.ExpenseRecorder           : 内容: Scala関西の交通費
2018-01-16 15:41:35.131  INFO 16948 --- [io-8080-exec-10] sample.service.ExpenseRecorder           : 金額(円): 28000.0
2018-01-16 15:41:35.140  INFO 16948 --- [io-8080-exec-10] sample.service.ExpenseRecorder           : 経費種別: 旅費交通費
2018-01-16 15:41:35.140  INFO 16948 --- [io-8080-exec-10] sample.service.ExpenseRecorder           : 承認済み?: はい

見事ログが出力されましたね!

今回は簡単なデモのためにログ出力としましたが、サービスタスクの実装はJavaのクラスで行われているので、外部のAPIを叩くもよし、データベースにストアするもよし、メッセージキューに送信するもよし、なんでもできます。

以上で今回の経費申請デモは完了です。今回の記事で実装したプロジェクトはこちらになります。

github.com

皆さんの手元でも、承認が「却下」になる分岐を実行してみたり、「却下」の場合の情報も出力するサービスタスクを追加して実行してみたり、遊んでみてください。

業務ワークフローをBPMNエンジンで実装するメリット

CamundaのようなBPMNワークフローエンジンを利用するメリットとして、私個人として一番大きいと思っているのは、ワークフローの「状態遷移」をバックエンドの実装と分離することができるところです。BPMNでモデル化できるような業務フローをバックエンドサービスとしてフロムスクラッチで実装しようとすると、どうしてもステートマシンのようなロジックを実装する必要がでてきます。手動で実装するステートマシンは結構楽しいんですが、業務要件に変更が加わった際に牙を向きます。ある状態から他の状態へ遷移する途中に新しい状態や分岐が必要となった場合、影響範囲の調査もなかなか厳しいですし、実装の変更工数はもとよりテストの工数もかさみます。さらにいうと本番環境でステートマシンから外れた例外的な状態遷移を実行する必要が出てきたときにニッチもサッチもいかなくなるという悲しみも抱えることになります(経験談)。

BPMNワークフローエンジンを用いて状態遷移をBPMNのモデルに集約するようにすると、状態遷移のロジックは分離され、バックエンドの実装は情報のストア(CRUD)やメッセージングに専念することができます。BPMNモデルそのものが業務要件を表現しているので、変更の影響範囲は可視化されます。新しい状態「遷移」を追加するにはBPMNのモデル側を変更すればよく、状態「情報」そのものはサービスタスクを用いてバックエンドにストアするような形になるでしょう。バックエンド側に遷移ロジックが入り込まないことで、変更に伴う状態の矛盾のようなバグが入り込む余地がなくなり、アジャイルで柔軟かつロバストシステム開発を実現できるのではないかと考えています。

さらに、バックエンドの情報ストアに関しては、Reladomoのように履歴管理が容易なデータアクセスレイヤーを利用できると業務アプリケーションとしてはさらにいろいろ面白いことができるでしょう。この辺はいつか別のブログで書けると良いなと思います。

BPMNに変更を加える際のデプロイの方法論に関してはいろいろ検証が必要だと思いますが、デプロイ前にワークフローの中間状態のプロセスを排除(全て終了まで持っていくか、全てを始点に戻す等)するような施策が必要になってくるかなと思います。

第三回のまとめ

今回はBPMNでモデル化した経費申請フローを用いて、Camunda上で実行させるのに必要なフォーム実装とサービスタスク実装、BPMNの設定を行いました。第一回〜第三回までの準備・モデリング・実装プロセスだけで実際に動作する業務アプリケーションが完成しました。

いかがだったでしょうか。思いのほか簡単にモデリングできるし、アプリケーションの実装も非常にシンプルです(実装のほとんどはフォーム側で、バックエンドに相当するJavaのコードは今回はほとんど書いてませんw)。

ここまででCamundaの持つモデリング駆動の開発の可能性と、ワークフローエンジンとバックエンドストアの責務分離の強力さをなんとなく感じてもらえたら幸いです。

次回は、BPMN単独ではなかなかモデリングが難しい、意思決定のためのモデリング記法であるDMNを学んでみましょう。今回までに作成した経費申請ワークフローを拡張し、意思決定プロセスを導入してみます。お楽しみに!

【第二回】経費申請のフローで学ぶCamundaの基本(BPMNモデリング編)

前回Camunda ModelerのインストールとCamunda Spring Boot StarterによるCamunda環境構築を行いました。

今回は、前回インストールしたCamunda Modelerを使って、「経費申請」の基本的なワークフローをBPMNでモデリングしてみましょう。

BPMNとは

BPMNとはBusiness Process Modeling Notationの略で、ビジネスプロセスをモデリングするための記法です。

ビジネスプロセスとは業務の一連の流れのことで、BPMNは、タスクの担当者や担当範囲、フローの分岐条件、システムが担当するタスクやメッセージのやりとりなどが明確かつロジカルに定義できる、定型作業としてのワークフローをモデルとして表記することを得意とします。

百聞は一見にしかずなので、経費申請のワークフローを下記のような簡単な業務要件としてモデリングしてみましょう。

  • ユーザーが経費申請のフォームを入力する
  • 承認者が申請を承認する
    • 申請が却下された場合、ワークフローは終了する
    • 申請が承認された場合、経費の記録のためのタスクに経理アサインされる
      • 経理が確認を終了すると、自動的にシステム上で経費が記録され、ワークフローは終了する

上記のような要件をBPMNで表現してみると下のようなモデルが出来上がります。

f:id:itohiro73:20180115112911p:plain

GitHubにもあげてあるのでCamunda Modelerから開いて見ることも可能です。

ぱっと見ただけでも直感的に何を表現しているかわかりやすいかと思いますが、今回はこのBPMNのモデルを作成するのに必要ないくつかのコンポーネントの説明と、Camunda Modeler上でのコンポーネントの作成方法を解説していきます。

プール

ひとかたまりのビジネスプロセスや、あるプロセスを担当する一番高次な概念(たとえば部署等)を表現するためのコンポーネントを「プール」と呼びます。プールは、Camunda Modeler上の左下にあるこちらのボタンを押すと作成することができます。

f:id:itohiro73:20180115112950p:plain

プールの詳細についてはこちらのドキュメントに詳しく記載があります。

今回は「経費申請」のためのプロセスということで、こちらのボタンから経費申請のプールを作成すると下のようになります。

f:id:itohiro73:20180115113026p:plain

プールの名前(今回は「経費申請」)は、プールを選択した際に右側に表示されるProperties PanelName欄に入力すればOKです。

f:id:itohiro73:20180115113055p:plain

プール以外の他のBPMNコンポーネントについても、名前に関してはすべて同じProperties PanelName欄から記入することができます。

スイムレーン

スイムレーンとは、プール内で特定のタスクの担当範囲を分割するためのコンポーネントです。詳細についてはこちらのドキュメントを参照してください。

今回の経費申請ワークフローでは、申請者承認者経理という担当範囲を定義しているので、経費申請プールにこのスイムレーンを作成してみましょう。

まずはプールを選択した際に右側に現れるDivide into two Lanesというボタンを押してみましょう。

f:id:itohiro73:20180115113135p:plain

下のように、プールが2つのレーンに分割されます。

f:id:itohiro73:20180116082730p:plain

次に、どちらかのレーンを選択して右のメニューのAdd Lane aboveもしくはAdd Lane belowどちらでも良いので押して見ましょう。

f:id:itohiro73:20180115113159p:plain

これでスイムレーンが3つ作成できたので、それぞれ申請者承認者経理としましょう。

f:id:itohiro73:20180115113234p:plain

イベント

BPMNには「イベント」という概念があり、ワークフローのスタート起点となるイベントStart Eventであったり、プロセスの途中に発生するエラーハンドリングのようなイベントIntermidiate Event、プロセスの終点となるイベントEnd Eventを表記することができます。

Camunda Modeler上では左のメニューにあるこれらの丸いコンポーネントがそれぞれStart EventIntermidiate EventEnd Eventとなります。

f:id:itohiro73:20180115113558p:plain

さらに、それぞれのイベント内にもメッセージを起点とするイベントMessage Eventや時間発火するイベントTimer Event、特定の条件で発動するイベントConditional EventSignal Event等さまざまな種類のイベントが存在します。今回はこれらの紹介は省きますが、イベントの詳細はこちらのドキュメントを参照してみてください。

f:id:itohiro73:20180115114027p:plain

今回は起点イベントとして「経費申請フォーム送信」というStart Eventを作成してみましょう。左のメニューからStart Eventを選択して申請者レーンに配置をし、「経費申請フォーム送信」という名前をつけるだけなので直感的にいけるかとおもいます。

f:id:itohiro73:20180116082758p:plain

タスク

さて、一旦起点イベントから「経費申請フォーム」が送信されたら、今度は承認者が申請フォームを承認する必要があります。BPMNでは、このような、人やシステムが何かしらのアクションを起こす概念を「タスク」と呼びます。

タスクにもさまざまな種類があり、こちらのドキュメントで詳細を確認することができます。

「経費申請」フローで使用するタスクは、人がアクションを起こすUser Taskと、システムがアクションを起こすService Taskの2種類で、今回の例ではUser Taskは「承認」と「確認」、Service Taskは「経費記録」のタスクをこなします。それぞれ下のようなBPMNコンポーネントとなります。

f:id:itohiro73:20180115114159p:plain

f:id:itohiro73:20180115114213p:plain

ここでは承認者が承認するユーザータスクUser Taskを作成してみましょう。

先ほど作成した「経費申請フォーム送信」イベントを選択すると、タスクを追加するためのAppend Taskというメニューが表示されるので、こちらをクリックします。

f:id:itohiro73:20180115114650p:plain

タスクを配置する場所をマウスで選択できるので、承認者レーンでクリックします。

f:id:itohiro73:20180115114851p:plain

すると、下記のようなメニューが出現するので、レンチマークのChange Typeを選択し、

f:id:itohiro73:20180115114936p:plain

タスクのタイプとしてUser Taskを選択しましょう。

f:id:itohiro73:20180115115001p:plain

名前を承認とすることで、下のようなモデルが出来上がります。

f:id:itohiro73:20180115115031p:plain

これでUser Taskを作成することができました。

ゲートウェイ

ビジネスプロセス上では、さまざまな状況に応じてプロセスが分岐したり、分岐したフローが合流したりといった事象を表現をしたいことがあります。BPMNでこの分岐や合流を表現するのがゲートウェイです。

ゲートウェイの詳細はこちら

今回は、経費申請が「承認されたかどうか」という判断と分岐するためにExclusive Gatewayというコンポーネントを使います。

f:id:itohiro73:20180115115117p:plain

Exclusive Gatewayは、ある条件(例えば今回は「承認済みかどうか」)に対して一つの分岐フローだけが選択されるゲートウェイになります。

承認ユーザータスクから「承認済み」かどうかを判定し分岐するためのExlusive Gatewayを足します。

f:id:itohiro73:20180115115135p:plain

作成されたExclusive Gatewayに「承認済み?」という名前をつけ、「いいえ」の場合の分岐に「経費却下」というEnd Eventを作成しましょう。下のようにEnd EventAppend EndEventから作成することができます。

f:id:itohiro73:20180115115207p:plain

f:id:itohiro73:20180115115235p:plain

「承認済み?」の条件が「はい」だった場合の分岐には、経理のレーンに「確認」のUser Taskを作成します。

f:id:itohiro73:20180115115304p:plain

仕上げ

ここまででプールスイムレーンイベントタスクゲートウェイというBPMNの基本的なコンポーネントを使用して「経費申請」のワークフローのほぼ最終段階までモデリングしてきました。最後に、これまでと同様の流れで「経費記録」というService Taskと「経費受理」というEnd Eventを作成すれば今回のBPMN図は完成になります。

f:id:itohiro73:20180115112911p:plain

最初にも書きましたが今回作成したBPMNはこちらのGitHubにあげてあるので、Camunda Modelerから開くことができます。最終成果物を確かめてみたい方はこちらからどうぞ。

https://github.com/itohiro73/camunda-expense-example/blob/2-bpmn-model/src/main/resources/expense_application.bpmn

第二回まとめ

いかがでしたでしょうか。業務要件の定義さえしっかりされていれば、Camunda Modelerを用いたBPMNモデリングは非常に直感的かつ簡単にできることがおわかりいただけたのではないでしょうか?

今回は「経費申請」という単純なワークフローを題材に、下記のBPMNコンポーネント用いてビジネスプロセスをモデリングしてみました。

次回は、今回作成したBPMNモデルをCamundaの実行環境にデプロイし、いくつかのフォームとJava実装と連携することで、実際に動く経費申請アプリケーションを作成してみます。お楽しみに。

【第一回】経費申請のフローで学ぶCamundaの基本(導入編)

Camundaとはオープンソースのワークフロー&ビジネスプロセスマネジメントの総合プラットフォームで、BPMN2.0CMMN1.1DMN1.1に準拠したモデリングや実行環境を構築することができます。

JJUG CCC Fall 2017では世界的な証券会社であるゴールドマン・サックス社においてもCamundaを利用してビジネスプロセスの可視化や自動化を行なっているという講演がありました。

現状Camundaに関する日本語の情報はほとんど見当たらない状況で、アドカレの記事でも書いたように自社での導入検証もしていければとも考えているので、せっかくなのでオープンな形で技術検証をしていきたいと思います。

今回、下記のような連載形式で、「経費申請」という比較的簡単な業務フローをテーマに、Camundaを用いたモデリングとワークフロー実装の基本を学んでいきたいと思います。

実際に業務要件をモデリングするような流れでひとつのアプリケーションを作成していこうと考えているので、網羅的に機能を解説していくというよりは、「Camundaを使うと何ができるのか?」を全体を通してイメージできるようになることを目的としています。

(下の流れはあくまで予定)

本エントリーでは導入編として、モデリングツールであるCamunda Modelerのインストールと、Camundaの実行環境をSpring Boot上に構築してみます。

連載を通して基本的に下記の環境にて動作確認をしていますが、他のOSやJavaバージョンでもほとんど差異はないものと思います(おそらく)。もし何か気付いた点などありましたらブログへのコメントやGitHubへのプルリクをいただけると幸いです。

Mac OS High Sierra: Version 10.13.2
Java 9
Gradle 4.2.1
Spring Boot 1.5.9-RELEASE
Camunda 7.7
Camunda Spring Boot Starter 2.2

Camunda Modelerのインストール

Camunda ModelerはCamundaのワークフローエンジン上で実行することができるBPMN/CMMN/DMNのモデリングツールです。各モデリング記法の目的やモデリング、実行方法は第二回以降の記事で少しずつ学んでいければと思います。

Camunda Modelerのインストールに関しては非常に簡単で、こちらのダウンロードページから対応するOSのバイナリをダウンロードして、実行ファイルを実行するだけです。

https://camunda.com/download/modeler/

f:id:itohiro73:20180115002450p:plain

次のエントリーではBPMNのモデリングをしてみる予定なので、まずは「BPMN diagram」をクリックしてみましょう。

f:id:itohiro73:20180115002516p:plain

BPMNのモデリングの画面が立ち上がりました。Camunda Modelerに関してはこれで準備完了です!

Spring Boot上でCamundaの実行環境を構築

Spring Boot上のCamunda実行環境をGradleで構築します。CamundaのSpring Boot StarterはこちらのGitHubで提供されています。最小構成のbuild.gradleはこんな感じ。

build.gradle

buildscript {
    ext {
        camundaVersion = '7.7.0'
        springBootVersion = '1.5.9.RELEASE'
        camundaSpringBootVersion = '2.2.0'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
        classpath "io.spring.gradle:dependency-management-plugin:1.0.4.RELEASE"
    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'
apply plugin: "io.spring.dependency-management"

group = 'sample'
version = '0.0.1-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencyManagement {
    imports {
        mavenBom "org.camunda.bpm:camunda-bom:${camundaVersion}"
        mavenBom "org.springframework.boot:spring-boot-dependencies:${springBootVersion}"
        mavenBom "org.camunda.bpm.extension.springboot:camunda-bpm-spring-boot-starter-bom:${camundaSpringBootVersion}"
    }
}

dependencies {
    compile('org.camunda.bpm.extension.springboot:camunda-bpm-spring-boot-starter-webapp')
    compile('com.h2database:h2')
    testCompile('org.springframework.boot:spring-boot-starter-test')
    if(JavaVersion.current() == JavaVersion.VERSION_1_9) {
        runtime('javax.xml.bind:jaxb-api:2.3.0')
    }
}

最小構成のアプリケーションコードはこんな感じ。 src/main/java/sample/CamundaExpenseProcessApplication.java

package sample;

import org.camunda.bpm.spring.boot.starter.annotation.EnableProcessApplication;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@EnableProcessApplication
public class CamundaExpenseProcessApplication {

    public static void main(String[] args) {
        SpringApplication.run(CamundaExpenseProcessApplication.class, args);
    }
}

Camundaが使うデータベースをin-memoryのh2に設定するために、application.propertiesを作成します。下記の設定ではメモリー上のh2データベースを使用するのでアプリケーションを立ち上げなおすたびに情報はリセットされます。もし情報を永続化しておきたい場合はspring.datasource.url=jdbc:h2:mem:camundaコメントアウトして代わりにspring.datasource.url=jdbc:h2:~/camunda;DB_CLOSE_ON_EXIT=falseを有効にしてください。

src/main/resources/application.properties

spring.datasource.url=jdbc:h2:mem:camunda

#Use this config in case you want to persist camunda data into file system (comment the config above in that case)
#spring.datasource.url=jdbc:h2:~/camunda;DB_CLOSE_ON_EXIT=false

デフォルトの設定として、デモユーザー(id/password = demo/demo)と、すべてのタスクを表示するfilterをapplication.ymlを追加しておきます。

src/main/resources/application.yml

camunda.bpm:
  admin-user:
    id: demo
    password: demo
    firstName: デモ
    lastName: ユーザー
  filter:
    create: All tasks

以上で最小限構成のCamundaアプリケーションが立ち上がる状態になりました。GitHubに動作確認済みのプロジェクトを上げてあるのでこちらからどうぞ。

https://github.com/itohiro73/camunda-expense-example/tree/1-intro

GradleでSpring Bootアプリケーションを立ち上げます。

$ gradle bootRun

立ち上がったら http://localhost:8080/ にアクセスすると、下のような ログイン画面が立ち上がります。

f:id:itohiro73:20180115002610p:plain

上記で設定したデフォルトのデモユーザー(demo/demo)を使用してログインしてみます。

f:id:itohiro73:20180115002647p:plain

これでCamundaが立ち上がりました。トップ画面からはCockpitTaslklistAdminの機能にアクセスすることができます。

この時点ではまだワークフローをデプロイしていないのでCockpit上では何もできませんが、TasklistやAdminの基本的な機能は動くのでポチポチ遊んでみてもいいかもしれません。

以上でCamundaの準備は完了です!

第一回まとめ

今回は導入編として

をしてみました。次回は早速「経費申請」の基本的なフローをBPMNを用いてモデリングしてみたいと思います。お楽しみに。

新卒から12年エンジニアとして勤めた某外資証券会社を辞めてスタートアップにジョインした話

本記事は退職者その2 Advent Calendar 2017の25日目の記事です。

adventar.org

僭越ながら最終日を担当させていただきます、@itohiro73 と申します、よろしくお願いします。

半年前ちょっと前にすでに入社エントリーは書いているので順序が大幅に逆になってしまっていますが、今回は退職者アドベントカレンダーということで、せっかくの機会なので新卒から12年も勤めた前の会社のことを振り返ってみようと思います。

前の会社について

世界的にもかなり大きい外資系証券会社でエンジニアとして新卒から入社して12年と1ヶ月半くらい勤めました。会社名に関しては、自分がかなり大々的にOSS活動等もしていたこともあり公開情報からちょっと調べればわかるところではありますし、本記事を読んで明らかにわかる部分もあります。とくに隠しているわけではないのですが、一応大人の事情としてあえて言及はしないといったところです。

なぜ外資系証券会社に「エンジニア」で新卒入社?

よく、新卒で外資系証券会社の「エンジニア」という選択肢をどうしてとったのかとよく聞かれます。

(余談ですが12年くらい前は「エンジニア」ってあんまり呼ばれていなかった気がしていて、同様の職種は日本だとプログラマー、海外(社内)だとdeveloperと呼ばれるのが普通でした。いつのころからかエンジニア/engineerが定着しましたね。ということで当時的にはdeveloperとしての新卒入社です。)

外資系証券会社というと、投資銀行部門のバンカーであったり、トレーダーやセールス、アナリスト等のいわゆる「フロントオフィス」の職種が花形であるとされています。

そんななかで、なんでエンジニア?っていうのが外資系証券会社をよく知る人たちにとっての疑問なのでしょう。逆にエンジニア系の人たちにとっては、(今でこそFinTechが盛り上がりを見せているものの)そもそも金融であったり証券に興味がない人が多いため、SIやWeb系企業ではなくなぜ外資証券?っていうのが疑問なんだと思います。

実際当時の私にとっては、外資証券に興味があったわけでは特になく、いわゆる就職活動によくある自己分析というやつで下のような3つの軸を求めてました。

  • 自分で手をうごかしてものづくりをしたい
  • 自分がつくったものを実際につかってくれるユーザーの側で働きたい(フィードバックを直に受けたい)
  • インターナショナルで多様な環境で働いてみたい

この軸に沿っていてくれさえすればいいと思っていたものの、当時この3つすべてを満たすような会社は自分が調べた範囲内ではほとんど存在していませんでした。

そんな中バイト先で一緒に働いていた大学の同期が、〇〇っていう会社の選考を受けてきた〜っていう話を聞いたのがこの外資証券の会社でした。へぇーそんな会社があるんだと思ってリクナビから会社ホームページを調べたりいろんな口コミページ的なのをを眺めて見たら、「社内で使われているほとんどのトレーディングシステムや業務システムが内製化されている」「チームワークを重要視するカルチャー」「外資系企業で、多様性を重視、さまざまなバックグランドの人が働いている」というような文言がならんでいるじゃないですか。まさに上の3つの軸を満たしていそうで、「自分が求めているものにすごくしっくりくる、ここしかないんじゃないかー」、と思って早速エントリーしたのがすべての始まりです。

実際、就職活動中にお話を聞いた先輩社員の方達は皆ものすごく魅力的で、最高に輝いて見えました。その中には現在bitFlyerでCEOをされている加納さんもいました。こんなすごい人たちと一緒に働けるのであれば最高に楽しいだろうなと考え、全力で選考に挑んだところ、ご縁があって採用していただけました。

最初の「外資系証券会社でなぜエンジニア?」という疑問ですが、実際蓋を開けてみると、全世界で3万数千人いる社員のうちの約4分の1、つまり8~9000人のオーダーでエンジニアが働いています。この人員だけでITの大企業がつくれてしまう規模感です。それほどまでにエンジニアリングに力をいれている環境で、世界中の優秀なエンジニアと切磋琢磨しながら証券会社という複雑怪奇なビジネスをつくりあげていく。こんな最高に面白い環境は今考えても他になかったとおもいます。実際エンジニアとして最初の12年のキャリアを積むのにはこれ以上ない環境だったと感じていますし、今でもこの会社のことは大好きです。

前の会社のカルチャーと強み

ここの会社の一番好きなカルチャーは、スーパースター個人個人に頼るような仕事の進め方はせず、チームワークを最重要視するところ。チームとして最高のパフォーマンスを出すために皆がそれぞれ自分のチームの中での役割をしっかりこなす。"I did this"ではなく"We did this"と言うのが美徳とされる、そんなカルチャーでした。

このカルチャーはチームレベルではなく、会社全体の特性としても現れていました。よく「世界最強の投資銀行」などと評される会社でしたが、特定の部門が最強だったわけではありません。投資銀行のバンカー、トレーダー、セールス、アナリスト、コントローラーズ、オペレーションズ、コンプライアンス、テクノロジー、人事から総務や広報にいたるまで、すべての部門でそれぞれ最高のパフォーマンスを出し、部門間でのチームワークを発揮することで会社として世界最強の名をものにすることができていたのではないかと、自分は12年働いてきた中でそう感じています。

会社の外でよく聞く評判では、イカつい人が働いてそうとかガツガツしてそうとか怖そうとか言われるんですが、実際そんなことはなくて、一緒に働く人みなものすごく温和でやさしくて非常に人間味あふれる人たちでした。

さらにすごいのは、このカルチャーがグローバルで、しかも偉い人から新人まで一貫しているというところ。グローバルで数万人規模の大企業であるにも関わらず、日本で働いていてもニューヨークに出張してもインドに出張しても、香港やシンガポールの人たちとリモートで働いていても、常にアットホーム感あふれるものすごく暖かいチームワークを常に感じていました。

そしてもうひとつ一貫していたのが、皆一様にものすごく負けず嫌いであるというところ。「温和」と「負けず嫌い」って相反する概念じゃ無い?って思うかもしれませんが、これは本当に面白いんですが、このふたつの性質は同居できるんですね。フロントオフィスの人たちはわかりやすく勝ちにこだわって攻めて行くという意味での負けず嫌い。バックオフィスの人たちは絶対に負けない戦略をじっくり練って守って行くという意味での負けず嫌い。温和でやさしいけど皆さん芯がしっかりしてるんです。

マイノリティであり、多様性の一部であるということ

この会社に入って一番のカルチャーショックだったと言えるのは、「日本人であることがマイノリティである」ということに気付かされたことでした。

この会社の東京オフィスでは、なんと「日本人のためのコミュニティ」というものがが存在します。東京にいるのに日本人のためってどういうこと?と思うかもしれません。私の例をとってみれば、東京オフィスでのテクノロジー部の新卒同期は9人。そのうち日本人は4人だけです。グローバルでみるとテクノロジー部新卒同期全体で240人ほど、他のオフィスでの日本人採用はゼロだったので、同期の仲間たちでも日本人であるということは全体の2%にも満たない、相当なマイノリティです。

ではこの「日本人のためのコミュニティ」(Fuji Committeeという名で呼ばれていました)は何をするために存在するのでしょうか。日本人は、実は自分たちでは普段意識していないような日本人特有の性質を平均的に持っています。たとえば、コミュニケーションのとり方がハイコンテクストであったり、同調意識が働いて他の人と違う意見を自分の中にためこみがちだったりだとか、単純に英語が得意でないとか。あとは欧米人が比較的得意なアクティブな議論の場で発言するのが難しいと感じたりだとか、「自分の意見を表明しない」という認識を周りに持たれがちだったりとか、グローバルな環境で働いている日本人たちにとっては「あるある」的な話が山ほどあります。

このFuji Committeeでは、こういった自分たちの性質に関する気づきを得たりだとか、それぞれが抱えている悩みを共有したり、その問題について議論したり、日本人がグローバルに活躍してキャリア形成を促進して行くにはどうしたらいいか、といった話をする場でした。また、シニアなメンバーが自分の培ってきたキャリアについて話す場を設けてくれて、彼らが抱えていた悩みやコンプレックス、それらをどうやって解決してきたかを赤裸々に語ってくれたりしました。自分がまだジュニアだった頃には、自分にとって凄い神のような存在である彼らが、自分と同じような悩みをかかえていて解消してきたんだという話は、とても親近感を持てますし、安心感と勇気を与えられました。

この経験からも、自分が比較的シニアになってからも、自分がかつて抱えていた仕事上の悩みやコンプレックスをジュニアなメンバーたちと正直かつ素直に共有するようになりました。

このようなある属性にフォーカスしたコミュニティは、日本人向けのものだけではなく、黒人やヒスパニック、LGBTやWIT(Women In Technology)などの様々なコミュニティが存在していました。それぞれのコミュニティで特有の悩み共有や問題解決をおこなって行くことで、それぞれの属性をもつグループが最高のパフォーマンスを出せるように改善していき、結果として会社全体への強みへと昇華して行く。このように多様性を重視して、それぞれの強みを最大限活用できるようにしていくカルチャーの大切さというのは、自分がマイノリティであることを認識してはじめて身にしみて感じました。この会社で働いて初めて、どんな人であっても多様性を構成する一部である、そんなあたりまえのことを大きな気づきとして得られた気がします。

前の会社のエンジニアリングについて

前の会社では、「コントローラーズ」と呼ばれる、トレーダーの損益を日々モニターしてリスク管理やレポーティングをする職種の人たちが日々使うアプリケーションの設計・開発・テスト・運用に関わりながら最初の10年間のキャリアを過ごしました。アナリストからアソシエイト、ヴァイスプレジデントと昇進を重ね、エンジニアとしてはジュニアデベロッパーからプロジェクトマネージャー、QAのグローバルコーディネーション、テクニカルアーキテクトと呼ばれるアプリケーションチームの技術面を支える仕事まで様々なレイヤーを経験しました。キャリアのほとんどをここのチームで過ごしたので、実はここで学んだことや経験したことが自分のエンジニアリング哲学を形成する大部分になるんですが、残念ながら会社の機密に関わる仕事が多かったため、ほとんど語ることができません(汗)

代わりに、最後の2年ほどを過ごしたプラットフォームチーム、そのなかでもOSS活動について語って見たいと思います。プラットフォームチームとは、アプリケーション開発とインフラのちょうど間くらいに位置するチームで、開発者たちが日々使うアプリケーション(たとえばSoftware Development Life Cycleのなかで重要な位置を閉めるコードレビューやビルド、リリースのプラットフォームであったり、各言語のライブラリやフレームワーク、テストのためのプラットフォーム、ジョブスケジューラー、ワークフローエンジン等々多岐に渡ります)を開発していました。ここは純粋に技術的なチームだったため、会社のビジネス業務に直接関わる機密的な話はほとんどなく、比較的表に出しやすい内容なのです。

Eclipse Collections秘話

その中でも、Eclipse Collectionsと呼ばれるJavaのコレクションフレームワークは私にものすごく関わりが深い技術で、かつOSSで公表されている技術なのでブログに記載することに何の問題もありませんw

コレクションフレームワーク誕生

このフレームワークは、もともと Donald Raab というエンジニアがコントローラーズのテクノロジーチームでCaramelという名で開発を始め、そのあと社内で広く使われるようになりました。上記の通り私もコントローラーズのチームで働いていたので、このフレームワークは当初からユーザーとして使用していました。Javaのコレクションフレームワークとしては相当使いやすい実装でかつパフォーマンスも良く、同僚たちとは「Javaに標準で入るのはいつだ」なんて冗談をよく言っていました。2012年に同社初のOSSとして公表された際は社内の皆でものすごく興奮した覚えがあります。

拡散と認知

2014年にはJavaOneで初めてDonald Raab がキーノートに登壇し、同フレームワークのセッション発表もされたことで、世界的に知られるようになりました。これを知った私はものすごく興奮し、日本でもこのフレームワークの認知を広げたいと考え、日本のJavaコミュニティへの進出の機会を探り始めました。JJUG CCC 2014 Fallに始めて参加した際に、JJUG会長の鈴木さんとお話ししたところ、驚いたことにJavaOneキーノートでの発表を見てくださっていたようで、日本支社の誰かとコンタクトを取りたいと思っていた、とのことでした。そこからはあれよあれよという間に話が進み、2015年Java Day Tokyoでの同社での初登壇が実現しました。その直後にはJJUG CCC 2015 Springでもセッションをすることができ、コミュニティによる情報拡散の手助けにより、このフレームワークは日本で一気に知名度を上げます。

この頃のコミュニティ活動は完全に社内での同フレームワークの1ユーザーとしてのボランティア活動であり、別に担当していた本業のかたわらやっていました。もともと日本では同フレームワークの開発チームはいなかったのですが、業務のかたわらちょいちょいコントリビュートを送るようにしたり、東京チームで興味のあるエンジニアにもコントリビュートを勧めたりしていました。

NYからの電話

そんな2015年夏のある日、ニューヨークから自分のデスクに一本の電話がかかってきました。

電話の主は上述の Donald Raab 、同フレームワーク創始者でありプロジェクトマネジャーです。ニューヨークのマネージングディレクターなのでめっちゃ偉い人。面識はあったものの、直属の上司ではなかったので電話で話したことはほとんどありません。いったい何の要件かと聞いてみると、

「今度同フレームワークEclipse Foundationに移管するんだが、このプロジェクトリードをお前に任せたい。日本のお前のボスに相談しようと思うんだが、どう思う?」

と言うのです!ぶったまげましたが、同時に体の芯が震えるほど喜びました。同フレームワークはそれまで、会社内のsvnで開発・管理されているコードが社内でリリースされ、リリース時のコードのスナップショットのみがGitHubにアップロードされていると言う状態でした。つまり、GitHub上にソースコードは公開されているものの、外部からのコントリビュートを受け付けることができなかったのです。これでは完全なOSSとは言えません。Eclipse Foundationへの移管によりこの問題を解消し、完全に外部での開発ライフサイクルにのせてOSS化するというのが本プロジェクトのゴールです。

実はこの時点でUS以外での同フレームワークダウンロード数は日本がダントツ1位で、大きなユーザー数を占めていました。これはまさに上述した日本でのコミュニティ活動での成果であり、この功績を買われて白羽の矢がたったのです。

Eclipse Foundation移管と"Eclipse Collections"誕生

当然ふたつ返事でプロジェクトのリードを引き受けました。実際の移管プロジェクトでは、最新バージョンの機能開発はもちろん、同フレームワークのパッケージネーム変換から、Eclipse Foundationの開発フローに法ったワークフロー整備やツールの整備、法的書類のフォローアップなど、技術的・非技術的なチャレンジがさまざまありました。自分以外のプロジェクトメンバーは全員ニューヨーク在住、そしてEclipse Foundation本部はカナダのオタワにあるので時差の問題も大きく、電話ミーティングでのフォローアップも大変でした。2015年のJavaOneではDon Raabと共同で本件についての発表を行い(JJUG CCC 2015 Fallで発表された日本語版の同内容の資料はこちら)、2015年12月に無事移管リリースを迎えます。"Eclipse Collections"誕生の瞬間です。

当然OSSだけでは会社のビジネスに直結するわけではないので、移管リリース終了後は本業のプロジェクトも進めつつのOSS業をこなしていました。

紆余曲折を経てのEclipse Collections OSSリリースでしたが、今では開発者以外によるコミュニティでの発表も見られたりと、非常に裾野が広くなってきており、非常に喜ばしい限りです。

OSSブランディング

こういったOSS活動は会社のブランディングとしての側面も大きいです。ここでいうブランディングは大きく2つの意味合いがあります。まず一つ目は、社外のエンジニア達に会社に興味を持ってもらうこと。つまり、会社がOSSを使うだけではなく、OSSを開発して発信することでコミュニティへの還元をしている、それくらい技術にコミットしているのだということを知ってもらうことです。もう一つは、社内のエンジニア達の士気をあげること。自分たちの会社はOSSにコミットするくらいエンジニアリングに対して本気なんだということを理解してもらい、そしてOSSという皆が平等に貢献できる土壌をつくることで、社内エンジニアリングの士気を上げて行く、空気感をつくることです。

こういった土壌はWeb系の企業だとわりと普通かつ自然に培われるものかもしれませんが、世界的な大企業であり金融機関、という環境でこのムーブメントをゼロから起こすのは相当な情熱(Passion)忍耐力(Patience)やり続けること(Persistence)が必要です。この3つのPは私が Eclipse Collectionsの創始者である Donald Raabから学んだ大切な哲学です。

彼は実際2004年からEclipse Collectionsのもととなるライブラリを作り始め、2012年に初めてソースコード公開、そして2015年についに会社外からもコントリビュータを受け入れられる完全なOSSとしてEclipse Foundationへの移管を完了しました(私が任されたEclipse Foundation移管のプロジェクトはこれです)。そしてDonは今現在もひきつづき同フレームワークJavaの標準ライブラリとして取り込むための活動を続けています。これらの成果はまさに彼の情熱(Passion)忍耐力(Patience)やり続けること(Persistence)の賜物と言えると思います。

このOSS活動で学んだエンジニアブランディングにまつわる経験は現職でも生かされていて、現在の会社であるFOLIOで一緒にクリエータブランディングに注力してくれているよこなさん @ihcomegaFOLIOアドカレ第一日目にもその辺の話題が書かれています。

ihcomega.hatenadiary.com

退職直前のこと

この会社には社内SNSがあったんですが、私もそれなりに投稿してました。いわゆる社内ブログとか呟きみたいのの影響力(like数とか、コメントの数とか、投稿数とか)によってポイントランクみたいのがあって、退社直前についに8000人中の20位内に入り込みました。20位以内だとリストの一番最初のページにプロフィールが表示されるので、やめる直前でトップページに食い込めて喜んだ覚えがあります。この社内SNSのおかげで会ったことないけど私のこと知ってくれている人は世界の各オフィスに結構いて、それなりにグローバルにも影響力あったと思います。ただ、これに関しては完全に私のボスだった Donald Raab のおかげであり、彼がいなければOSSのプロジェクトリードをまかされることもなかったし、社内外に影響のあるプロジェクトを任されることもなかったと思います。彼は私のキャリアのなかで最も尊敬する人物のひとりで、技術的にもマインド的にも彼から学んだことの大きさは計り知れないです。

なぜ辞めたのか

めちゃめちゃ良さそうな会社なのに、なんで辞めたの?と疑問に思うかと思います。実際今でもこの会社のことはすごく好きですし、未だに動向はウォッチしています。

150年ほども歴史があるグローバルな会社で、仕事も楽しく待遇も良く福利厚生も良い、本当に至れり尽くせりのとても良い会社でした。とはいえ、これだけの長い歴史の中で、いろんな人たちが作り上げてきた会社、ということを肌で感じる中で、自分はただそのおこぼれを享受しているだけなのではないか、というのがここ数年のもやもや感でした。もし可能であれば、自分でもこんな素晴らしい会社・組織をつくってみたい、何よりもエンジニアが本気で活躍できるような組織や土壌というものを自分の手でつくりあげてみたい、ということをすごく考えるようになりました。

また、ここ数年日本の技術コミュニティに顔を出すようになり、日本のエンジニアたちの強さ、凄さを目の当たりにする機会が増えました。それとは裏腹に、グローバルと比較した際の日本のエンジニアたちの待遇の低さというのもよく嘆かれています。日本で埋もれているような優秀なエンジニアたちが思う存分実力を発揮して、その実力を適切な待遇で評価される、そして彼ら彼女らがグローバルで活躍できるような土壌をつくる、そんなエンジニア組織を自分でつくれたらいいなと考えていました。

そんな中FOLIOという面白い会社と出会いました。この会社と出会った当時はまだ創業1年そこそこで、社員数20数人ほどのできたてほやほやのスタートアップです。代表の甲斐もクリエーター(デザイナー+エンジニアをあわせてFOLIOではクリエーターと呼んでいます)を非常に大事にしており、次世代証券会社、そして今までの常識をくつがえすような金融プラットフォームをこのチームでつくりあげて行くんだという、そのマインドがものすごく気に入りました。エンジニアたちとも話がめちゃめちゃ盛り上がり、自分の次のキャリアを積むのはここしかないと考えました。なんせ、次世代証券会社をビジネス陣・クリエーター陣で協力してゼロからつくりあげてしまおうというスタートアップなので、私がやりたいと思っていたことにどハマりなのです。

このFOLIOが持っている多様性はFOLIO Advent Calendar 2017を見てもらえばわかると思います。現在ではクリエーター陣だけで30数人(全社では60人ほど)の組織になっていますが、その担当範囲やバックグラウンドは多種多様です。証券会社をつくるクリエーターとして、それぞれが自分のプロフェッショナリズムを発揮して仕事をしています。

qiita.com

そして、FOLIO Advent Calendar 2017では「株式会社FOLIOの次世代証券システムをひもとく」と題して本日の最終記事を担当したのですが、当記事でも述べたように次世代証券会社のシステムをつくりあげていくにはまだまだやることが山積みであり、とてもチャレンジングなさまざまなプロジェクトがわれわれを今後待ち受けています。

締め

以上、わりとエモい感じで前職の会社のお話と5月にジョインしたFOLIOのお話を書き連ねました。まだまだ書き足りないことはいっぱいあったんですが、さすがに長すぎると自粛しました。楽しんでいただけたら幸いです。