松岡(little_hand_s)さんを招待してDDDモデリング・ハンズオンを開催しました

マーベリック株式会社、技術広報のリチャード 伊真岡です。先日弊社に松岡さんをお呼びしてDDDモデリング・ハンズオンを開催しました。

little-hands.hatenablog.com

弊社では「エリック・エヴァンズのドメイン駆動設計」の読書会を行っています。過去には「実践ドメイン駆動設計」も読書会の課題図書として取り上げたのですが、エヴァンズ本はそれと比べて抽象度が高く、読解はなかなか挑戦しがいがあります。読書会メンバーから「実際にモデリングを体験したら理解の助けになるのではないか」という声が上がり、ハンズオン形式のワークショップを提供してくれる社外の講師を探すことになりました。

エリック・エヴァンスのドメイン駆動設計

エリック・エヴァンスのドメイン駆動設計

  • 作者:Eric Evans
  • 出版社/メーカー: 翔泳社
  • 発売日: 2013/11/20
  • メディア: Kindle版

そこでDDDに関して、ブログや雑誌寄稿、登壇などで積極的に情報発信を行っている松岡さんにお願いしたところ、快く弊社でのモデリング・ハンズオン開催を引き受けてくださいました。

www.slideshare.net

ハンズオンはおよそ2時間半、最初の30分程度は松岡さんからのスライドを使った解説です。「モデリングとは何?」「どんな順番でモデリングを進めればいいの?」という基本に立ち返って、かつ松岡さんの実体験とともに解説していただき、エヴァンズ本を読んだけでは理解に自信が持てなかった部分を確認しながら学べました。

その後はハンズオンに移ります。ホワイトボードを使って「ユースケース図」「ドメインモデル図」という2種類の図をみんなで描きながらモデリングを行いました。

f:id:maverick-techblog:20200121040035p:plain

2チームに分かれてのモデリングで、片方のチームは「通販サイトのアフィリエイトプログラム」もう一方は「社内本棚に置く本の購入申請プロセス」というドメインをそれぞれ選びました。

「通販サイトのアフィリエイトプログラム」は非常に範囲が大きいドメインなので、ワークショップで収まる内容にするため適宜制約を仮定しつつ、一方で現実に即した「ポイント付与のタイミング」「ユーザや商品とアフィリエイトポイントの紐付け」などの難しい課題に挑戦していました。

「社内本棚に置く本の購入申請プロセス」は現在社内で実際に使っている手順をもとに議論しました。社内手順はスプレッドシートを使った非常に簡単なもので、「申請→承認くらいしか手順がない」「購入申請が予算内かどうかしか条件がない」と思っていたのですが、モデリングによってこの予想は裏切られます。運用でカバーしていた複雑さがあらわになり、管理者が様々な条件を考え、複数の状態遷移が伴う業務手順であることがわかりました。世の中の「スプレッドシートで十分」と思っている社内業務や、「CRUDで表現できる」と思っているアプリケーションの多くも、運用でカバーしている部分を解き明かせば想像以上に複雑なのかもしれません。

以下でワークショップに関する参加者からの感想の一部を紹介します:

  • ユースケース図→ドメインモデル図を作成する流れはわかりやすい、具体から抽象の流れはやりやすい
  • 困ったら世界を単純化(この世界の住民は全員アフィリエイトプログラムの会員など)で完結した、ただ実際にはドメインモデルはさらに複雑になりそう
  • ドメインモデルのオブジェクトとテーブルは別という意識が持てた
  • 多重度(1対N、N対N)を意識すると思考漏れに気付けそうだとわかった
  • 「状態遷移図をモデリングの早いタイミングで使っていい」「ホワイトボードの議論をツール(PlantUMLなど)で清書する」と現場でも使えそうなガイドをいただけた
  • 経験をもとに技術的な解決策を提示してもらえたのが学びになりました:
    • 「問)ユビキタス言語は頑張っても語彙がブレそう→答)画面の表示文言で合わせる方法がある」
    • 「問)ドメインモデルを自然に反映するためにデータベース層はRDBよりNoSQLの方が良い?→答)突き詰めるとCQRSが選択肢の一つになる」

参加者からも好評だった今回のワークショップ、知識として身につけたものを手を動かして確認できる良い機会でした。松岡さんありがとうございました!弊社ではDDDを含め今後も開発チームのスキル向上を、楽しさを追い求めながら継続していきます。

第二回ビジネス&開発部門 合同勉強会を開催しました!

マーベリック株式会社、技術広報のリチャード 伊真岡です。昨年12月に第一回を開催した社内ビジネス & 開発部門合同勉強会、その第二回を1月7日に開催しました。

techlog.mvrck.co.jp

この勉強会は現在100人を超え、様々な部署に分かれている弊社の中でビジネス側と開発側のお互いの理解を深めようという意図で始めています。まずは楽しくワイワイしながらお互いの仕事内容を紹介し合いましょう、という会です。

f:id:maverick-techblog:20200110120913j:plain

f:id:maverick-techblog:20200110120955j:plain

第一回に引き続き、ビジネス側のメンバー2人がホワイトボードとともに「顧客への広告キャンペーンの提案の流れの紹介」「今提案の現場で何が求められているのか」「弊社が提案をする際どこが作業効率のボトルネックになっているのか」「業界の各参加者の知識レベル向上に伴う変化」などの話題について紹介しました。

今回寄せられた感想の一部はこちら。

  • 実際の提案の流れに沿って話してもらったのがよかった
  • 規制強化などの厳しいニュースがある業界ですが、明るい希望がある話が聞けるの大変いいです!
  • 営業さんの実際の動きや流れはなんとなくしかわからなかったので、聞けたことはよかったです!!
  • 変にオブラートに包まず、ぶっちゃけていくスタイルは個人的には好きです
  • 営業さんの仕事の流れがイメージできたこと。あとプレゼンの引き込まれ感がすごかったです!
  • マーケットの話はもっと聞いてみたくなりました!体系的に学ぶことも必要ですが、どこから入っていっていいかがわからん状態です。
  • 営業だけでなく運用の仕方など各部門毎の仕事内容を詳しく知りたい

