ytake blog

Web Application Developer

今年買って大変良かったもの 2022

今年買って良かったものです。
何かの参考にあれば幸いですが個人的なものも含みますので、
ふーん くらいに捉えていただければ。

書籍

事業分析・データ設計のためのモデル作成技術入門

読んだ感想を書こうと思ってすっかり忘れてましたが、
概念的な要素も多分に含むモデリングですが、
わかりやすい数学的な解説(集合論とかもありますよ!)と、
読み進めていくと体系的に学べるようになっている内容でおすすめです。

データ設計とタイトルにありまして、
データモデルを学ぶ上でも非常に有用ですが、
ソフトウェア設計にもマイクロサービスアーキテクチャ的にも
自分たちのドメインモデルを作っていく上でもどうやって考えていけばいいか、
がまさにここにある、という具合です。

めちゃくちゃ良い本なので、年末年始のお供にぜひ

継続的デリバリーのソフトウェア工学

気がついたら2022年12月に発売されていた書籍。
原語版でもかなり好評だったもので、日本語で読めます。

タイトルはCI/CDをイメージする方も多いかもしれませんが、
そのまんまの内容ではありません。
工学というキーワードを見聞きすると、アカデミックな難しいものと想像する方も多いと思いますが、
著者がソフトウェアにおける工学を定義して(誰でもわかる内容です)、
様々な事柄を明らかにしていく内容です。

学びのエキスパート、複雑さ管理エキスパートという言葉が最初に出てきますが、
読み進める度にそのための要素を探っていくのが読んでいて非常に楽しく、
これからものづくりをやっていくぞ!という方にも、
ソフトウェアの本質を知ってより良い設計に繋げていきたい!という方にも非常に良いのではないかと思います。

年末年始や輪読会にも

ちょうぜつソフトウェア設計入門

ひさてるさんの著書です。
サンプルコードはPHPですが、他の言語を使っている方にも読めますし、
サンプルコードがJavaの本と同じテイストなので、
PHPに偏見がある方にもぜひ読んで欲しい、というのはありますがw
書影はキャッチーですが、ソフトウェア設計について真正面から向き合っている本です。

ひさてるさんらしい わかりやすい文章でオブジェクト指向を掘っていき、
密接な関係がある開発サイクルも含めていろんなプラクティスが散りばめられており、
クラスベースの言語や関数ベースの言語などにも役に立つと思います。

もちろん読んだだけで何かが解決されて全てうまくいく、というわけでもなく
コピペしてコードが書けるようになるわけではありませんが、
中級者になっていくエンジニアの方には大きな助けにもなると思いますし、
知識の補完やエンジニアとしての思考方法を広げてくれるものでもあると思います。

ほんとに書影からは想像できないくらいの内容で、
キャッチーでありながらも、ひさてるさんの深くて広い知識と経験から出てくる文章が
非常に素晴らしいです。
ミノ駆動さんの本と合わせて横に置いておくと、いろんなものに対応できるようになると思います。

Domain Modeling Made Functional

発売してから4年くらい立っていますが、思い出して買った書籍

ドメイン駆動設計を体系的に学べて、かつ関数型言語のF#を使って表現していく内容ですが、
本の半分以上はどのようにドメイン駆動設計を進めていくか、という点に割かれていて
注文を受け取って請求書を送るシステムを例に、
イベントストーミングでドメインを分析していくのが重要としています。
データ構造よりもイベントやワークフローを軸にしていくので、
メッセージングを扱うリアクティブシステムに興味がある、ES+CQRSも!という方にも非常に良いと思います。
あとデータ基盤などを作る上で、イベントストーミングは避けて通れず、
何がエビデンスとして価値に繋がるのか、などの観点からも本書は有用だと思います。
表現としての型を重視しているのも読み進めていくと納得できるものですし、
関数型でなくても書籍を読んでいくとコンテキストが共有されるので、
自分たちの場合はどうしていくのがいいか、と考えられると思います。

もちろんコピペして他の言語に移植するだけではこの本の価値はあまりありません。
むしろそれはドメイン駆動でもなんでもないものですので、
翻訳をしながらでもきちんと読み進めていきましょう!
これを読んだ後にF#のドキュメントを読みまくって、ちょっと触ってみたりして
ぜひ導入してみたいなと思いつつ、Scalaだけでも一杯一杯なのでやめましたw
(Scalaが難しいという意味ではありません)

そのうちこれらの本について深掘りした記事でも書こうかな

楽器

IT界隈では楽器演奏する方も多いと思いますが、
今年買って良かったものが多くあったのでついでに

Roland V-Drums TD-27KV2

ドラム自体は結構長くやっていて、エンジニアになる前はドラムばっかりやっていまして。
自宅に生ドラムがあったんですが(1セットはスタフェスCTOに譲った)子供が興味を持ち始めたのもあり、
ちびっこに生ドラムは流石に音量もデカくて、大きさも大人サイズなので叩けないし、
ということで思い切って購入。

www.roland.com

楽器屋をやっていた時の知識のまんまだったので、
生のドラムと全然別物だしなぁ、と悩んでいて実際に店頭でも試奏させてもらったりして、
ハイハットが一枚で表現されてるし、スネアもなんかなぁ、と思っていたんですが
drumtecの動画をみたりして、購入をしたわけです。

www.youtube.com

セットアップしてみると、たしかに生ドラムのようにチューニングでパッと音が作れるとかではないですが、
結構細かくトリガーの設定もできるし、音も自分で組み合わせたりできて、
一番感動したのは重ねシンバルがあらかじめある!という点。
元々terry bozzioが大好きだったり、最近だとほとんどが重ねシンバルで構成してるbenny grebだったり、
そういうドラムスタイルに影響けていたりして、
20代の頃からシンバルの半分は重ねシンバルでセットしていたので、
普段と変わらず叩けるっていうのはいいですね。

www.youtube.com

youtu.be

あとスネアドラムもデジタルではありますけど、かなりよく再現されていて、
メッシュのヘッドもかなり満足度が高い。
生ドラムではなくこの手の電子ドラムから始める方は、
ベロシティなどを少し調整して、強めに叩かないとしっかり音が出ない、とかにすれば良いかもしれません。
太鼓を鳴らす感覚はもちろんないので、それは生ドラムで養えばいいくらいですね。
クリックももちろんあるし、コーチ的なモードもいいです。
もっと早く買えば良かったなぁっていうくらいのもの!

Ibanez QX527PB 7弦ギター

20代の最初のうちから7弦ギターをずっと触っていて、
いろんな事情で手放してからは6弦ギターだけ手元に残していたんですが、
やっぱりどうしても7弦ギターが欲しくなり、
個人的にはフロイドローズ系のトレモロで24フレットで、というこだわりがあったんですが、
最近はそうしたギターもあまり販売されていおらずという時代でもあるなかで、
strandbergが最近の楽器メーカーに影響を与えているのか、
いろんなところからヘッドレススタイルのギターが出ているわけですね。

結構いい値段するし、趣味ギターおじさんにはちょっともったいないなぁ、ということで
このQX527PBがスポット販売された時から気になっていて、
でてもすぐ売り切れという状態だったんですが、夏ぐらいから再生産されたタイミングで遂に購入!

www.ibanez.com

スラントフレット(人間の手首の傾きに合わせて斜めにフレットが打たれているもの)は非常に楽で、
ちょうどいい7弦ギターのネックの太さと、めちゃくちゃ軽量で、
薄さゆえにしっかり生音がする、というのも非常に気に入ってしまいました。

元々ついてるピックアップもまぁまぁ良いものではあるんですが、
リアは購入してすぐにSEYMOUR DUNCAN Pegasus Passiveに変えてしまいました。

https://www.soundhouse.co.jp/products/detail/item/184229/

手元にある6弦ギターには、ブリッジにDuncan DistortionとDuncan Customをのせたものが一本ずつありますが、
Pegasusはそれらよりも出力がちょっと低めで、
パワフルに歪むという系統ではないんですが、音の分離もよく、倍音がまた綺麗なやつでこれもまた満足。

ちなみにこのギターのピックアップ交換は非常に楽な仕組みになっているので、
自分で交換したことがある方は感動すると思います。

Ibanez PIA

Steve Vaiは高校生の頃からずっと聴いていて、いまだに憧れのギタリストなんですね。
そんなにたくさんは弾けませんがw(弾けたらエンジニアになってませんねw)

前のモデルのJEMの方が印象が強くて、当時買えれば良かったんですが、
シュッと購入できる状態ではなかったんですが、
上記にあるQX527PBを購入して、昔のようにいろんな曲を練習したり、
運指のトレーニングだったりをやっていくうちに、
憧れのギタリストのモデルが欲しいよね、という欲望と、
IbanezのEdgeトレモロの安定感が素晴らしく、手元にフロイドローズオリジナル搭載のFender ストラトがあるんですが、
24フレットも欲しいし、ということもあって遂に買ってしまった。

www.ibanez.com

フロイド系のトレモロは構造上胴鳴りはあまり期待できないんですが、
アルダーボディで日本製、しかもj.custom的なグレードのものでもあるためか、
予想外に鳴りがあってネックも鳴るんですが、この鳴りの一体感がとても素晴らしくて、
ついつい弾くのが楽しくなってしまうギター!

買って良かった!と思えるものですね。
これは素晴らしい!

Line6 Pod Goも実は買ったんですけど、これ一台あれば完璧ですね。
オーディオインターフェースに突っ込んでもいいし、ヘッドホンを繋いでシミュレーターを堪能してもいいし。
自分はPod Goをプリアンプにして(キャビネットのシミュレーターをオフにして、アンプをプリアンプにします)、
ギターアンプのリターンに突っ込んで使ったりもしていますが、
音がとにかく幅広いのでなんでもできます。
すごい時代だ・・!

来年はあまり買わないようにしよう・・

今年も大変だった 2022年振り返り

2022年は大変だった

年末でいつもの振り返り。

去年に引き続きブログ等のアウトプットもあまりしていなかったので、
振り返りとどんなことをしていたのか、 毎年の殴り書きです。

アウトプット

今年もアウトプットは意図的に抑えていました。
やはりコミュニティには新陳代謝は必要だと思うので、
いつものおじさんにならないようにCFPなどはほとんど出しませんでした。

登壇はPHPerKaigi 2022の「入門 境界づけられたコンテキスト」のみ

fortee.jp

トークイベントとしては
ミノ駆動さんと
ビジネスとアーキテクチャとアーキテクトなどのテーマを扱ったトークをしました。

flxy.jp

上記で内容はサマってあるものの、結構脂っこい話をしたりしました。
ここ数年はオフラインイベントが難しかったりしていますので、久しぶりに面白い話がたくさんできた・・。