引き続きビジネス側、開発側力を合わせて楽しさを追い求めていきます!

社内でビジネス&開発部門 合同勉強会を開催しました!

マーベリック株式会社、技術広報のリチャード 伊真岡です。本日は弊社で開催されたビジネス&開発部門 合同勉強会の様子を紹介いたします。

f:id:maverick-techblog:20191226030523p:plain

弊社は現在100人を超えるメンバーがいます。創業当初はもちろん小さな会社だったので、部門を越えた連携は容易でした。しかし人数が増えるにつれて会社として様々な活動ができるようになった一方、社内の他のチーム全てで何が行われるかを把握するのは難しくなっていきました。

開発部門では30人超のメンバーがバックエンド、フロントエンド、インフラ、機械学習、QA、コーポレートエンジニアなどの様々なチームに分かれています。さらに管理部門があり、開発と管理部門を合わせると会社の半分弱の人数を占めます。残り半分強はビジネスサイドと呼ばれ、広告代理店らしく営業、トレーディングデスク、クリエイティブ、など様々なチームと職種が存在します。

私は技術広報として開発部門に所属していますが、正直弊社のビジネスサイドがどうやって売上を上げているのか、その構成比、業界のトレンドや弊社の立ち位置などまだまだ把握できていない部分が数多くあります。一方でビジネスサイドも「もっと開発サイドの知見を活かせれば面白い取り組みがたくさんできるはず」という思いがあり「それならまずは気軽な勉強会で、開発とビジネスお互いが理解できる場をつくりましょう」ということで勉強会を開催することになりました。楽しそうな試みを思いついたら、現場の人間たちで実行まで移せるのはとてもマーベリックらしいと思います。

今回の勉強会ではまず営業から2人の人間が、弊社全体での売上構成の大まかな紹介、それからとある営業チームの様子紹介と、彼らの日々直面している課題を紹介してくれました。開発サイドからは15人ほどが参加して、開発から営業の2人へ多くの質問が飛び交う活気のある会となりました。一部感想を紹介すると:

  • 自社の利益構成や広告業界話はおもしろかった
  • 自社プロダクトと代理店販売の違いが興味深かった
  • 今回の続きとして同内容の話をもっと深く聞きたいし、違うテーマでも面白いと思う

さらに次回以降の勉強会に向けて、開発サイドからビジネスサイドへ共有できる話として:

  • Webシステムの構築やデータベースのテーブル設計など
  • 色んな方法での自動化に関しては話ができるかと思う、なにより「何も考えず自動化するとかえって悪化する」ケースについては役立つ知見を提供できそう

といった点があげられました。「Webシステムとデータベース?」と思うかもしれませんが、今回の営業側から参加したうちの一人はPythonとPHPが使えるそうで、彼は純粋にITの技術に興味があるのです。私リチャードはPythonもPHPもどちらもできないので負けています(笑)

感想にもあったように開発者からも勉強会は好評で、ビジネス側の2人も交流を楽しんでいたので、次回以降の勉強会を鋭意計画中です。

ここで話があさっての方向に飛ぶのですが、先日私の友人から「ガイアの夜明け」というテレビ番組を見るように強く勧められました。一部視聴者層から熱狂的な支持を得る、テレビ東京の看板番組です。その中でも友人曰く「メロン農家親子の回」が最高の出来だから絶対見ろ、メロンの出来も番組の出来も最高だから必ず見ろ、とのことでした。その中で出てきた「にっぽんの宝物」グランプリを主催する羽根拓也氏は「日本の地方には隠された宝物がある。少子高齢化でそれを継げないケースもあり、もったいない。売れる成功事例を作りたい」と、地方の隠された「宝物」を発掘しコラボ商品の企画を仕掛けています。

何が言いたいかというと、友人に感化されてただガイアの夜明けの話がしたかっただけなのですが、弊社の属するインターネット広告業界は競争が厳しく、規制も目まぐるしく変わり「今までと同じことを続けるだけでは生き残ることもままならない」という点は同じだと思います。そして、もともと宝物といえるような光る部分を持った人と人が、コラボレーションによって新しい価値を生む可能性がある点も似ていると思います。

現在のITの世界は、技術が細分化・高度化しすぎて、とても一人でプロダクト開発で採用する技術を隅から隅まで知り尽くすのは無理になってきていると言えます。もちろん世の中には超人的な技術者がいて、複数の分野で第一人者としての知識と開発力があり、新しい技術分野もあっという間にマスターしてしまう人がいるのは知っています。しかし会社としてそういう技術者を雇うのは困難を極めますし、そもそも数が少なすぎて彼らに頼った組織は作れないでしょう。一方でひとつの技術分野、もしくは1.5分野くらいで第一人者となれる技術者は弊社にもたくさんいると私は感じています。それはビジネスサイドでも同じで、業界に深く精通していて顧客から高い評価を受ける人間が数多くいます。

先程述べたPythonとPHPができる営業の人間は「今、営業が困っていることを、ITの力を使えば解決できるという場合も、営業だけではその可能性に気づくことさえ出来ない場合もある」と言っていました。反対に開発サイドも「営業が困っていること」自体に気づいてない場合が多くあるでしょうし、それが「気づいていない」という理由だけで実現できないのだとしたら機会損失です。