もう一つは
あらたまさんと リアーキテクティングについて、これまでの取り組みの内容やどういった観点で判断していくか、
ビジネスはどうなっていくのか、などなどこちらも楽しいテーマでトークしました。

flxy.jp

もっと時間があれば、もっとたくさん具体的な話ができるのに!という所感ですが
これはこれでまた時期がきたらどっぷり肩まで浸かって話すみたいなことをしたいなと思いました。

スタフェスの現在進行形についてのリアーキテクチャ等については後述。

スターフェスティバル

メインでコミットしているスターフェスティバルでは、
エンジニアが増えてきて これからが面白そうな取り組みができる下地が少しずつ構築されてきた、という具合。
これまでエンジニアが多いような組織体系の会社ではなかったところに、
プロダクト開発体制に変わりながら一緒に未来を作るエンジニアが少しずつ増えてきて、
ついに今年はアドベントカレンダーが余裕で埋まってしまうという状態に。

qiita.com

みんな日常の開発等が忙しい中、アウトプットが好きな人たちが多く
投稿前にみんなでレビューしあったりして、大変素晴らしいエンジニア組織になってきたなぁという一年。

自分は引き続き将来の事業にフィードバックしていくための仕組みづくりや、
エンジニア外に対しての意識作りなどを継続して活動 & 実践をしたような気がする。
ドメイン駆動ということで、開発だけでなく会社全体に認知されるようにコミュニケーションを取ったり、
エンジニアとそれ以外、自分たちだけ、という関係性じゃないよ的なことも取り組んだりしてました。

去年インフラエンジニアが抜けたあとに、自分がしばらくやっていたところに
同じようなベクトルを持つエンジニアがジョイン! (koonagi)

インフラ周りはもちろんデータ基盤関係も一緒に取り組むことができて、
ひたすら地道に続けていたデータをとある形でプロダクトとして繋がりはじめて
これからどんどん面白いことができるのではないかなぁというのが見えてきたところ。

来年はsnowflake等を使いながらクラスタリングや機械学習などを使って、
価値あるものを作っていくぞ!と。

Webアプリケーション側はアドベントカレンダーにも書いたので、そこを参照してください。

blog.ytake.jp.net

マイクロサービスアーキテクチャはやる予定はなく、そっち方向に倒すつもりも現在ありませんが、
仕組み上どうしても分散トランザクションを扱うものがちらほら見え隠れしているため、
Scalar DB(KotlinかScalaと合わせる)を導入するか、Akka等でリアクティブな骨組みを作り、
その上でNode.jsやPHPのアプリケーションが構築される、みたいなのを準備していこうかなとかやんわり考えています。

github.com

基盤整理だったり業務フローに関係するものの再構築に関わっているチームは、
KafkaだったりCDCに徐々に慣れてきているところで、
イベントだったりメッセージングを実際に向き合っているので
来年等は面白いアウトプットがチームから出てくるのではないかと思ってます。

毎年やろうやろうと思っているフロントエンドアプリケーションはあまり手がつけられず、
AWS Amplifyで色々作ってみようと準備したところで年末になってしまった。
社内では開発をずっとしている、みたいな動きはしていないので
ytakeのコードとか実装をたくさん知れる!みたいな感じではありませんが、
お、面白そうじゃん?っていう方はぜひお話ししましょう!

stafes.notion.site

カジュアル面談は弊社エンジニアまで気軽に連絡してください!

副業

株式会社ネットプロテクションズ

以前から副業でお手伝いしていた株式会社ネットプロテクションズ(以下 np)のPRが出た!

prtimes.jp

npはネットで買い物をしている人なら大体見たことがあるのではないか、という
NP後払い等を展開している会社です。
技術とドメインを結びつけて次に進むためのお手伝いを継続的にさせていただいており、
ドメイン駆動・開発のサポートだったり、事業のところにも少しずつ入り込んだりしています。

システムのリプレイス、新たな言語選定だったりチームビルディングだったりを組織全体で取り組んでいるところで、
まだまだエンジニアが足りておらず、たくさんのボトムアップも必要な段階で絶賛エンジニア募集中なので、
ちょっと話聞きたいな!とかあればこちらも気軽にどうぞ!
今話題のKotlinやGo、Node.js(TS)だったりで色々やっています。
エンタープライズ的なアーキテクトになりたい!っていう方にも良いフェーズだと思います。

他にもいくつかの企業様でお手伝いを継続的にさせていただいてまして、
来年とかにもまた色々アウトプットが出てくるのでは、と思いますので
乞うご期待

プライベート

夏ぐらいから介護看護的な事象が発生したりして大変な中、
首がヘルニアになってしまって、しばらく動かないようにして寝ながら仕事したり、
勤務時間を少なくしたりして休んでいました。
ヘルニアからの神経痛と、ものすごい肩こりからくる神経痛の両方が併発してしまい
一ヶ月半くらいほとんどキーボードも打てない状態だったりしましたが、
12月に入ってからヘルニアが落ち着き、
ものすごい肩こりからの神経痛だけが残るというところまで回復してきたので
新年会等やりたいな!と思ってます。

そしてなぜか楽器熱が上がってきて(知っている方は知っているかもしれませんが、エンジニアになる前は音楽系だったりします)、
楽器が急に充実してしまった。
これはまた別のエントリとして書こうかなと思います。

来年はまた少し大きなアウトプットが出せそうなので
楽しみにしながら(大変な現実から目を背けている)、
引き続きビジネスに貢献を実践しながら同じような動きをしていくエンジニアを各所で育てて
圧倒的な成長に繋げるおじさんを継続していこうと思いますので、
関係者の皆様は引き続きよろしくお願いいたします。

現状と向き合ってシステムを考える時の頭の中

このエントリはスターフェスティバル株式会社の スターフェスティバル Advent Calendar 2022
16日目記事でもあります。

みなさんは開発する時にどう考えていますか?

大した内容ではありませんが、今回は開発をする上で
「どう考えて設計して表現していくか」、という永遠の悩みの中で
自分が複雑な物事に立ち向かう時の頭の中を少し書き出してみようと思います。
各カンファレンスなどで話しているものを結合したものではあります。

一緒に仕事をしたりしている方々にはお馴染みの話です

前半くらいは前提の話や分析の思考、
後半はイベント駆動などにおけるメッセージについて
という流れになってます。

ちなみに自身はスターフェスティバルではアプリケーション全般の開発には関わっていますが、
主にデータ基盤やデータドリブンなマインドを伝播させていくことや、
データを使った戦略を立てながらのプロダクト作りや、インフラ全般に携わっています。
(オフライン勉強会とかも少なくなったのでこういう話をする機会も減りましたねぇ・・)

細かいドメインモデルやデータモデル、設計手法の云々等については触れません(十分なボリュームすぎて・・)。

商品とは?

せっかくなのでスターフェスティバルで取り組んでいるテーマを例にしてみましょう。
*そのものずばりの内容ではなく、実際のものよりも単純にしていたり想像しやすい内容にしています

前提としてスターフェスティバルは下記の領域を取り組んでいる会社です。

企業理念
ごちそうで 人々を より 幸せに

飲食店が中食・デリバリーに参入するためのソリューションとして、
「製造」以外の部分にあたる「商品開発」「販路提供」「販売促進」「注文受付」「決済」
「配達」のすべてをスターフェスティバルが一気通貫でトータルサポートいたします。

また、最大級のモールを運営する弊社ならではの、製造・物流ネットワークや
販売チャネルを活用したサービスも行っています。

何らかのシステムを介して、おいしいごちそうを頼みたい!ユーザー(喫食者と呼んでいます)、
おいしいごちそうを提供したい製造元(以下製造パートナー)と
そしてそれを届ける配送が全国各地でリアルに動く、と大まかな関係があります。

では商品とは何を指すのでしょうか?

ドメイン知識などがない場合は、商品という言葉から
「8割くらいごちクルで販売している弁当とかのことを商品というんだな、
データベースにはこの商品があるだろう」
と思うはずです。

これは購入する喫食者の視点からみた時の認識であると同時に、
開発者としての視点での認識でもあります。

現状を理解せずに喫食者(らしい者)と開発者視点のままで開発をしていくと、
半分は飲食店のメニューとして並んでいるような商品で、
半分は商品の属性などが入り混じった商品という言葉から連想されるものが結合した何か、
データベース設計でいうならばポリモーフィック前提のような構造になってしまいます。

これを読んでいるみなさんの周りのアプリケーションにも似たような状態のものはたくさんあると思います。
アプリケーションがきちんと機能していればネガティブなものではありません。
多少見通しが悪いなどはありますが

これまではこれでも良かった。

が、物事は永遠に変わらないわけではなく、
色々と変化が必要なタイミングもあり業務フローとシステムを大きく見直す必要が出てきました。

取り組んでいる領域には「ごちそう」を軸に

  • 喫食者
  • 製造パートナー
  • 配送者

の3つがあることはわかっていましたが、本当にそれだけでしょうか?

実際は製造以外の分野を引き受けるため、
ごちそうを商品として登録する運用チーム、営業チームがあり、
このチームが製造パートナーの商品情報を受け取り、
サービスが提供する商品として成立するための情報を付与し、
はじめて喫食者が目にする「ごちそう」となります。

よく考えたら当たり前の構造ではありますが、
これまではよくても(雑にいうと)事業成長を支えていくためには現在の会社の状況を正しく理解し、
業務フローもスケールしやすくするためにはある程度ながく戦えるシステム・仕組み(例えです)を作らなければなりません。

事業を支えていくような基盤の場合は短い期間で大きなリプレイスが難しい場合もありますし、
サービスの特性や領域によっては簡単にリプレイスを繰り返すものもあります。
皆さんの環境や領域ではまた変わってきますので、皆さんの環境に置き換えて考えてみてください!
(これはあくまで例です)

業務フローとシステム(みなさんが想像しやすいアプリケーションと思っていただいて良いです)は
密接な関係があり、「運用でカバー」がそのまま謎の進化を遂げたり、
とりあえず今の要求通りで動けばいいやというシステムは、
やがてスケールできない業務フローや事業戦略が立てづらい企業と繋がっていきます。
(そのために継続的なリファクタリングや、変化に対応しやすく未来を一緒に作っていけるアジャイルなどを取り入れたり)

現状をより理解する

では長く戦うために考えることは、
マイクロサービスアーキテクチャ
でしょうか?

いやいや、そうではなくて現在の業務フローや構造などを再度見つめなおしてみます。

「ごちそう」と表現される商品の大元は、
弁当などを製造する店舗・製造パートナーがありそこで認識される商品は、
あくまで製造側が認識できる「xx弁当」という商品になります。

たとえば製造側は上記のような認識だとしましょう。

製造パートナーが認識できる商品に対してスターフェスティバルで必要な情報が付与されます。
配送に関連したものであったりさまざまですが、
店舗が認識している商品とはまた少し違った形となります。

同様に配送する場合に認識する商品もそれぞれ似たところもあるけど異なるもの、
となります。

(もちろん実際とは異なります)

つまり商品は3者の認識が混入しているっぽい、と当たりがつくようになります。
これは開発者だけではなく社内の各方面の方々とコミュニケーションをとりながら、
実際に言葉の齟齬がないか背景や事実確認などを行いながら現状を深掘りします。
社内で共通の言葉を使いながら、領域ごとに多少の背景を汲み取りながら
それぞれの領域で疎通ができるようになればはっきりと境界線が認識できます。
(これは実際に自分でやっています。)

これを認識せずに実際のコードやシステム設計に落とし込むと
データモデルとも大きく乖離してしまうデータベース設計(フラグを多用したり、ライブラリで使いやすい構造になってしまったり)や、
商品という名前を使った巨大ななにかに繋がるコードとなってしまいます。
これはクラスベースの言語でなくても、型演算多用で何者にでもなれてしまう何かになってしまったり
どういう指標で作っていくかはすべて実装者依存になってしまいます。

これでは10年戦えるシステム・仕組みどころか、
実装者が変わった途端に「なぜこう作ったんだろう?」のみが残ってしまい、
開発者の好みなどの粒度でさらに分割したり結合したりとどんどん複雑になってしまいますので、
みなさんも「あれ?」という瞬間があったらぜひ現状の分析をしてみてください。

起点を探す

さて、この記事はここからが本題です。

この境界線はそれぞれの立場や業務などの背景があるのは明確ですが、
それはどのような瞬間から分かれていくのでしょうか。

それは下記のものがキーとなります。

製造以外の分野を引き受けるため、  
ごちそうを商品として登録する運用チーム、営業チームがあり、  
このチームが製造パートナーの商品情報を受け取り、  
サービスが提供する商品として成立するための情報を付与し、  
はじめて喫食者が目にする「ごちそう」となります。  

つまり一番最初の事象は、
製造パートナーからシステムを通じて販売したい商品データをもらう・投入してもらう
という事象を起点に商品に対する認識が少しずつ変化します。

そして商品に対するさまざまな情報が加えられたりすることで「ごちそう」となるわけです。
雑に整理すると下記のようになるでしょう。

  • 製造パートナーが販売したい商品情報を入稿した
  • スターフェスティバルがサービスで販売するための商品として承認した
  • 商品が「ごちそう」へと生まれ変わった

(多くなるのでこの辺で・・)

もちろんこの事柄以外にもありますが、価値として大きなものはこのようになります。
ではなぜこの事象を取り上げたのでしょうか?

実はこの事象はWebアプリケーション開発の視野と異なる視点でみると
少し違う見方ができます。

これらの事象は製造パートナーとして販売したい商品がどのくらいあり、   販売できなかった商品がどのくらいあったのか、
「ごちそう」と変化した商品がどのくらいあったのか
という事業としても製造パートナーとのやりとりの改善や、
他にもさまざまなエビデンスとなり活用できる価値のあるデータへとつながります。

そして物事の始まりでもある「製造パートナーが販売したい商品情報を入稿した」事象をきっかけに
いろいろな世界へと繋がることが見えてきます。
人によってはまるで小さなビックバンを見つけたかのような感覚にもなるかもしれません。
自分はよく
「今ここでジャンプしたら地球の裏側にはどういうことが起こるかワクワクしながら想像するようなもの」と喩えたりしています。

そしてこの例の事象1つ1つは会社活動やサービス、「ごちそう」など、
それぞれの視点だけで完結するピュアな事象に近いということがわかってきます。
各領域のアプリケーション中ではもっとたくさんの事象がありますが、
会社活動としても重要な事象はどれか、という思考で分類します。

たとえば事象の起点から最終的に「ごちそう」へ変化するまでの道のりを俯瞰することで
物事を把握できるデータ・エビデンスとしても活用できる、
かつ社内外の業務フローからみても、この事象はピュアなまま活用できます。

あくまでたとえばの話ですので、みなさんのアプリケーションや会社活動の中ではどうなのか、
と向き合ってみるとまた違うものが見えてくると思います。
(時系列があることはしっかりと認識しておきましょう)

やがてはこの事象が「あの時どうだったっけ?」という真のデータソースとなり得るもので、
それぞれの立場・領域でこれを元に世界が広がっていく拡張されていくデータの流れとしても捉えることができます。

事象が保管されることでそれぞれの領域に閉じた構造を作ることができるようになります。
お互いを行き来する場合でも相関IDや真のデータソースを介することで
翻訳しあえることも見えてきます。

この事象を更新などで変化をさせずに的確に保管し、
読み込み時にそれぞれの領域の視点を取り入れた構造にすることもできそうです。
(複製しても再度やり直せば良いということにもなります。)

もちろん1つのDBですべてが完結していて
そこで分析やさまざまアプリケーションが動いていて問題がなければ難しくする必要はありません!
あくまで捉え方の例としてください。

どういう仕組みで作っていくか

ここでやっと実装の話に。

事象が発生したタイミングで複数の領域の視点や関心が加わっていくということはわかっています。
この事象が発生したよ、と複数の領域に伝播・指示できれば良さそうです。

もし領域ごとにアプリケーションが存在しているとするとどう取り組んでいくと良いでしょうか?
この場合は社内業務用のアプリケーションに社外用のデータ投入アプリケーション、
そして販売サイトとして作用するアプリケーションとなりますのでアプリケーションが分かれていることになります。
*注意 マイクロサービスアーキテクチャではありません。

気をつけなければならないのは、
複数の領域があるためHTTPを挟んだりデータベースへ直接書き込みにいったり、
データ処理のためにBigQueryやS3へ一緒に書き込むぞ!
という選択肢をとると難しい問題と立ち向かわなければなりません。

ここまで述べた中でいくつかの領域があるため、
たとえばシステムが分かれていた場合は3領域に対してデータベースに書き込んだり、
HTTPを挟んで書き込んだりする場合(Web APIなど)は、
どこかの領域で失敗した場合、どこで整合性が担保されるのかという問題があります。

リトライを挟んだとしてどこで3つの領域に書き込まれたよ、と担保できるのでしょうか。

なにかの障害などでどこかの領域に指示ができない場合はどうしていくのでしょうか?

それぞれの領域で構成変更などが起きた・起きる場合は同時に対応しなければならなくなりそうです。

分かれているシステムへ直接データベース書き込みにいくと、
リリースやデータ構造の変更が発生する場合は一緒に行動する必要が出てきます。
加えてどれかの領域の関心が入り込んだテーブルやカラムなどが追加される可能性が高くなっていきます。
どこかで必要だけどどこかで必要ない、という形になっていきます。

このアプローチでは2相コミットが発生するのと、
相手が必ず生きていることを前提とした同期処理ということになってしまいます。

ja.wikipedia.org

つまり指示元が相手先を考慮しなければならなくなります。
お互いに仕様変更がある場合なども大変そうなことも見えてきます。

難しい場合はマイクロサービスアーキテクチャだ!分離だ!と意気込まずに、
まずは結合したアプリケーションとして着手していきましょう。

この例の場合で、各領域に影響を与えず、かつそれぞれの領域独自の視点を入れてもよい、と考えると
メッセージブローカーなどを介するのが良さそうです。

結果整合を選択することになりますが、この例の場合は問題ないという前提にしておきましょう。

事象が起きたことを記憶したい領域がメッセージブローカーに伝えることができれば
指示を受ける側に何かあっても気にする必要はなく、
各領域のタイミングで自由に受け取ることができます。

これにはSQSやMSK、Kinesisなどがありますが
弊社ではMSK(Kafka)を利用しています。
SQSなどのQueueとMSK、Kinesisとの違いなどはまた別の機会にするとして、
ここではKafkaを選択したとしましょう。

*受け取る領域が途中で増えたとしても過去の事象を再送することができるので、再送のために再度指示を投げる必要もありません

メッセージをどうするか?

メッセージブローカーに送信するにはメッセージが必要となります。

メッセージはIDだけを記述して受信側が再度問い合わせる、
という形にすると「あの時どうだったっけ?」ができているつもりでも、
「あの時」が更新された最新のものになってしまうことになります。
必ず事象をそのままスナップショットにしましょう。

肝心のメッセージの送信方法ですが、大まかに2つの方法があります。
1つ目はoutboxパターンを用いてメッセージブローカーに送信、
2つ目はメッセージブローカーに直接送信があります。
(Akka等はこの記事では触れません)

www.infoq.com

outboxパターンを用いる場合は、データベースのトランザクションを使って
送信したいメッセージをテーブルに書き込み、CDC(change data capture)を介してメッセージブローカーに送信されます。

この場合はoutboxとして利用するテーブル以外にも、
送信元のアプリケーションで必要なデータベースに書き込む必要がある場合などにも利用できます。
レガシーアプリケーション改善などにも活用できます。

注意点としてはCDCの対象はテーブル単位となりますので、
複数テーブルの変更をバラバラで受け取ることになりますのですべてを同時に受け取ることは難しくなりますので、
その場合は1つのメッセージで完結するような集約を作る必要があります。
AWSのDynamoDB Streamsなどで用いられている仕組みのようなもの、と捉えてもらえると良いと思います。
WAL(Write Ahead Log)で変更などを検知して吸い上げてメッセージとして送信されるものです。

Debeziumでoutboxパターンをサポートしてくれる機能がありますので、
興味のある方は下記をどうぞ。

debezium.io

すでにスターフェスティバルのアドベントカレンダーに一部がありますので、
こちらも合わせてどうぞ!

zenn.dev

これについてはまたどこかで解説しましょう。

メッセージブローカーに直接送信する場合は、
送信元のアプリケーションでリアルタイムにデータベースへ保存する必要がなく、
メッセージブローカーに送信さえできれば良いという場合に利用します。

この場合は上記にも記述しましたがメッセージブローカーに送ってデータベースにも書き込んで、
といった実装になる場合は2相コミットが発生しますのでなるべく選択することを避けてください。

データベースなどへの書き込みなどはコンシューマーとなるアプリケーションが担当、
もしくはKafka Connectなどを通じて転送して保存できます。

RDBMSやS3、BigQueryやさまざまなデータソースに転送したり、
もちろん前述のCDCとKafka Connectを組み合わせることができます。

なおMSK、Kinesisなどは送信時にトランザクションを利用できますので、確実に届けることができます。

1つの領域で閉じたメッセージ送信などの場合は、アプリケーション側で担保できますが、
本記事の例では複数の領域やデータ処理などにも繋がっていくため、いったんメッセージブローカーを介していきましょう。
(いろんな言語で解説するのが大変なのでGoの例にします)