今後弊社内の勉強会を通して、「にっぽんの宝物」グランプリのように、もともと実力のある異分野の専門家たちがコラボする取り組みができれば面白そうです。この記事を読んでいる人の中でITの世界に長くいるひとは、世の中に出回っているプロダクトやサービスで、当初海の物とも山の物ともつかなかったのに、後に大きく成長していった例を数多く見てきたでしょう。もちろんプロダクトやサービスという大きな成果につながるものばかりではないでしょうが、社内でのコラボから生まれた成果がいつかマーベリックの事業になんらかの形で繋がる未来を期待しています。

以上、半分以上ガイアの夜明けの話のブログでした。

もうひとつ、友人はNHKのプロフェッショナル 仕事の流儀「疾走、あんこ道~菓子職人・小幡寿康~」も勧めていましたので、興味があれば見てください。

Scala 3.0 enumの紹介 (前半) enumの利点と利用例

マーベリック株式会社、技術広報のリチャード 伊真岡です。今回はScala 3.0の主要機能の一つenumについて紹介します。

2019年12月現在Scalaの最新バージョンは2.13ですが、2020年中にScala 3.0のリリースが予定されています。正式リリースに向けてScala 3.0用コンパイラはDottyというプロジェクト名で開発されていて、多くのScala 3.0向け機能がすでにDottyから利用可能となっています。

そんなScala 3.0で導入されるenumは、Scalaの創始者であるEPFL(スイス連邦工科大学ローザンヌ校)のMartin Odersky教授が「Scala初心者に勧めたい3.0新機能ランキングの中で1位」と述べています。

enumを使いこなすことはScalaプログラマにとって、特にScala初心者にとって大きな力となるでしょう。enumに近い機能はScala 2.xでも利用可能でしたが、Scala 3.0のenumではより便利で簡潔な記述ができます。もしScala 3.0のenum「のみ」について知りたい方はDotty公式のenumのページを読んでください。この記事はenumの背景や利用例から説明するので少し回りくどくなっています。

また記事は前後半にわかれていて、前半である今回の記事はenumの利点や利用例を解説し、後日公開する後半ではenumの歴史を振り返り、なぜScala 3.0で新しくenumを実装する必要があったのかを説明します。Algebraic Data Typesに関する話題は後半の記事で触れます。

enumの利点

Scala 3.0のenumは昔からプログラミングの世界で利用されてた列挙体と呼ばれる概念を改めてScalaの中で実装したものです。列挙体は歴史あるプログラミング言語であるC言語やPascalでも古くから利用可能でした。多くの言語で利用可能な列挙体、そのScala 3.0実装であるenumはどんな場面で利用するのが効果的なのでしょうか?それは変数の取りうる値を制限したいときです。

例を挙げて説明してみましょう。いまWebアプリケーションの画面があってドロップダウンリストから属性を選び、その値をバックエンドであるScalaアプリケーション側で処理する機能を考えます。ドロップダウンリストから属性を選ぶので、属性は予め決められた少数の選択肢の中から選ぶことになります。つまりドロップダウンリストの選択肢以外は属性として不正な値です。

f:id:maverick-techblog:20191209044057p:plain

選択肢のデータをString型でこのように表現できます。

Webアプリケーション上での表示 Stringで表現したシステム内部での属性値
コート ”Coats”
ジャケット ”Jackets”
ニット(セーター) ”KnitWear”
シャツ ”Shirts”
パンツ ”Pants”

しかしString型を使ってしまうと、上記以外の不正なStringが属性値として間違って使われてしまう可能性があります。

// 属性をStringで表現すると、ありとあらゆる不正なStringが可能!
“”                               //空String
“Socks”                          //靴下は売っていない
“あqwせdrftgyふじこlp;” //全くの不正String

ソースコード上のあらゆる場所でこの属性を表すString変数があらわれるたびに「このデータは不正なStringになってないだろうか?」という心配がつきまといます。そのため例えば下記のisValid(shoppingCategory)のような 形で、属性値を表すStringであるshoppingCategoryが正しい属性値か不正な値かをチェックする必要があります。

def doSomething(shoppingCategory: String, ...): Result = {
  if(isValid(shoppingCategory))
    ... //Exceptionをthrowする?
    ... //あるいはEitherのLeftを戻り値として返す?
}

このdoSomethingはshoppingCategoryが不正な値であったときにExceptionをthrowするか、あるいはEitherのLeftを戻り地として返却する実装を持つとします。するとdoSomethingが呼ばれるすべての場所でtry-catchでExceptionをハンドルするかEitherのLeftをハンドルします。これは面倒ですし、ソースコードの見た目も読みづらくなります。

f:id:maverick-techblog:20191209044934p:plain

このようにあらゆる場所で不正な値のチェックを行うのは大変です。Web APIと外部の境界、データベースとScalaアプリケーションとの境界など、境界部分のみでStringのチェックを行い、それ以外の場所では不正なStringが入り込む余地を残さないのが理想的でしょう。しかしそういった理想的な状態のソースコードを保つことは難しく、いつしか不注意なソースコードの変更で不正なStringが混入する可能性があります。いったん不正なStringを混入させてしまったら、あらゆる場所でのチェックが必要になり、チェックを行った際エラー処理まで考えなくてはなりません。

こういった時にenumを使うとプログラムの安全性がたかまり、コンパイラの助けによって不正な値を型レベルで防いでくれます。以下のように選択可能な属性値を定義しておくと、ShoppingCategory型の変数は不正な値を取ることはありえません。

// Scala 3.0 または Dotty
enum ShoppingCategory {
  case Coats, Jackets, KnitWear, Shirts, Pants
}

Web APIからの入力やデータベースからの入力はScalaアプリケーションと外部の境界になるので、チェックを行いStringやIntなどの型からScalaのenumで表す型へと変換する必要があります。しかし、チェックが必要なごく一部の処理とその他大部分のShoppingCategory型に変換されたあとの安全な処理を明確に分けることができます。