メッセージを作る

すべての例を記載していくのは大変なので、下記のものだけをやっていきましょう。

  • 製造パートナーが販売したい商品情報を入稿した

特別な知識は排除した簡単な例ですが、おそらく次の構造になるでしょう。

{
  "correlation_id": "11111122223444",
  "store_name": "ytakeキッチン",
  "store_id": 2,
  "product_name": "めちゃくちゃおいしいカレー",
  "wholesale_price": 100,
  "price_including_tax": 108,
  "tax_rate": 1.08,
  "comment": "めちゃくちゃおいしい",
  "type": "new",
  "created_at":"2022-12-12T00:00:00+09:00"
}

だいぶ省略していますが税率が複数ある場合はこのままでは対応できませんので、税率を含めて構造化等を。
データベースに直接突っ込むものではありませんので、
実際に永続化するときは商品と価格などは分けておきましょう。

package message

import "time"

type EventType string

const (
    New    EventType = "new"
    UPDATE EventType = "update"
)

// Product 商品に関するメッセージを表現したもの
type Product struct {
    // CorrelationID 相関ID
    CorrelationID string `json:"correlation_id"`
    // StoreName 製造パートナー名
    StoreName string `json:"store_name"`
    // StoreID 製造パートナーID
    StoreID int `json:"store_id"`
    // ProductName 商品名
    ProductName string `json:"product_name"`
    // WholesalePrice 卸値 税抜
    WholesalePrice int `json:"wholesale_price"`
    // PriceIncludingTax 卸値 税込
    PriceIncludingTax int `json:"price_including_tax"`
    // TaxRate 税率
    TaxRate float64 `json:"tax_rate"`
    // Comment 何かあれば
    Comment string `json:"comment"`
    // Type データ入稿が新規か更新かなどなど
    Type EventType `json:"type"`
    // CreatedAt 事象の発生日時
    CreatedAt time.Time `json:"created_at"`
}

良さそうに見えますね。

待ってください?!

たとえばデータ投入で少し変わってきた場合にどうやって構造が変更されたことを担保するのでしょうか?
型指定があまり得意ではない(型指定しているように見えてもそれはあくまでコード上だけだったりも然り)場合はどうやったら?

という場合には
Apache AvroProtocol Buffersを利用するといいでしょう。

これらを使うことで各アプリケーションやデータ処理などで定義を共有できます。
下記はProtocol Buffersの例です。

syntax = "proto3";

package product;
import "google/protobuf/timestamp.proto";

option java_package = "com.github.ytake.example.protobuf";
option go_package = "github.com/ytake/example/pb";

message RegistrationAction {
  uint64 correlationId = 1;
  string storeName = 2;
  uint32 storeId = 3;
  string productName = 4;
  uint32 wholesalePrice = 5;
  uint32 priceIncludingTax = 6;
  float taxRate = 7;
  string comment = 8;
  enum EventType {
    NEW = 0;
    UPDATED = 1;
  }
  EventType event = 9;
  google.protobuf.Timestamp created = 10;
}

言語に対応したoptionなどもありますので必要に応じて足します。
AWS DMSやDebeziumなどでoutboxとしてこれらを利用する場合は下記を参照してみるといいでしょう。

medium.com

さてとこれで。。

おっと、まだ考慮しておくことがあります。
これまで述べた長い背景の中で事象には順序があるらしい、とわかっています。
たとえばこの事象ごとにQueueやTopicを分けて送信してしまうと、
送った側は時系列順に送ったつもりでも受け取り側はそうなるとは限りません。

どこかでスタックしてしまうと、時系列は想定通りにはならず簡単に順番が入れ替わってしまいます。
スーパーのレジを想像してみてください。
並んでる人が少ない列で待っていると、「あれ、あっちの方が多かったはずなのにもう捌けてる・・」
あの現象が起こります。
事象の分析や時系列を理解してメッセージ設計に含めましょう。

今回の例であれば最終的には付加情報をつけて「ごちそう」になるわけですから、
前の状態がなくとも突然「ごちそう」のメッセージが流れてきても問題ないかどうか、
時系列の判断などが可能かどうか、スナップショットと不整合が起きないかどうか、
結果整合となりますのでそちらで担保が可能で、
頭から読み出して再処理しても問題ないかどうか、などなどがあります。

上記のprotoファイルに、たとえば付加情報を含めてスナップショットとする、などなど
Protocol BuffersのOptional なども理解しておきましょう。

このあたりの設計について触れるとどんどん長くなってしまうので、今回は割愛します。
機会があればこれはこれでどこかで・・・

protoファイルを使ってGoのコードを吐き出してあげます。

# Goのプロジェクトルートから実行して./pbに吐き出すとしたら
$  protoc --go_out=./pb --go_opt=paths=source_relative ./product.proto

簡単なサンプルですが、下記のようになります。

import (
    "errors"
    "github.com/confluentinc/confluent-kafka-go/kafka"
    "github.com/ytake/example/pb"
    "golang.org/x/xerrors"
    "google.golang.org/protobuf/proto"
    "google.golang.org/protobuf/types/known/timestamppb"
    "strconv"
)

// 抜粋
    ra := pb.RegistrationAction{
        CorrelationID:     11111122223444,
        StoreName:         "ytakeキッチン",
        StoreId:           2,
        ProductName:       "めちゃくちゃおいしいカレー",
        WholesalePrice:    100,
        PriceIncludingTax: 108,
        TaxRate: 1.08,
        Comment:           "めちゃくちゃおいしい",
        Event:             pb.RegistrationAction_NEW,
        Created:           timestamppb.Now(),
    }
    tn := "topic-name"
    by, err := proto.Marshal(&ra)
    if err != nil {
        return xerrors.Errorf("error: %w", ErrProtobufMarshal)
    }
    deliveryChan := make(chan kafka.Event)
    err = p.Produce(&kafka.Message{
        TopicPartition: kafka.TopicPartition{
            Topic:     &tn,
            Partition: kafka.PartitionAny,
        },
        Value: by,
        Key:   []byte(strconv.Itoa(int(ra.CorrelationID))), // パーティションKey
        Headers:        []kafka.Header{{Key: "適切なヘッダーKey", Value: []byte("header values are binary")}},
    }, deliveryChan)
// 以下略
// close等していませんのでこれをコピペしても動きませんので注意

これで良さそうです。

いや、まだあります。

メッセージの定義は時の流れとともに変更されていきます。
開発していくなかで要件が変わったり、設計見直しなどがありますのでずっとこのままというわけにはいきません。

そんな時にオススメなのはSchema Registryです。

docs.confluent.io

AWSではGlue Schema Registryがあります。
(GCPは利用していないのでわかりませんがCloud Pub/Subとか?)

もちろんすべての変更に完璧に応えるというわけではなく、
どのように互換性を担保するかを指定しなければなりません。
互換性チェックのタイプとしては大まかに下記の通りです。

  • 後方互換性
  • 前方互換性
  • 完全互換性
  • 互換性チェックなし

コンシューマーから先にアップグレードするかプロデューサーから先か、など
ユースケースによって選ぶことができます。
詳細を解説すると本1冊分くらいになりますので、下記を参照してください。

docs.confluent.io

互換性の指示についてはSchema RegistryのREST APIを利用するか、
クライアントで指定できます。
Schema Registryを介してKafkaへ送信する場合は下記のように利用できます。

// 抜粋
    p, err := kafka.NewProducer(&kafka.ConfigMap{"bootstrap.servers": "bootstrapServers"})
    if err != nil {
        return err
    }
    client, err := schemaregistry.NewClient(schemaregistry.NewConfig(url))
    if err != nil {
        return xerrors.Errorf("Failed to create schema registry client: %w", ErrSchemaRegistryClient)
    }

    /* subjectを指定して互換性を変更する場合は下記のように利用できます
    *
    *_, err = client.UpdateCompatibility("subject名", schemaregistry.Forward)
    *if err != nil {
    *  return xerrors.Errorf("Failed to change compatibility: %w", ErrSchemaRegistryCompatibility)
    *}
    */

    ser, err := protobuf.NewSerializer(client, serde.ValueSerde, protobuf.NewSerializerConfig())

    ra := pb.RegistrationAction{
        CorrelationID:     11111122223444,
        StoreName:         "ytakeキッチン",
        StoreId:           2,
        ProductName:       "めちゃくちゃおいしいカレー",
        WholesalePrice:    100,
        PriceIncludingTax: 108,
        TaxRate: 1.08,
        Comment:           "めちゃくちゃおいしい",
        Event:             pb.RegistrationAction_NEW,
        Created:           timestamppb.Now(),
    }
    tn := "topic-name"
    payload, err := ser.Serialize(tn, &ra)
    if err != nil {
        return xerrors.Errorf("error: %w", ErrProtobufMarshal)
    }
    deliveryChan := make(chan kafka.Event)
    err = p.Produce(&kafka.Message{
        TopicPartition: kafka.TopicPartition{
            Topic:     &tn,
            Partition: kafka.PartitionAny,
        },
        Value:   payload,
        Key:     []byte(strconv.Itoa(int(ra.CorrelationID))),
        Headers: []kafka.Header{{Key: "適切なヘッダーKey", Value: []byte("header values are binary")}},
    }, deliveryChan)
    // 以下略

Consumerも同様に実装できます。
長くなるので下記のサンプルなどを参考にしてください。

github.com

データ処理系はメッセージが格納されたTopicに対して
Kafka Connectを利用してS3やさまざまなデータストアに対して転送できます。
下記のconverterを利用して変換を行う形になります。

Kafka Connect Protobuf Converter | Confluent Hub

docs.confluent.io

と、ボリュームがえらいことになってきたので、駆け足的にここまでとなりますが、
どのように物事と向き合って実現するための方法など
両方が結びつきあうことで開発以外の領域に対しても広く視野が持てるようにもなります。

実際にはここまで複雑につくらずとも同期的な処理を利用した実装や
物理的にデータベースを共有しながらアカウントや権限で分離するなど、
シンプルに解決できる方法もありますので、
色々な角度で物事を見ながら抽斗をたくさん作っていきましょう。

採用情報

スターフェスティバルではエンジニアを絶賛採用中です。
気になる方は是非以下のページなりを見ながらインフラやデータ基盤の話、
アプリケーション開発周りの話などを聞きたい方がいましたら、カジュアルお話ししましょう!
弊社のTwitterやエンジニアにDMなどお気軽に!

stafes.notion.site

PHPerKaigi 2022でトークしました #phperkaigi

PHPerKaigi2022!

オンラインな時代になり、カンファレンスへの参加がオンラインになってしまったり
色々ありますが1年ぶりくらいにカンファレンスに登壇(録画)しました。

この2年くらいで子供が大きなったり色々ありました(当たり前)
現地参加ができなかったのが悔やまれますが、
今回は下記の 「入門 境界づけられたコンテキスト」 についてのトークでした

speakerdeck.com

ただし録画の音質が悪く、多分ノイズキャンセルあたりがかかってしまったのではないかと思います・・
大変失礼しました。

コンテキストの話ということで、資料だけを公開するとコンテキストをすっ飛ばしてしまうので
公開しないつもりでしたが、音質があれだと、ということで。

内容について補足など

今回はコンテキストの話ですが、
これは日常生活のあちこちにあり、コンテキストを意識せずに、
自分達都合で解釈して進めてしまうと物事のいろんなところに歪みが生まれる、という例を挙げながら
どうすれば防げたのか、どう考えればよかったのか、という思考の話でした。

コミュニケーションに取り入れることで、物事が円滑になったり、
認識の齟齬を減らすということができるわけですが、
プログラミングにおいてはどういう繋がりがあるのか、というところを交えての内容としました。

聞く人によってはリアルすぎる状況の話だったりするので、
胃が痛くなる人やドキドキしてしまう方も多くいたのかもしれません。

何気ないコミュニケーションや思考の裏側にはエンジニアとして
どのように実装しなければいけないか、あのライブラリはこうだから
こう考えないといけないからこう言っておこう、という思考を混ぜ込みすぎると
歪みが出てきたり、あの人とこの人が同じようなことを言っているので、それを混ぜて共通化しよう、
となるとどんどん目の前のものが複雑になっていきます。

同じ言葉でどのような範囲において変わっていくのか、を意識することは非常に重要で、
それを理解することで、
あの人とこの人が同じようなことを言っているけど、実は違っていてこの機能はいらないですよね、
指しているものはこうだから多分これは別ですよねー、というようなやりとりに変わっていくわけです。

第一声にそれは無理っすというより、やはりいろんなものが前を向いていくシーンは多くなると思います。

そして設計をする時もこの領域を意識して落とし込んだり、
どこまで実装で表現するかというフェーズになっていきます。

ここで注意しなければならないのは、現実ではこうだからクラス設計も全く同じにしないと!という
現実のものをそっくりそのまま射影しすぎないように気をつけましょう!

現実のものを同じように表現するとそれはそれで難しくなりますし、
実装するのがどんどん難しくなっていきます。
(OOPの幻想みたいな話になっていきますね)

ですが、エンジニア都合の物事の考え方だけでなく、
いろんな俯瞰した思考から ここが落とし所だ! と見えてくるようになります(試行錯誤の繰り返しですが)
そして宣言的なプログラミングへと変わっていくきっかけにもなります。

そしてこの言葉をうまく捉えるには文字だけではなくて、
利害関係者とのコミュニケーションと、抽象のハシゴによる思考と、メタ情報を見つける思考が非常に大切です。
これらを組み合わせることで境界づけられたコンテキストを見つけていく、
そんな行動のための話だと思ってもらえると良いと思います!
(達人プログラマーなどの書籍にも似たような話がたくさんあります)

最近発売になった書籍にもこうした行動の解説があったりします。

モジュールやアーキテクチャ特性などを明らかにする際の思考は
こうした境界づけられたコンテキストによる分離スタイルによるものとなります。

が、 そのまんまクラス設計やアーキテクチャに落とし込む、という話ではありません。
繰り返しになりますが、現実のものをそのまま表現するには難しすぎますし、
エンジニアとしての視点もある程度混入させてなければなりません。

ただコンテキストを理解しなければ、なぜそうしたのか、が結びつきません。
そのため資料の中の実装例にすっ飛ばしていかないように実装の話を全く入れていません。
バランス感覚と思考の話ですし・・
概念だから、と片付けられる話でもあるかもしれませんが、
HOWを重視するのではなくてWHYを的確なものにしなければなりません。

blog.magnolia.tech

magnoliaさんの記事なども参考に見てみるといいでしょう!

とはいえこの境界づけられたコンテキストとうまく付き合うことで、
マイクロサービスアーキテクチャなどの切り出す範囲を決定できるかなと思います。
やる時は実際にどれとどれは同じか、というのを関係者と一緒に作業したりしています。
もちろんやったら終わりではなくてビジネスの変化だったり時間で変わることも多々ありますので、
都度変更していく、という具合です。

実装についてはまたどこかの機会に・・!

是非感想などのフィードバックをください!
音質はすいませんでした・・

アンカンファレンス

アンカンファレンスでは、
境界づけられたコンテキストから広がる世界を実装するためにも組み合わせることが多いドメインイベントから、
イベントソーシングに流れるにあたってやってしまいがちではあるけどやってしまうとデータ連携だったり、
サービス間でのやり取りで不整合が発生して
意図しない分散トランザクションを生んでしまったり、時系列がぐっちゃぐちゃになってしまう、
イベントがソースにならなくなるアンチパターンについてお話ししました。

こちらは以前にラクスさんのモデリングのLTの時にお話しした内容をピックアップしたものです。
実践する時に興味があれば是非覗いていただければと思います。

speakerdeck.com

知人が出版した

感想はまた別の機会に書きますが、
前職で一緒だったりコミュニティ活動等で一緒になることがおおいメンバーによるLaravelの書籍が発売に!
自分の本とは違って入門に優しいしっかりした内容になっております。
是非どうぞ!

Kafka+Spark Structured Streamingとマイクロサービスアーキテクチャ

Kafka+Spark Structured StreamingとProtocol Buffers

マイクロサービスアーキテクチャというと、
適切なドメインモデルを参考にシステムを切り出して、
小さくしてチームを分けてAPI開発すればいいんだな、と思う方も多いかもしれませんが、
実はこのまま鵜呑みにしてやってしまうと、事業をうまく支えられるかどうか厳しい面が多くあります。

ビジネスを進める上でアプリケーションが動いていることは当たり前ですが、
事業を拡大したり推進していくには、エビデンスとなるデータが必要不可欠です。

つまりAPI連携をしてうまく動いてるように見えても、
あくまでアプリケーションレベルでの動きだけで、
マーケティングやさまざまなエビデンスになるデータを取得することは困難である
という状況になってしまうことが多々あります。

愚直にこれに対応しようとなると、数多くのバッチ処理、
バッチ処理の依存を解決するための仕組みづくりや、
深夜を超えて処理されるバッチ処理についての問題解決などなど、
実は最初に目をつぶって後回しにして、数年後ににっちもさっちもどうにもいかなくなる、
というのがデータ処理関連では非常によくある問題であったりします。
そもそもグローバルになると深夜という概念がなくなりますので、
なんでも日本時間の夜中に定期実行させる、というのが難しくなりますね。

事業をうまく支えるには マイクロサービスアーキテクチャというキーワードが注目されて数年経ちますが、
我々が目指すのはマイクロサービスアーキテクチャではなくて、
リアクティブシステムでなくてはいけません。
*リアクティブシステムはリアクティブ宣言(Reactive Manifesto)で定義されていますので、
詳しくはそちらを参照ください。

www.reactivemanifesto.org

リアクティブシステムを目指していけば、
自然とマイクロサービスアーキテクチャになっていくというのがなんとなくわかるかと思います。
マイクロサービスアーキテクチャが主で考えてしまうと、
GraphQLなどのBFF的な解決策やAPIそのものについての思考が優先されてしまうため、
リアクティブシステムの文脈はあまり見えてこないかと思います。

ということで、今回はリアクティブシステムを支えることができる
メッセージブローカーとしても強力なApache Kafkaを中心に、
Spark Structured Streamingと、アプリケーション関連で利用ケースも多いProtocol Buffersを扱う例の解説です。

なんでメッセージブローカー?

そもそもマイクロサービスアーキテクチャ等でKafkaのようなメッセージブローカーを使え、
と多くの実践者が発信するのはREST APIやGraphQLなどで通信を行う場合、
副作用のないReadを扱うには非常に強力で、むしろこれ以外の選択を取るのは難しいわけですが、
副作用のあるPOST、PUT、PATCH、DELETEのようなものへのアプローチであったりします。

単一のアプリケーション観点でみるとこれらはただのHTTPメソッドだったりするかもしれませんが、
システム全体でドメインモデルなどと照らし合わせてみると、
多くがドメインイベントだったり(イベントストーミングなどで導き出したものなど)がほとんどのはずです。
つまりWebアプリケーションの裏にあるデータの流れ(ストリーム)の観点では、
ビジネスを推進して行ったり、エビデンスとしていく信頼できるデータは
このドメインイベントのスナップショットを活用していくことであったりします。

もちろん永続されたRDBMS上のデータもそうですが、
ほとんどは副作用があるデータとRDBMSやS3やその他たくさんのデータを組み合わせなければなりません。

ここの解決策に、例えばAPIにPOSTリクエストを送った後に、
分析とか機械学習とか、マーケティング等に必要なデータを他のストレージに入れていきますね、
という処理ばかりを作っていくと全て同期的に処理しなければならなくなりますし、
なにしろn層コミット問題をAPI側で作り込まねばなりません。
データ連携がうまくいかないのを検知してSlackなどで通知して手動対応、もあるかもしれませんが、
アプリケーションが使われれば使われるほど手動対応がかなりの割合を占めていきます。
これではマイクロサービスアーキテクチャ的な側面だけを導入しても、
数年後にはうまく機能しないシステムと評価されてしまうでしょう。(それもひとつの経験ですが!)

この状態の場合は、DebeziumやAWSのDMSなどを使ってCDCを導入して解決していくしか無くなってしまうわけですが、
これにしても結局Apache KafkaやAmazon Kinesisといった
大規模な分散処理に対応したメッセージブローカーを活用することとなります。 (Akkaなどで解決するのももちろんあります)

今回はタイトルにもある通り、Apache Kafkaです。
AWS上ではMSKとして提供されていますし、オンプレでもクラウドでもConfluentなどで構築することができます。

kafka.apache.org

www.confluent.io

KafkaとProtocol Buffers

アプリケーション間でやり取りをするにあたって、すべてのシーンで型の制限がないJSONを使い続けるのはやはり難しいです。
ということでProtocol Buffersを利用することも多くあるでしょう。

developers.google.com

今回は例として、単純なユーザー作成に関するものを扱うものを使うとします。

syntax = "proto3";

package sample;
import "google/protobuf/timestamp.proto";

option java_package = "com.github.acme.sample.combine";
option go_package = "github.com/acme/sample/pbd";

message UserAction {
  uint64 correlationId = 1;
  enum EventType {
    CREATED = 0;
    DELETED = 1;
  }
  EventType event = 2;
  uint32 userId = 3;
  string name = 4;
  google.protobuf.Timestamp created = 5;
}