f:id:maverick-techblog:20191209044948p:plain

またScalaのenumはパターンマッチと相性がよく、以下のように書くと網羅的に場合分けを記述できます。コンパイラがexhaustiveness checkを行って場合分けの漏れを防いでくれるので、enumを使ったパターンマッチ網羅性に起因するエラーを未然に防ぐことができます。

enum ShoppingCategory {
  case Coats, Jackets, KnitWear, Shirts, Pants
}

def doSomething(category: ShoppingCategory): Unit = category match {
  case ShoppingCategory.Coats => … 
  case ShoppingCategory.Jackets => …
  case ShoppingCategory.KnitWear => …
  case ShoppingCategory.Shirts => …
  case ShoppingCategory.Pants => …
}

パターンマッチ網羅性に起因するエラーを見るために、下記のようにShoppingCategoryにSocksを加えてみましょう。オンラインショッピングストアで新しく靴下(Socks)の取り扱いを始めた想定です。

// 最後にSocksを追加
enum ShoppingCategory {
  case Coats, Jackets, KnitWear, Shirts, Pants, Socks
}

//ここでコンパイルエラー!Socksのcaseが含まれていない
def doSomething(category: ShoppingCategory): Unit = category match {
  case ShoppingCategory.Coats => …
  case ShoppingCategory.Jackets => …
  case ShoppingCategory.KnitWear => …
  case ShoppingCategory.Shirts => …
  case ShoppingCategory.Pants => …
}

このとき、上記のコードはSocksに対するcaseが含まれていないので以下のようなコンパイラWarningが表示されます。コンパイラの設定によってこれをWarningからエラーに変え、より安全にできます。

// [warn] -- [E029] Pattern Match Exhaustivity Warning: 
// [warn] 12 |  def doSomething(category: ShoppingCategory): Unit = category match {
// [warn]    |                                                      ^^^^^^^^
// [warn]    |                                    match may not be exhaustive.
// [warn]    |
// [warn]    |                                    It would fail on pattern case: Socks
// [warn] one warning found

enumの利用例

その他のenumの利用例を見てみましょう。例えばenumで次のように曜日を定義できます。

enum Day {
 case Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
}

Java標準のDayOfWeekですでに曜日の定義があるのでありがたみは薄いかもしれませんが、何らかの理由でDayOfWeekへの依存を避けたい場合、例えば階層型アーキテクチャを採用していて、ドメイン層にはすべて自前の型を使うルールがあるといった場合には利用できます。

この際enumでは自動的にordinalという0から始まるInt値が付与されるので、「月曜日は火曜日の前」といった比較を行うことができます。

val l = List(Day.Sunday, Day.Friday, Day.Tuesday, Day.Wednesday, Day.Saturday, Day.Monday, Day.Thursday)
    
println(l)
//List(Sunday, Friday, Tuesday, Wednesday, Saturday, Monday, Thursday)

println(l.sortWith{(day1, day2) => day1.ordinal < day2.ordinal})
//List(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday)

さらにvaluesというメソッドも自動で付与され、当該のenumに属するすべての値をArrayにして取得できます。

Day.values.foreach(println) //Day.valuesはArray[Day]型
//Sunday
//Friday
//Monday
//Saturday
//Tuesday
//Wednesday
//Thursday

曜日だと7種類しかありませんが、もっと種類の多いもの、例えばHTTPのステータスコード200 Successや404 Not Found等をenumで表現できます。現在のScalaのWebフレームワークやライブラリではvalを多数並べて、ステータスコード自体はIntの値で表現している物が多いです。

実用上はvalとIntを使った定義で問題なく、不注意であったとしてもわざわざ不正なHTTPステータスコードのInt値を利用したコードを書く人は少ないでしょう。しかし、もしこれらのWebフレームワークやライブラリが誕生する前からScala 3.0が存在していたら、ステータスコードは型安全なenumを使って表現されていたかもしれません。

JSONのserialization, deserializationにおいてもenumを利用する機会は多いでしょう。Web APIを開発するとき、HTTPリクエストのボディにJSONを利用するとします。JSONのあるmemberが取りうる値を制限したいときjson-schemaであればこのようなschema定義をするでしょう。

{
  "type": "string",
  "enum": ["red", "amber", "green"]
}

Scala 3.0のenumを使えばScalaのコードで同様の制限を表現できます。Scalaで最もよく使われるJSONライブラリのひとつであるplay-jsonを使うとこう書けます。play-jsonの現行バージョンはまだDotty対応が完了していないので、下記のサンプルではimplicitを使っています。Dotty対応が完了したらgivenが使えるはずです。

enum Color {
  case Red, Amber, Green
}

object Color {
  //型レベルでこのパターンマッチがRed, Amber, Greenを網羅していると保証できない…
  def unapply(str: String): Option[Color] = str match {
    case "red" => Some(Red)
    case "amber" => Some(Amber)
    case "green" => Some(Green)
    case _ => None
  }
}

またunapplyメソッドのパターンマッチの右側がenumの値すべてを網羅しているか、コンパイラはチェックできないので、この記事の「enumの理論的側面」の項目で紹介しているようにテストコードと組み合わせて網羅性をチェックするとよいでしょう。

//play-jsonのDotty対応が完了すればimplicitではなくgivenを使えるはず
import scala.language.implicitConversions

implicit val readsColor: Reads[Color] = Reads[Color] {
  case JsString(str) => str match {
    case Color(color) => JsSuccess(color)
    case _ => JsError(str + " is not a valid color")
  }
  case json: JsValue => JsError(json.toString + " failed to convert to color")
}
case class Pen(
  owner: String,
  color: Color
)

implicit val reads: Reads[Pen] = (
  (JsPath \ "owner").read[String] and
  (JsPath \ "color").read[Color]
)(Pen.apply _)
val penJson = Json.obj("owner" -> "Alice", "color" -> "red")
println(penJson.validate[Pen])