副作用が大事といいつつUPDATEがないのはただの例だからです。

ただのprotoファイルですがマイクロサービスアーキテクチャだったり、データ処理観点が混ざっているので
APIなどのWebに近いアプリケーションはGo、データ処理はSpark+Scalaでやるという体です。

Apache Kafkaとの組み合わせでよく質問されますが、
Protocol Buffersはただのバイナリとして扱われますので、Kafkaでも問題なく扱えます。

Goでは下記のようにして扱えます。

protoファイルで定義した通りに詰め込みます。

package message

import (
    pbd "github.com/acme/sample/publisher/pbdef"
    "google.golang.org/protobuf/types/known/timestamppb"
    "math/rand"
    "time"
)

func makeTimestampForProto() *timestamppb.Timestamp {
    return timestamppb.Now()
}

func ExampleMessages() []*pbd.UserAction {
    var sua []*pbd.UserAction
    return append(sua, &pbd.UserAction{
        UserId:  uint32(1),
        Event:   pbd.UserAction_CREATED,
        Name:    "aaa1",
        Created: makeTimestampForProto(),
    }, &pbd.UserAction{
        UserId:  uint32(2),
        Event:   pbd.UserAction_CREATED,
        Name:    "aaa2",
        Created: makeTimestampForProto(),
    })
}

Kafkaへの送信はこんな感じです。

(複数のtopicへのメッセージをうまく扱うにはインターフェースなどにしておくといいでしょう)

package message

import "github.com/confluentinc/confluent-kafka-go/kafka"

// Publisher interface
type Publisher interface {
    Client() *kafka.Producer
    RetrieveTopic() *string
    RetrievePartition() int32
}
package pub

import (
    "fmt"
    "github.com/confluentinc/confluent-kafka-go/kafka"
    "github.com/acme/sample/publisher/message"
)

// Client Kafka client struct
type Client struct {
    Producer *kafka.Producer
}

type Messenger struct {
    publisher message.Publisher
}

// RequestParameter for publisher
type RequestParameter struct {
    Byte []byte
    Key  []byte
}

// NewProducer create producer
func NewProducer(broker string) (*Client, error) {
    p, err := kafka.NewProducer(&kafka.ConfigMap{
        "bootstrap.servers":        broker,
        "api.version.request":      "false",
        "message.timeout.ms":       "300000",
        "socket.timeout.ms":        "30000",
        "message.send.max.retries": "5",
    })
    return &Client{Producer: p}, err
}

// Publish to kafka bootstrap server
func (c *Messenger) Publish(parameter RequestParameter) error {
    deliveryChan := make(chan kafka.Event)
    km := &kafka.Message{
        TopicPartition: kafka.TopicPartition{
            Topic:     c.publisher.RetrieveTopic(),
            Partition: c.publisher.RetrievePartition(),
        },
        Value: parameter.Byte,
    }
    if len(parameter.Key) != 0 {
        km.Key = parameter.Key
    }
    err := c.publisher.Client().Produce(km, deliveryChan)
    if err != nil {
        return err
    }
    e := <-deliveryChan
    m := e.(*kafka.Message)
    if m.TopicPartition.Error != nil {
        fmt.Printf("failed to deliver message: %v\n",
            m.TopicPartition)
    } else {
        fmt.Printf("delivered to topic %s [%d] at offset %v\n",
            *m.TopicPartition.Topic,
            m.TopicPartition.Partition,
            m.TopicPartition.Offset)
    }
    return nil
}

// SampleTopicClient for no key
type SampleTopicClient struct {
    kafka     *Client
    topic     *string
}

// NewSampleTopicClient client for no key example
func NewSampleTopicClient(topic string, c *Client) *Messenger {
    return &Messenger{publisher: &SampleTopicClient{kafka: c, topic: &topic}}
}

func (c SampleTopicClient) Client() *kafka.Producer {
    return c.kafka.Producer
}

func (c SampleTopicClient) RetrieveTopic() *string {
    return c.topic
}

func (c SampleTopicClient) RetrievePartition() int32 {
    return kafka.PartitionAny
}

このような感じで送信すると、Kafkaにはvalueはただのバイナリとして保管されます。
アプリケーション連携でAPI連携で使いたくなりますが、
これをドメインイベント、メッセージとして連携していきます。
Kafkaを使うのでサブスクライブするコンシューマは、
protoファイルを使って必要な値に変換するだけとなりますので、特に変わったことはありません。

Kafkaのtopicはgroup.idが分かれていれば並行処理をいくつも増やせることができますし、
partitionを分割することでround robin的に負荷分散をすることもできます。 Kafka Connectをそのまま使えば何も実装せずにS3やRDBMS、Elasticsearchや他のデータストレージへ保管できますので、
前述のCDC的なこともドメインイベント、メッセージを主としてできます。
(DynamoDB StreamsやDMSの場合は書き込んだタイミングでメッセージ送信になります。)

ECのカートのようなものへのアプローチとしてはまだ足りませんが、
だいたいのアプリケーションの副作用的な動作はこれでイベントを正として保管しつつ(イベントソーシングとしても)、
後続の処理は非同期で、リアクティブシステムの要素を取り込んで独立してきます。
CQRSだったりに繋がっていく話ですね。

ではデータ処理観点ではどうでしょう。

ScalaPB

データ処理観点ではメッセージブローカーに到達した時に、
何かのデータと組み合わせて拡張したようなデータソースをつくることはよくあります。
ウインドウ集計などもそうです。

マイクロサービスアーキテクチャなどで分割されたアプリケーションであっても、
メッセージブローカーでドメインイベントがスナップショット的に送信されていれば
こうしたことは容易です。
最初の頃にやってしまいちなのは、ドメインイベント送信だ!といいながら中身は
パラメータ化されていて、受け取った側はそれをもとに副問合せしてほしい、というパターン。
これにしてしまうと、他の処理が先に動いていたり、partition分割で他のコンシューマが処理していたりすると、
後続の処理全体がファントムリードやダーティリード的な状態に陥りやすくなりますので、
やらないように注意してください!コマンドバス的に信号だけを送るようなアプローチは場合はまた別ですが。

さてさて、ScalaではScalaPBを使ってProtocol Buffersを扱うことが多くあります。

scalapb.github.io

project/scalapb.sbtなどを作って下記のものを記述します。

addSbtPlugin("com.thesamet" % "sbt-protoc" % "1.0.3")
libraryDependencies += "com.thesamet.scalapb" %% "compilerplugin" % "0.11.3"

build.sbt にも同様に追記していくといいでしょう。
sparkと組み合わせるには以下のような具合です。

libraryDependencies ++= Seq(
  "org.scala-lang" % "scala-library" % "2.12.10",
  "org.apache.spark" %% "spark-core" % "3.0.3" % "provided",
  "org.apache.spark" %% "spark-sql" % "3.0.3" % "provided",
  "org.apache.spark" %% "spark-sql-kafka-0-10" % "3.0.3",
  "com.thesamet.scalapb" %% "sparksql-scalapb" % "0.11.0",
  "com.thesamet.scalapb" %% "scalapb-runtime" % scalapb.compiler.Version.scalapbVersion % "protobuf",
  "junit" % "junit" % "4.13" % Test,
  "org.scalatest" %% "scalatest" % "3.2.7" % Test
)

assembly / assemblyShadeRules := Seq(
  ShadeRule.rename("com.google.protobuf.**" -> "shadeproto.@1").inAll,
  ShadeRule.rename("scala.collection.compat.**" -> "scalacompat.@1").inAll
)

Compile / PB.targets := Seq(
  scalapb.gen() -> (Compile / sourceManaged).value / "scalapb"
)
Compile / PB.protoSources += file("../protobuf")

ここで注意なんですが、最新のSpark 3.2.0だとScalaPB自体で対応が追いついていないものがあるので、
うまく動かなくなりますので、2022/1/2現在では 3.0.3 を使うようにしておきましょう。

これで、protoファイルに記述した option java_package = "com.github.acme.sample.combine"; がパッケージとして
そのまま利用できます。

Kafka+Spark Structured Streaming(with ScalaPB)

spark.apache.org

簡単なStream処理だけであればKafka Streamsだけでもできますが、
他のデータをくっつけて集計したり、ほかのストレージに書き出したりは
Apache Sparkに任せた方がより多くのことができます。

SparkSessionは特に変わったものはありません・・。

import org.apache.spark.sql.SparkSession
import org.apache.spark.{SparkConf, SparkContext}

/**
 * @param appName
 * @param checkPoint
 */
class SparkApplication(appName: String, checkPoint: String) {

  protected def context(): SparkContext = {
    val conf = new SparkConf().setAppName(appName)
    conf.set("spark.sql.session.timeZone", "Asia/Tokyo")
    new SparkContext(conf)
  }

  def createSession(): SparkSession = {
    val spark = context()
    spark.setCheckpointDir(checkPoint)
    spark.setLogLevel("WARN")
    SparkSession.builder
      .appName(appName)
      .getOrCreate
  }
}

ここではKafkaを接続してStructured StreamingでSelectだけする例です。
Spark Streamingでもできますが、
Spark SQL形式で記述できたりするのでこちらから入っていく方が簡単かなと思います。

Kafkaへの接続はreadStreamでformatにKafkaを指定するだけです。
DataFrameになりますので扱いも簡単に!

import org.apache.spark.sql.{DataFrame, SparkSession}

object KafkaDataFrame {

  def make(ss: SparkSession, bootstrapServers: String, topic: String): DataFrame = {
    ss.readStream
      .format("kafka")
      .option("kafka.bootstrap.servers", bootstrapServers)
      .option("subscribe", topic)
      .option("startingOffsets", "earliest")
      .load()
  }
}

SparkSessionを生成して、Kafkaと接続してストリーム処理はこれだけで始められます。

import com.github.acme.sample.combine.definition.UserAction
import org.apache.spark.sql.functions._
import org.apache.spark.sql.types.{IntegerType, LongType, StringType, TimestampType}
import scalapb.spark.Implicits._
import scalapb.spark.ProtoSQL

import java.util.Properties

object StructuredStreamingRunner extends App {

  if (args.length < 1) {
    throw new IllegalArgumentException(
      "This program takes one argument: the path to an environment configuration file.")
  }
  val ss = new SparkApplication(getClass.getName, "/tmp/streaming_runner").createSession()

  val prop = new Properties
  prop.load(new java.io.FileInputStream(args(0)))
  val df = KafkaDataFrame.make(ss, "127.0.0.1:9092とかkafkaのbootstrap.servers", "topicの名前")
  // 省略
}