こうして不正な値が入り込む可能性をWeb APIとその外部との境界部分に限定します。境界部分で上記のようなコードによってStringからenumへの変換を行ってしまえば、Web APIの内部では型安全なenumによって属性値を表現できます。

あるいはMySQLのようなリレーショナル・データベースではenum型をデータベース内で利用できます。

CREATE TABLE items (
    name VARCHAR(40),
    size ENUM('coats', 'jackets', 'knit_wear', 'shirts', 'pants')
);

このときScala側のenum ShoppingCategoryとデータベース側のenumの対応を付ければ、安全かつ自然な属性表現ができます。

(enumの利用例としてFinite State Machineの実装を紹介しようと思ったのですが、記事が長くなってきたので後半の記事で改めて紹介します。)

enumを利用しない方がよい例

enumは万能のツールではありません。enumを使うのが適切でない例として、利用可能な値が頻繁に追加・削除される用途が挙げられます。

例えばタスク管理ツールなどのラベルや、進捗管理ツールの進捗ステージ名などにenumを使うとしましょう。これらは、それぞれのツールの利用者が様々なラベル名、進捗ステージ名を自由につけて使うことが予想されるので、ツールの利用者全体では利用可能な値の数が膨大になりますし、高頻度でそのラベル名や進捗ステージ名が更新されます。

f:id:maverick-techblog:20191209221504p:plain

こういったときはenumによってソースコード内で利用可能な値を制限するのではなく、素直にデータベース内にラベルや進捗ステージの名前を保存し、高頻度な更新に備えるのが良いでしょう。

enumの理論的側面

「enumの利点」で述べたようにenumを使うメリットは、型レベルで不正な値を防ぐことによってプログラムの安全性が上がることです。英語の情報になりますが、Quoraでもその点に触れている質問があり

”Strings (or Ints to represent certain meaning) are prone to errors”

という解説がなされています。「変数やパラメタ、あるいは戻り値に不正な値が使われる可能性」はソースコード上においてエラーの主要な原因のひとつです。先に述べたようにソースコード上のあらゆる場所で不正な値のチェックをしなければならないとしたら、それは非常に頭を悩ます問題です。

この点をうまく理論的に説明している資料として、Scalaとは別の言語ですがElmのドキュメントがあります。不正な値を型レベルで防ぐという点について、身近な例をまじえつつ集合論や濃度といった抽象度の高い概念を用いて説明しています。関数型プログラミングが好きな人にはElmのドキュメントの該当部分をよむと思考が整理されスッキリとした気分になるかもしれません。

Elmにおけるプログラミングの中で最も重要なテクニックのひとつは、コード中で可能な値を現実世界での正当な値に完全に一致させることです。これにより不正なデータの入り込む余地がなくなるため、…

また、取りうる値の範囲が型によって決まっているというのは、テストの面でも有利です。型によって取りうる値が制限されているため、そもそもテストを書かなくてよい場面も出てくるでしょう。それでもテストを書く必要がある場合以下のような単純なfor comprehensionですべてのケースを網羅できます。

enum Day {
  case Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
}  

enum ShoppingCategory {
  case Coats, Jackets, KnitWear, Shirts, Pants
}
for {
  day <- Day.values
  category <- ShoppingCategory.values
} doTest(doSomeMethod(day, category))

まとめ

今回の記事ではScala 3.0のenumを題材に、列挙体という概念の基本に立ち戻って使い方を紹介してきました。冒頭で言及したようにScala 2系でも列挙体として使うことのできる機能はあったので、この記事で紹介したことの多くはScala 2でも違った方法によって達成可能です。

そこで後半の内容である次回の記事では、Scala 3.0 enumが誕生した背景やScala 2系までで利用可能だった類似の機能との比較などを紹介します。

参考文献:

clickhouse-odbcインストールでぶつかったgccバージョン固定の壁をSCL (Software Collection)で乗り越えた手順

マーベリック株式会社、インフラエンジニアの小川です。

特に古めの枯れたOSを使ったサーバ上で新しめのミドルウェアを使いたいと思った時、OS標準のパッケージやライブラリが古くてすんなり入らない…といったケースは多々起きると思います。弊社では以前、ClickHouseというOSSのデータベース導入を試みたときに上記の問題に遭遇しました。そこで、弊社でどうやってそれを乗り越えたかを、SCL(Software Collections)の使い方とともに紹介します。

1年で数億件以上のレコードが追加される予測

弊社でClickHouse導入を決めたのはある広告系のプロダクトでした。プロダクト開発の初期段階でデータベースの検討を行っていたとき、ひとつのレポーティング系テーブルのサイズが非常に大きくなることが懸念されました。過去の別プロダクトのデータなどをもとに見積もると、1年間の運用で数億件のレコードが追加されていくとの予測でした。もしビジネスが予想以上に拡大すればレコード数の成長速度も更に上がります。困ったことにレポーティング系のテーブルなので基本的にデータが減ることはなく、時間の経過とともにレコード数は増える一方です。レポーティングは毎回その巨大なテーブルに対しクエリを走らせねばなりません。

f:id:maverick-techblog:20191202121802p:plain

ClickHouse

そこで目をつけたのがClickHouseです。ClickHouseは以下のような特徴を持ち、ClickHouseのデータ構造に適したケースでは行指向なデータベースより高い性能を期待できます。

  • 列指向(カラムナ)データベース管理システム
  • OLAP処理向きのデータ分析基盤
  • オープンソース
  • SQLクエリを使用して分析データレポートをリアルタイムで生成できる

社内で行ったベンチマークでは、テーブルの構成や検索条件でばらつきがありましたが、クエリを走らせたとき1秒あたり数千万~1億以上のレコードをスキャンできました。これで十分実用に耐えうる速度がでたのでClickHouseを本番導入することが決定し、以下のような構成で当時の弊社のEC2環境、Amazon Linux 1イメージのインスタンス群上でアプリケーションの構築を目指しました。

f:id:maverick-techblog:20191202122112p:plain

clickhouse-odbc

さて、自社開発アプリケーションからClickHouseに接続するにはいくつかの方法があります。このアプリケーションはHaskellで書かれていたため、ODBCドライバを経由してClickHouseを利用することにしました。

  • アプリから ClickHouse を扱うための ODBC ドライバ
    • Haskell 製アプリ → unixODBC → clickhouse-odbc → ClickHouse
  • RHEL7/CentOS7/Amazon Linux 2 用のドライバは .rpm が提供されているため、特に困らない(はず)
  • RHEL6/CentOS6/Amazon Linux 1 用は .rpm パッケージが提供されていない

f:id:maverick-techblog:20191202122233p:plain

さてこれでClickHouseのデータベースとドライバがあれば動くはずでしたが…。

gcc バージョン問題にぶち当たる

GitHub からソースを取得して、Amazon Linux 1環境のサーバ上でビルドを試みるも失敗してしまいました。 https://github.com/ClickHouse/clickhouse-odbc#installing-prerequisites-linux

You'll need to have installed:

- Fresh C compiler, which understands -std=c++14
- Static libraries
    - static libgcc
    - static libstdc++
    - static libodbc
- cmake >= 3

Fresh C compiler, which understands -std=c++14

理由としては要求されるビルド環境の条件が厳しかったことです。特に gcc の「C++14」が大きな壁で、当時弊社の環境でのEC2インスタンスのOSはAmazon Linux 1で、gcc は「C++11」なので、ビルドが通りませんでした。

何ということでしょう

OSのバージョンをAmazon Linux 2に上げずに上げずに何とかできないものか…? と探していたとき

ありました

SCL (Software Collection)

https://www.softwarecollections.org/

SCLは現環境を壊さずに、新しめのミドルウェアが使えるものです。 (Node.js における nvm, Python における PyEnv などに使用感が近いです)

SCL の使い方

scl enable devtoolset-7 bash

→ bash が新たに起動し、ライブラリは SCL 提供のものを使うようになります

使ってみた on Amazon Linux 1

$ cat /etc/system-release
Amazon Linux AMI release 2018.03
  • 通常時の GCC バージョン
$ g++ --version
g++ (GCC) 4.8.5 20150623 (Red Hat 4.8.5-28)
Copyright (C) 2015 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  • SCL 切り替え後
$ scl enable devtoolset-7 bash
$ g++ --version
g++ (GCC) 7.3.1 20180303 (Red Hat 7.3.1-5)
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

これなら行けそうな気がしてきました!

clickhouse-odbc インストール手順

新しめの unixODBC をインストール

cd /opt/
wget http://www.unixodbc.org/unixODBC-2.3.7.tar.gz
tar zxvf unixODBC-2.3.7.tar.gz 
cd unixODBC-2.3.7
./configure && make && make install

Cmake, その他必要なものをインストール

yum -y install git cmake3 libtool-ltdl-devel --enablerepo=epel

CentOS SCL(Software Collections) インストール
元々のビルドツールやライブラリを壊すことなく、より新しいバージョンのツール/ライブラリを利用できる。 C++14 対応の gcc もこの中に含まれている。

cd /opt/
wget http://mirror.centos.org/centos/6/extras/x86_64/Packages/centos-release-scl-rh-2-4.el6.centos.noarch.rpm
wget http://mirror.centos.org/centos/6/extras/x86_64/Packages/centos-release-scl-7-4.el6.centos.noarch.rpm
wget http://vault.centos.org/6.7/SCL/x86_64/scl-utils/scl-utils-20120927-11.el6.centos.alt.x86_64.rpm
wget http://vault.centos.org/6.7/SCL/x86_64/scl-utils/scl-utils-build-20120927-11.el6.centos.alt.x86_64.rpm
yum -y install *.rpm
yum install -y devtoolset-7-gcc-c++ devtoolset-7-make devtoolset-7-build --enablerepo=epel
scl enable devtoolset-7 bash

clickhouse-odbc インストール

git clone --recursive https://github.com/yandex/clickhouse-odbc.git
cd clickhouse-odbc/
git checkout -b v0523 refs/tags/v1.0.0.20190523
mkdir -p build; cd build && cmake3 .. && make -j $(nproc || sysctl -n hw.ncpu || echo 4)
make install

無事ビルドが通れば、以下の場所に clickhouse-odbc の ODBC ドライバが出来上がる。

ls -la /usr/local/lib64/odbc/libclickhouseodbc.so 

ODBC への組み込み

ODBC で認識するように、ドライバの設定ファイルを用意する。 /etc/odbcinst.ini

[ClickHouse]
Driver=/usr/local/lib64/odbc/libclickhouseodbc.so

~/.odbc.ini

[ClickHouse]
Driver = /usr/local/lib64/odbc/libclickhouseodbc.so
url = https://localhost

動作確認

clickhouse-odbc ドライバが動作するか確認する。

isql -v ClickHouse

動作確認結果
成功すれば、ODBCのクライアントが起動する

# isql -v ClickHouse
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+
SQL> 

失敗の場合は、 Segment Fault 等のエラーが出る

動作確認完了
確認できたら、クライアントから抜ける。

SQL> quit

まとめ

SCL について

冒頭で述べたように、ミドルウェアをインストールする際にOS標準のパッケージやライブラリが古くてすんなり入らないといったケースは、長年使い続けている環境だと起こりがちです。本来は素直にOSのバージョンを上げて最新に近いものを使うべきでしょうが、あらゆるしがらみでできないことがよくあります。特に GCC に手を加えるのは…正直避けて通りたいものですよね。