簡単!
このままの場合は、Spark内でのスキーマは

    root
    |-- key: binary (nullable = true)
    |-- value: binary (nullable = true)
    |-- topic: string (nullable = true)
    |-- partition: integer (nullable = true)
    |-- offset: long (nullable = true)
    |-- timestamp: timestamp (nullable = true)
    |-- timestampType: integer (nullable = true)

という定義になります。
valueがProtocol Buffersでバイナリになったものです。
これを取り出すためにScalaPBを介して分解します。 UDF (User Defined Function) を介してバイナリをStructに変換できます。
UDF自体は使い勝手がいいわけですが、Sparkは分散処理が基本なので多用すると
パフォーマンス劣化につながるので注意してください。
JSONなどはStructTypeでschemaを定義して
org.apache.spark.sql.functionsfrom_jsonなどを使うだけです。

val parseUserAction = ProtoSQL.udf { bytes: Array[Byte] => UserAction.parseFrom(bytes) }

このudfを適用すると、下記の通りprotoファイルで記述した通りの定義になります。

 |-- value: struct (nullable = false)
 |    |-- correlationId: long (nullable = true)
 |    |-- event: string (nullable = true)
 |    |-- userId: integer (nullable = true)
 |    |-- name: string (nullable = true)
 |    |-- created: struct (nullable = true)
 |    |    |-- seconds: long (nullable = true)
 |    |    |-- nanos: integer (nullable = true)

このままでも value.event のようにアクセスすることもできますが、他のDataFrameとJOINなどをする時にちょっと面倒です。
わかりやすく最適化して、value自体は削除してもいいでしょう。

  df
    .select(col("key"), col("value"))
    .withColumn("key", col("key").cast(StringType))
    .withColumn("value", parseUserAction(col("value")))
    .withColumn("correlation_id", col("value.correlationId").cast(LongType))
    .withColumn("event", col("value.event").cast(StringType))
    .withColumn("user_id", col("value.userId").cast(IntegerType))
    .withColumn("name", col("value.name").cast(StringType))
    .withColumn("created", col("value.created.seconds").cast(LongType))
    .withColumn("created_timestamp", col("value.created.seconds").cast(TimestampType))
    // ウインドウ集計したい場合などは ウォーターマークを付与するなどしましょう
    // .withWatermark("created_timestamp", "10 minutes")
    .drop("value")
    .createTempView("events")

例えば上記のようにすると、Spark SQL内ではeventsがテーブル名となりますので、
慣れ親しんだSQLで記述が自由にできるようになります。
keyはKafkaでpartitionが複数ある場合の判定などにも使うもので、この例ではkeyとvalueをDataFrameから取り出して、
そのDataFrameを基準にカラムなどの定義をする処理になっています。
必要なカラムを直感的に操作できるように定義した結果は下記のとおりです。

 |-- key: string (nullable = true)
 |-- correlation_id: long (nullable = true)
 |-- event: string (nullable = true)
 |-- user_id: integer (nullable = true)
 |-- name: string (nullable = true)
 |-- created: long (nullable = true)
 |-- created_timestamp: timestamp (nullable = true)

これがeventsテーブルとなるので、いかようにも分析なり、データ結合なりウインドウ集計などができます。
最後に下記を追加することで、指定したKafkaのトピックにデータが受信されると、
サブスクライブの処理的な感覚でマイクロバッチ的にストリーム処理されていくようになります。

  ss
    .sql("SELECT * FROM events")
    .writeStream
    .format("console")
    .start()
    .awaitTermination()

ドメインイベントなどをメッセージとしてアプリケーションに適用することで、
Protocol Buffersを使ったリアクティブシステム・マイクロサービスアーキテクチャ的なアプリケーションに対しても
データ観点で重要なスナップショットを活用してさまざまなビジネスや、もちろん他アプリケーションへ繋げることができます。

手法ばかりに注目していると、こうしたデータ処理やその先のビジネス的な戦略など、
裏側の世界にたいして有用でないものにもなり得ますので、
是非メッセージ・イベント駆動などに触れてみていただけるといいかなと思います。

Apache Kafka or Amazon Kinesis + Apache Sparkの組み合わせはまだしらばく定番かと思いますので、
これをきっかけに取り組んでもらえると仲間が増えて嬉しいです!

多様な働き方で忙しかった2021年振り返り

2021年は忙しかった

年末でいつもの振り返り。
今年は多忙につき年末のいつものアドベントカレンダー参加は見送っていたのと、
ブログ等のアウトプットもあまりしていなかったので、振り返りと、どんなことをしていたのか、
ということの殴り書きです。

スターフェスティバル

本業でメインでコミットしているスターフェスティバルの総括はCTOのsotarokのエントリを!

note.com

この中で自分はどういうことをしていたのかというと、
前半は致命的な問題を抱えているシステムの改善などを主にやってました。
もちろんそれは技術分野だけではなく、
業務フローやアプリケーション起因のオペレーション、データ設計も含めて全体的にここに問題があるぞ、
というのを明らかにする活動もやりつつ、非エンジニアの方々と率先してコミュニケーションしながら、
信頼を得るための活動だったり、とにかくドメインエキスパートになるためにかなり振り切った活動をメインに。
入社して半年過ぎくらいで大体の改善ポイントだったり、
改善のための長期ロードマップを自分の中で確定させて大体の行動指針をまとめて、
周りに少しずつ共有していく、ということを日常的に回して実際に着手を開始していました。

組織は当然生き物なのでその時で変わったりでいろいろやることが増えてきたなぁというのが後半。
CTOのエントリにもあるプリンシパルエンジニアという役割がついたのもこのくらいの時期だったかも・・。

後半はデータ領域の改善やインフラ領域も持ちながら、根深い問題を抱えているアプリケーションの刷新・移行に着手開始!
内外に向けたDXだったり、全社の意識を変えていくというパワーを使うことを徐々にメイン。
DRE的な働きはここ数年ずっと注力してやっていたり、Webアプリケーションは長くやっていて、
加えてプロダクト開発的な視点や、経営関連(他社でCTOやってた)もこれまで携わっていたのもあり、
エンジニアによっては全くエンジニアの動きに見えないところも多々あったとは思うんですが、
全社的に良い環境、良い文化に変えていかなければエンジニアが良いモノづくりをできない、というのは当たり前なので、
当たり前のことを当たり前にやっていました。

組織や経営的なことはCTOのsotarokがやっているので、
その他の例えば長期プロジェクトを進める上で必要な関係者の意識を徐々に変えるための活動だったり、
データ観点での業務改善の意識づくりだったりに重きを置いて仲間を増やそう、みたいなこともやりつつ、
他プロジェクトの技術的なアドバイスだったり(全体的なシステムの繋がりなどを踏まえたやつ)、
ひたすらETL周りの開発だったり、リアクティブシステムの仕組み作りだったりを並行してやっていました。

全然これはまだまだ途中なので数年かかる話ではあるんですが、
当然だけどかっこいいアーキテクチャで作る、という頭は毎回ほとんどなく(最初からマイクロサービスアーキテクチャ化も絶対にしない)、
できた結果がそうだった、というスタイルなので完成図がこうみたいなことはほとんどなくて、
ただデータ観点やビジネスの変化に耐えうる柔軟な変化と回復力などを考えて基本的にリアクティブ、
というのをベースにしています。

全体的にWebアプリケーションとデータ領域と業務がこうなる、という全体像がある程度見えてはいるわけですが、
これまで全領域に携わってきた!みたいな方はほとんどいないので、ytake何言ってんだこいつ
みたいなことも多かったかなと思います(市場的にもそういう方は少ないです)。

これはアプリケーションが主の思考ではなくて、

  • 事業を運営、拡大するにあたってエビデンスとなるデータはどのくらいあるか、どのくらい正確か
  • データがどのタイミングでどう生まれているか(ドメインイベントに近いですね)
  • データの中身はどういった業務と結びついているか
  • データに対する意識はどのくらいか

みたいなところから会社全体に入り込んでいき、
業務フローや事業設計だったりも分析しながら、それを支えられるアプリケーションになっているか、
大元のデータ設計はどうなっているかとメスを入れていく感覚です。

物事を進めていく上でアプリケーション開発は確かに大事ですが、
かっこいい設計や、洗練されたコーディング、開発言語選択よりも、
会社が事業を進めていくため、拡大していくためのエビデンスになるものを重視しなければなりません。

アプリケーションのここを変えることで反対側のデータがこうなって、業務がこう変わる、
そうすることで価値のあるデータに繋げられる!
これを使えばマーケティングや、セールス、もちろん今後のアレにつながる!
とアプリケーションに近いところからかなり反対側まで見ています。
言葉で書くと簡単ですが、実際にはもっといろいろあります。
(エンジニア組織外の方ととにかく会話しまくって、ドメインモデル作ったりとかコンテキストマップ作ったりとか)
これらを実現していくために技術を使って解決する、
ではそれをどんなスケジュールで、ロードマップはどう?と逆算して動いていくわけです。

ここからさらに加速していくと思いますが、
ここはなかなか本だけ読んでなんとかなる部分ではないし、数年後にわかるみたいなものなので、
関わる方はそういうものだと思ってください。
これもここ5年くらいと思考的に同じですね、昔一緒に働いていた方々は大体わかるかも。
最近の言い方だと、いわゆるCDOに近い感じかなと思います。
人によってはプロダクトマネージャ・プロジェクトマネージャにも見えるしエンジニアにも見える、
というものですね。

進もうと思う方向に進めるにはなんでもやる、という今までのスタイルそのまんまではありますが、
エンジニアでもそういう行動をしたい!という方は多いと思いますので、
中長期の事業などを考えてどういうステップを踏んで解決していくか、
それに連動して地球の裏側は何が起きてどうなっていくか、みたいな思考を重ねていくと
良いトレーニングになるかもしれません。
*後述する副業の方ではそうしたメンタリング、実現するための技術共有などもやっています。

技術的なこと

技術的には、自分の中ではほとんど新しいことはやっていなくて、
これまで培ったものをそのまま活かす、みたいな具合です。
相変わらずPHPはほとんど触らず、内部APIとか常駐プロセス型のアプリケーションはGoだったり、
データ領域はここしばらく使っているいつものApache Kafka / ConfluentとApache Sparkあたりを中心に
Scalaで開発したり(流石にこの辺をPHPでやるのは難しいっていうかできないので)。
Embulkのプラグイン作ったりもしてました。
システム全体のアーキテクチャ設計はこれもここ数年とあんまりスタイルは変わっていないので、
ソフトウェアのアプリケーション設計的な視点で見ればEvent Sourcing+CQRS、
データ的なものを取り入れて全体でみるとリアクティブシステムよりなものを作っていました(いまも)。

久しぶりにElasticsearchで形態素解析作ったりして、過去の経験をそのまま使って半分遊んでましたね・・w