そういった時、今回利用した SCL で元々のOSライブラリに影響なく、より新しいバージョンのツールが使える手立てがあるのは本当に助かりました。多用するのは気が引けますが、いざ! どうしても! という時の選択肢として活用できれば良いかなと思います。

ClickHouse について

また、 ClickHouse はまだまだ知名度が低い感じがあるものの、テーブル単位でレプリケーションが設定できたりと、噛むほど面白そうなカラムナDBです。まだまだ運用して日が浅いですが、今後弊社内でも活躍の場が増えそうな予感がしています。興味あるかたはぜひチェックしてみて下さい。

参考文献

セプテーニ・オリジナルさん主催でEventStormingワークショップを行いました。

マーベリック株式会社、技術広報のリチャード 伊真岡です。11月16日土曜日にセプテーニ・オリジナルさん主催でEvent Stormingワークショップを行いました。私はそこでファシリテーター役を務めたので、当日の様子を私から見てブログに記します。ディスカッション慣れしていた参加者の皆さんに助けられ、実りの多いワークショップになりました。

readeffectiveakka.connpass.com

Event Stormingとは複数人で行うワークショップの一形態です。英語の公式サイトに情報が載っていますし、日本語の情報だとyoskhdiaさんのブログ記事に解説が載っています。

f:id:maverick-techblog:20191120013742g:plain

このGIF画像のように、ふせんをたくさん使って議論と整理を進めていきます。段々とカラフルなふせんが増えて途中を仕切る線も加えられているのがわかるでしょうか?ふせんを並べ替えたり追加したり、ふせんの集まりを区切ったりしながら、皆で議論して業務手順の理解を深めます。

今回行ったワークショップはEvent Stormingの中でもBig Pictureワークショップと呼ばれる方式で、Big Pictureという名の通り特定のビジネスの全体、業務手順どうしのつながりに対して理解を深めるためのワークショップです。その他のにはUI(ユーザインターフェイス)デザインのためのEvent Stormingワークショップや、よりソースコードでの実装を意識したワークショップがEvent Storming公式サイトで紹介されています。

今回のワークショップの題材

ワークショップではより参加者になじみがあるビジネスであろう居酒屋を題材に、具体性をもって議論ができるように以下の設定をしました。

今回の分析対象: 客席数150人~200人規模の居酒屋

やや大きめの居酒屋です。居酒屋だとメニューの豊富さやコースの有無、予約やクーポン、団体客など程よく複雑性があり、分析しがいがありそうです。

さて今回は店の注文・配膳および会計など飲食店の業務全体を支えるシステムを導入することにしました。システムとしてはこちらが参考になるかもしれません。もちろん導入すべきシステムが私達の要望を全て満たしてくれるかわからないので業務分析を行ってシステム導入のための要求を洗い出しましょう。

店の規模:
カウンター席、テーブル席、大小様々な個室があります。全部で150人〜200
人収容できます。

厨房:
厨房と接客フロアは別れています。厨房の人員が直接注文をうけることはありません。

接客:
フロアの接客担当が注文を伝票に記入していました。会計時にはフロア担当が店入り口のレジで会計します。大人数の団体客には席での会計を受け付けていました。

システム導入の目的:
店舗での業務の効率が悪く、またミスが多く高い廃棄率にもつながっていたためシステムによって業務をサポートする判断がなされました。また外国人従業員の採用も考え、できるだけ簡単に業務が覚えられることが望ましいです。

ワークショップで行った分析を紹介

まずは全体の手順、来客から精算までの全体的な業務手順をあらいだし、だんだんと部分的な手順からより詳細な手順を洗い出していきました。それらの詳細な部分的手順の例の一つとして「団体客の1stドリンク受注」を切り出してみましょう。居酒屋で客が来店したあと、一番最初に全員分のドリンクを注文するあの手順です。「ユーザーストーリー」という言葉に馴染みがあれば、これはひとつのユーザーストーリと言えそうです。

写真だと少し見づらいので改めて描画ソフトで図にしたのがこちら。私達ワークショップ参加メンバー渾身の分析結果です。

f:id:maverick-techblog:20191120014320p:plain

最初は「ドリンク注文はメニューを客に渡して、注文を受け付けるくらいじゃないのか?」と思っていました。しかし詳細にストーリーを検討すると「飲み放題コースがランク別に分かれている可能性」「遅れてくる客がいてなかなか全員揃わない」「客がドリンク注文を決めるまでやたらに時間がかかる」「ドリンクが売り切れる」など様々な場合分けでの対応が必要なことがわかり、参加者みな団体客のドリンク注文処理の複雑さに改めて感じ入っていたようでした。

そしてこれは「初回のドリンク注文受付」のみであり、ドリンクを配膳するところは別のユーザーストーリーとなりますし、2回め以降のドリンク注文、食べ物の注文や調理、配膳もそれぞれ別のユーザーストーリーです。「居酒屋へのシステム導入」という設定でワークショップを行いましたが、現実の業務手順はソフトウェアで表現できるよりはるかに複雑で高度な処理を多数行っていることがわかりました。

f:id:maverick-techblog:20191120014442j:plain

参加者の感想

ワークショップの最後には参加者の感想を共有しました。一番皆が同意したのは「この複雑な処理を毎日何度も行う人は優秀」という人類に対する畏敬の念でした。他の感想は以下にあるとおりです。

  • ふせんのドメインイベント(公式サイトやyoskhidiaさんのブログ参照)には主語を書いたほうがわかりやすい
  • 登場人物をドメインイベントを考えると同時に貼っていかないと忘れそう
  • ふせんの整理が横軸(時系列)と縦軸(参加者のセンスに任される)だけだと難しい、もうひと軸欲しいくらい
  • 対象の業務領域の経験者がいるとドメインイベント洗い出しがはかどる
  • ワークショップの中でどこまで異常系・例外的なフローを洗い出すか
  • ドメイン・イベントは過去形の動詞を用いるというルールだがこだわらなくてもよい?
  • ドメイン・イベント主体で洗い出すと抜けもれが少なくなりそう
  • 物理のふせん紙を使うと検索ができない
  • 矢印を描きたい(今回はカベに直接ふせんを貼りました)
  • 事前に思っていたより細かい点まで分析できた、その共通認識がもてた
  • 2回目を明日やったらもっと上手に分析できそう
  • 今回は来客から精算までというスコープだったがその前後にも話が広がって収集がつかない部分もあった
  • 人数が増えると情報が増えすぎてワークショップ運営が難しくなりそう
  • 食べ物は必要、リラックスできる要因になった

Event Stormingというワークショップ形式自体がおもしろく、また参加者のみなさんが積極的に参加し鋭い指摘を次々にしてくだたおかげで様々な学びがありました。数多く挙げられた感想にそれが表れています。Event Stormingはいろいろな対象の業務分析に役立ちそうなので機会を見つけて活用していきたいです。

おまけ、ふせん紙の品質について 今回ドメイン・イベント用に使っていたふせん紙が何度もはがれおちてしまいました。ドメイン・イベントはEvent Stormingワークショップの中で最も数多く、100枚や200枚のふせん紙を使うこともめずらしくないでしょう。「最もよく使うものこそ高品質で使いやすいものを」という人生の教訓まで得られた1日でした。

参加してくださった皆さまありがとうございました!

Scala関西 Summit 2019で登壇しました!

マーベリック株式会社技術広報のリチャード 伊真岡です。

Scala関西 Summit 2019に参加してきました。2日間に渡って行われたこのカンファレンスですが、1日目の感想をブログにまとめます。公式Webサイトにあるように2トラックあった中で私は一日中ずっとトラック1の発表を聞き続けました。

2019.scala-kansai.org

トラック1ではClean Architectureに関連した発表が5本中3本ありました。弊社ではClean Architecture本やエリック・エヴァンズのドメイン駆動設計本の読書会を行っている最中ですので、実践をしている開発者たちからの知見がえられるのは嬉しかったです。

Dependent method types を利用した軽量Clean Architecture の紹介

最初のトークはがくぞさん。(スライド)

難しい抽象化の概念と捉えられがちなモナドにたよることなく、Tagless FinalやFree Monadなどで実現される利点をDependent method typeによって得ようという発表でした。

Scalaは高度な抽象化ができるからといって、モナドという理解のハードルが高いと思われがちな概念を取り入れるより、Javaに馴染みの深いエンジニアにもその延長線で理解できる技術を使うのはメリットがありそうです。

ソースコード全体に関わるかつ短期では変更しづらい方針なので、Tagless Finalを採用するかFree Monadにするか、Dependent method typeかは開発リーダーの腕が問われそうです。技術の取り入れやすさ、教育のしやすさ、メンテナンスコストなど様々なことを年単位で見通すという難しい判断になるでしょう。

Java 5.0時代の非同期処理技術から学び直すScala/Java非同期処理

次は私の発表。(スライド)

非同期処理はある程度時間をとって学ばないと何がなんだかわからない分野なので、いったんJava 5.0の時代までもどって、そこから現代の技術までたどると大きな流れが見えます。それが非同期処理理解の助けになりそうです、という発表でした。

Chatworkでリアクション機能をリリースした話

勝野さんの発表。(スライド)

Chatworkのリアクション機能楽しいですよね!今回はリアクション機能開発で行われていた性能要件分析とそれをもとにした技術選定、さらにベンチマーキングを行って詳細を検討する話でした。

特にベンチマーキングを行って「やってみないとわからないつまづき」(ベンチマーク環境でのAWSネットワーク構成の影響など)をひとつずつ潰して解決策を発見していく過程は、実践した人にしか提供できない価値ある情報だと思いました。

大きなシステムの改変を性能要件を見極めながら設計・開発していくのは非常に難しい一方楽しそうな仕事ですね。わたしもいつかそういう仕事をやってみたいと思える発表でした。

資産運用スタートアップの開発で採用した、PlayによるClean Arcitectureでの設計・開発事例

株式会社クラウドポートの若松さん(スライド)。資産運用サービスをたった3人のエンジニアで開発しているということにおどろいたのですが、少人数で効率良い開発を行うためにClean Architectureをどう活かしているかという発表でした。

Clean Architecture本に書いてあるようなメリットをうまく生かして少人数での開発を効率化している様子が伺える発表でした。

金融サービスの実装が複雑というのはそのとおりで、私が証券会社にいたときも感じていました。関連する規制・法律や業界慣習などが多岐にわたり複雑というのが一つの理由ですが、後のFolioさんの発表でも言及されますが金融関連のサービスは複雑になりがちです。

FOLIO のマイクロサービス in Action

最後はFolioのむらみんさんの発表(スライド)。むらみんさんの発表はスライドがきれいで発表慣れもしているのか話し方がわかりやすいです。

こちらもClean Architectureを絡めた話。Clean Architectureを使って層の分離をたもつことで、DDDの知識の蒸留のサイクルを回し続けるといった内容の前半と、マイクロサービス構成をFolioではどのように実現しているかという内容の後半に分かれていました。

Folio流の実践は工夫がたくさんあり、また発表からその工夫によって改善のサイクルがうまくまわっていることがうかがえる素敵な発表でした。

2日目はアンカンファレンスでした。アンカンファレンスの内容はこちらのブログには載せませんがツイッターのハッシュタグ #scala_ksでアンカンファレンスを含むScala 関西 Summitの様子がわかると思います。

楽しいカンファレンスでした!!