エンジニアは引き続き募集してますので、一緒に働いてみたいという方は是非!
上記にあるような動きをしたりが実際に見えたり、関わったりできるので将来的にCTOとかテックリードを目指したい!
っていう方には結構いい環境だと思います。
メルカリの元CTOがいろいろやっているっていう職場に興味がある方も多いでしょう!

findy-code.io

副業

コロナ禍というのもあり 今年は本業に加えて副業も。
知人の会社の手伝いをやっていたり、複数開発に携わっていたりというのを10年ぐらいやっているので、
時間を使いながら自分の知見を広めたり、いろんな課題のアプローチ方法を洗練させようというのもあり、
いわゆる技術顧問や社外CTO的なものを何社かで始めました。
(社名公開しても問題ないんですけど、各社で公開されていないので社名は秘密)

技術顧問というとコンサル的なことだったりもするんですが、
各企業で問題が異なるので、組織的なところやチームビルディンやプロジェクトマネージメント的なことも実際にやりながら、
コードレビューやペアプロ、モブプロ、メンタリングなどももちろんやっています。
事業側の方と話してロードマップ作ったりとかもやりました(大変だった・・)。

マイクロサービスアーキテクチャ化などで協力することもあり、
ドメイン駆動設計についてのワークショップや、ユースケース駆動やモデリングの勉強会、イベント駆動に関する勉強会、
分散処理勉強会、データ設計の勉強会などもかなり濃く実施しました。
(そういうのもあって、パブリックなイベントにはほとんど参加せず)

副業はKotlinをサーバサイドで使うところも多かったので、
Kotlinやったりtsやったり、たまにPHPだったりAWS移行の手伝いをしたりしてました。

自分の知見を共有して、実際にいろいろやっていただいたり、問題解決や組織が良い方向に変わったり、
当然自分にもやった分だけ問題解決に対するアプローチの抽斗が多くなって返ってくるわけですから、
みのりがある一年でした。
来年も引き続きよろしくお願いします!

*是非うちもお願いしたいんですけどとなっても体が空いていないため現在はお受けできません

書籍

「PHPフレームワークLaravel Webアプリケーション開発 バージョン8.x対応」を少しやったりしていました。

これ以外に実は水面下で単著の書籍を書くぞ!というのがスタートしていました。
が、かなり忙しくなってしまったのもあり、亀のようなスピードに・・。

どんな書籍なのかというと、事業分析とかしつつ技術的にどう改善・開発していくか、
中長期を支えるための分析方法だったり、それを表現するための技術選択だったり、
Webアプリケーション・データ基盤関連・モデリング・コミュニケーション方法だったり、
これからの時代の企業を支えるアプリケーション開発につながるようなそんなヒントになるようなものです。
ただ亀のようなスピードになってしまったので、来年はシュッといきたいところ・・・。

健康

去年頭頃に太ってしまったので3ヶ月くらいで一気に痩せて一年ちょっと。
リバウンドはほとんどなくてちょっと増加したのはありますけど(1kgほど)、
運動等を継続しているので、単順に筋力と共に増えた、という具合かもしれません。
ランニングは毎月大体100km前後くらい走り、週末は1日10km以上必ず走っています。
おかげで1年で1000km以上走って、NIKE RUN CLUBが青になりました。
忙しい時にこそランニングすると思考がリセットされて大変いいですね!
完全に習慣化しているのでランニングシューズにもうちょっと拘りたいところです。
ランニングの荷物を減らしたいのもあって初代Apple WatchからWifiのみだったのが、
セルラー版を買いました。進化した。

ランニングのボトルとちょっとした荷物(iPhone 13 Pro Maxも入ります)を入れられるポーチは
個人的に下記のものが今のところ最強です。

他にもいろいろありましたけど、今年一年はこんな感じでものすごいスピード感で駆け抜けていきましたね・・。

関係者の方々は今年一年お世話になりました。
来年もどんどんやっていきたいと思いますので、よろしくお願いいたします。

スターフェスティバル株式会社は仲間を募集してるらしいぞ

もう少しで在籍1年

スターフェスティバル株式会社にジョインしてもうすぐ一年くらいになります。

中長期の目線を持ってゴールに向かって様々なものを変えていく、
というところにフォーカスして日々改善やいろいろな破壊活動を続けています。

だがしかし、やはり物事を改善していったり、色々な整備や推進をしていくにあたっては
いろんな方の協力が必要で、スピード感もどんどん出していきたい、
ということで、タイトルにある通り各方面のエンジニアを募集しています。

募集だけ書いても面白くはないので、中身を少し書いていきましょう。

何をしている会社?

スターフェスティバル株式会社は、

顧客基盤をもとに、「ソリューション」と「販路」の両軸から、 飲食店の中食・ECビジネス参入を支援

という領域を主としている会社です。
サービスはいくつかあり、そのなかでも ごちクルは見聞きしたことがある方もいると思います。

gochikuru.com

飲食の経験が重要か、その知識が必要か、というと必ずしもそうではありません。
事業などに触れることで、ビジネスを動かすための知識を得ることができます。
当たり前ですが・・。

どんなエンジニアがいるの?

まずエンジニアの組織としては、まだまだ小さい組織です。
いろいろなことを考えると、全然足りない!という大きさです。

全員をここで載せることはできませんが、
中の人たちを紹介しましょう!

CTO

ご存知の方も多いですが、
現在CTOは メルカリでもCTOを務めていた @sotarok で、
吉祥寺のP2B Haus クラフトビアレストランのオーナーをしながら、
技術顧問だったり、いろいろやっています。
いつ寝てるんだろう・・

チームを率いてる人たち

@sotarokと同じく、メルカリで活躍していた @kajiken
エンジニアをやりつつ、プロダクトマネージャもこなしながら、
サービス作りをいろんな面で推進しています。
各方面でリードしていてすごい!

@sotarok、@kajikenと同じ、メルカリで活躍していた @yui_tang
kajikenと同じくエンジニアをやりつつ、プロダクトマネージャもこなし、
サービス作りをいろんな面で推進しています。
チームの育成もしながら、新しい技術チャレンジなども率先していて、
めちゃくちゃバリューを発揮しているすごい人!

パワフルな newcomer

PHP界隈で有望な若きエンジニアとしてお馴染みの @strtyuu
通称あひる、人の形をしていますがあひるです。
あひるさんはエンジニア歴としては相当若いんですが、
本人のキャラと親切さ、そしてチャレンジ精神を兼ね備えていて
チームを盛り上げつつドメインについて考えすぎて寝れなくなってしまうのではないか、
というぐらい事業に向き合っている力強い仲間です。

そして岡山からフルリモート勢の @ikkitang
いろいろな経験をしていて、そしてデータベースは ikkitangに任せておけば安心。
データベースの知識と経験、そしてエンジニアとしての素養とチャレンジ精神を持っていて、
知らないことでもどんどん吸収してどんどんフィードバックしてくれる、
高い行動力を持つ強力な仲間です。

ムードメイカーとしても最高な人たちが新しく加わって、
CTOを筆頭に組織の刷新や、業務フローの刷新やシステム面の改善などを行なっています。

どんな開発をしているの?

このご時世、なかなか他社の話を聞いたり、呑みながら、みたいなのも難しいので・・。

現スターフェスティバルでは、
Webアプリケーションと、データ基盤的な領域の開発の2つが大きなところとしてあります。

Webアプリケーション

スターフェスティバルのエンジニアの多くが携わっているところですね。

元々持っているWebアプリケーションなどはPHPで作られたものが多くあります。
これらのアプリケーションの運用だったり保守をしているメンバーがいたりもします。
PHP8.0へのアップデートに向けて毎日改善したりしています。

ここ一年くらいはPHPで作る、ということはあまりしておらず
Webアプリケーションのサーバサイドでは Node.js(TypeScript)、
フロントエンドはNext.jsを用いて開発することが多く、
GraphQL などを用いてモダンな開発をしてます。

最近はTS得意な勢いのあるチームが React Nativeで
積極的にiOS/Androidアプリもつくっていて大変良い流れです。

環境についてはAWSで、ECSを中心に自動化だったりしている環境です。

データ基盤的なところ

スターフェスティバルの取り組みとしては比較的新しいところで、
自分がメインでいろいろやっている分野です。

一般的なETLを使ったデータ集約を筆頭に、
集約したデータの加工や他データソースとの結合、
レポーティングのための仕組みづくりだったりに取り組んでいます。
データ基盤ということもあって、小難しいものが多かったりもしますが、
個人的にはここ5年くらいずっとやっているところなので、最高に楽しいところではあります。
ライブラリがない?それならば自分で作る、みたいな毎日です。

ここはGoやScalaなどを使って細々した処理を作ったり、
Sparkで動く処理をひたすらやりつつ、データ起点にビジネスを支える仕組みなどを考えて
ビジネス側の人たちと連携しながらわちゃわちゃしています。

どの領域もそうですが、技術ファースト的な考え方をしておらず、
事業を中心に、
要件分析にフォーカスしつつ中長期を意識したものづくりをしています。
当たり前のことですがドメイン駆動です。

とはいっても仲間がたりない

簡単な紹介だけですが、
各領域のエンジニアの仲間を求めています。

開発組織を強化して、様々な改善とサービス作りをもっと推進していきたい
というのが第一!!!

TSを用いた開発では、フロントエンド、バックエンド、というよりも
どちらに対してもある程度のカバレッジを保つTS/JSエンジニアでしょうか。
当然めちゃくちゃ高い専門性重視というわけではありません。
TSをメインの武器にしつつ、どちらでもチャレンジできる、
そしてチームワークと事業を主としてチャレンジ精神あふれるエンジニアであれば
どの開発でも力を発揮することができます。

逆に待ちのスタイルの方はあまりフィットしないかもしれません。

基本的にPHPのエンジニアも、データ領域のエンジニアも同じですね。

当然新しいものづくりだけでなく保守や運用もあります。
例えば0 1のフェーズは苦手だけど、
運用しながらアプリケーションをごりごり改善、リプレースしていくのが好き、
そんな方は神様になれます。

もちろん上記に挙げていないインフラエンジニアも求めています。

開発プロセスや、アプリケーション設計などについても日々ディスカッションしながら
毎日トライアンドエラーができる環境になっていて、
事業をドライブさせていく試みであれば大歓迎な組織です。

あ、住んでいるところは問いません。
フルリモート大歓迎です!!!!

あまり長々書いても誰も読まないので、
今は転職しないけど話を聞いてみたいな、とか
興味あるなーとか、面白そうだな、という方は気軽にtwitterなどで連絡をいただけるとうれしいです。
ここには書けない話なども詳しくお伝えします!!!!!!