ベイビー氏

Web系技術の技術者です。学習内容や経験の備忘録

フロントエンドにもドメインがあって、モデリングには価値があるという話

【後日追記】 この記事ではドメインモデリングのHowは特に触れてないです。 フロントエンドのドメインモデルとはWhatなのかを書いています。

———

最近フロントエンドのコードを作る時間が殆どになったのと、AIが生成したコードの品質を担保するのが仕事になった。 フロントエンドのコードを作るうえで、人間のエンジニアとしてやるべきこと・やる価値があることはなんだろうかと考えてるうちの一つにフロントエンドのドメインモデリングがある。

フロントエンドにも固有のドメインはある

ドメインモデリングについて本や記事を読むと多くはバックエンドの処理を前提に語られてる。 モデリング対象の例も、バックオフィスの業務が多くて、ドメインエキスパートもバックオフィスのスタッフを対象にした記事が多いように見える。 フロントエンドのモデリングの話も少しあるけど、バリデートされた値か否か~という文脈かが多いイメージ。

でも、フロントエンドにもバックエンドで語られるようなドメインモデルはあって、それはバックエンドとは違う形をしていることも多くあるはずという話。

UIを決める人(デザイナーやカスタマーサポートなど会社によりけり)は、背後のバックオフィスや内部のコードがどう動いているかを知らなくても、画面から見える情報をもとに、ユーザーがどういう状態にいて、次にユーザー何をしてほしいのかを想像する。

このときUIを決める人が思い描く状態やフローは、バックエンドが管理しているドメインとは違う粒度や用語で扱われることがある。 その粒度や用語が、フロントエンドが扱うべきドメインだと考えてる。

(フロントエンドのドメインモデルというのか、ビューモデルと呼ぶのかBFFの延長と呼ぶのかが正しいかはちょっとわからんのだけど、、 兎に角、UI側に「捉えるべき概念」が存在していて、それを説明するために"フロントエンドのドメインモデル"という用語をつかうことにしている。)

具体的にフロントエンドのドメインモデルとはどういうものなの

実務で出会ったケースをちょっと汎化して、配送状態を扱うシステムの例に置き換えて紹介してみる。

このシステムのUIには、「発送前か、発送済みか」で表示を出し分ける箇所があるとする。 配送追跡のリンクが発送後にだけ見えるといったもの。 ユーザーが気にしているのはもう発送されたかどうかだけ。

一方、バックエンドは配送の状態をもっと細かく管理している。

type ServerDeliveryStatus =
  | "LABEL_CREATED"
  | "PACKING"
  | "SHIPPED"
  | "IN_TRANSIT"
  | "OUT_FOR_DELIVERY"
  | "DELIVERED"

UI側が必要としているのは2つの状態だけなのに、バックエンドはこれだけの状態を持っている。バックエンドの扱う状態と粒度が違う。

素朴にAPIから受け取ったステータスをそのまま分岐条件に持ち込んだとする。

const isTrackingVisible =
  deliveryStatus === "SHIPPED" ||
  deliveryStatus === "IN_TRANSIT" ||
  deliveryStatus === "OUT_FOR_DELIVERY"

これでも正しく動くが、問題を抱えている。

この分岐が本当に気にしているのは「発送前か、発送済みか」でしかない。バックエンドの状態を6種類すべて知る必要はないのに、実装はその粒度を引きずってしまっている。画面を見る人の頭の中にある「発送済みだから追跡できる」という素朴な概念が、コードのどこにも形として存在しない。

そこで、表示制御に入る前に、バックエンドの状態をUI側の業務概念へ変換する。

type ShippingPhase = "BEFORE_SHIPPING" | "AFTER_SHIPPING"

function toShippingPhase(status: ServerDeliveryStatus): ShippingPhase {
  switch (status) {
    case "SHIPPED":
    case "IN_TRANSIT":
    case "OUT_FOR_DELIVERY":
    case "DELIVERED":
      return "AFTER_SHIPPING"
    case "LABEL_CREATED":
    case "PACKING":
      return "BEFORE_SHIPPING"
  }
}

表示制御はこの概念の上に乗せる。

const isTrackingVisible = toShippingPhase(status) === "AFTER_SHIPPING"

こうすると、バックエンドに新しい配送状態が追加されたときにやることは、「それは発送前扱いか、発送済み扱いか」を一度判断するだけになる。toShippingPhaseswitch を一箇所直せば終わりで、個々の表示制御には手を入れなくていい。

「整理」ではなく、変更影響範囲を閉じ込める活動

toShippingPhase を作ったのは、変更の影響範囲を、UI側の概念の単位に区切りなおすのが目的。

たとえば上記のUI表示制御とは別の部分で運用が変わって、「OUT_FOR_DELIVERY(配達中)は、配達が完了するまでは発送済みとして扱わず、発送前のフェーズに戻したい」という要望が出たとする。

要望起案者からすると、これは小さい変更に見える。概念のうえでは「OUT_FOR_DELIVERYAFTER_SHIPPING から BEFORE_SHIPPING に付け替える」だけの話で、個々の表示制御に影響が出てると思わない。(配送中か配送前かで制御されてる部分が細かい配送状態と同一概念とは思えない)

function toShippingPhase(status: ServerDeliveryStatus): ShippingPhase {
  switch (status) {
    case "SHIPPED":
    case "IN_TRANSIT":
    case "DELIVERED":
      return "AFTER_SHIPPING"
    case "LABEL_CREATED":
    case "PACKING":
    case "OUT_FOR_DELIVERY": // 配達完了まで発送前扱いにする
      return "BEFORE_SHIPPING"
  }
}

物理的な実装で言うとcase を一行動かすだけで終わる。 表示分岐に関しては発送済みなら表示するというロジックに変更はない。

でも、もし最初の素朴な実装のまま、status === "OUT_FOR_DELIVERY" の判定がUIのあちこちに散っていたら↓のような悪影響が出る。

  • 影響範囲を洗い出さないといけない
  • 条件が追加された場合の判定結果がデザインの意図どおりかを確認しないといけない

業務上は「分類をひとつ付け替えるだけ」の小変更なのに、実装上は「影響範囲の広い変更」「デザイナーを巻き込んでデザイン意図を確認しないと判別つかない変更」になってしまう。

この認識のズレが多いと、ビジネスに詳しいロールの人が、シシテムの変更案を検討して提案したけどエンジニアの見積が出たらコスパが合わないみたいな状態を起こす。 結果、事業の意思決定を阻害したりシステムの変更速度を下げる。

フロントのドメインを正しくコードに反映していれば、業務側が感じる変更の大きさと、実装側が引き受ける変更の大きさが一致する。 「配送済みの判定変えます」という一語で、両者が同じ規模感を共有したい。

生成AI時代に、人間が担うべき部分

最近はコーディングの多くを生成AIに書かせるようになった。

それらしい境界を引くことはAIにもできる。状態名の意味を推測して、もっともらしい分類を提案することはできる。 でも、それが正しい境界かどうかを決める根拠は、コードの中にはないのでここの最終意思決定は人間が担いたい。

境界の正しさは、モデルに関わるステークホルダーとの会話や、その人たちしか知らないコンテキストの側にある。AIはそこに能動的にはアクセスできない。だから、それらしい境界は引けても、正しい境界を引くことはできない。

ここが、生成AI時代に人間が引き受けるべき部分じゃないかなあ... どこの場で、誰の会話を聞いて、どの概念で整理して、どの変更影響範囲に閉じ込めて、どの責務でUIを分けるか。これはコードの外にある文脈を、コードの内側の構造に翻訳する仕事で、文脈を持っている人間にしかできない。

(コードの内側の構造ではなく、自然言語のドキュメントで残すってことも今ならできるけど、せっかく型とかで安全に意図を残す術があるのに自然言語に委ねるメリットは自分はあまり感じてない)

よりより正しい規模感でより安全にシステムを変えられる状態を作ること。それは、エンジニアが事業に対して担える役割のひとつだと思う。

OSSコントリビュートする時にメールアドレスをちゃんと設定しろという話

タイトルの通り、最近ちょいちょい使ってるOSSにpull request出す時にgit cofigの設定を間違えてしまった話。

普段OSSにcommitすることもなかったので、 git cofigには日常の遊びとかで使ってるメールアドレスを気にせず設定していた。

OSSに対してPullReqeustを出すにあたって、普段使いのメールアドレスを丸出しにするのはやだなあ、ということでuser.emailをxxxでマスクした(つもりだった)。

user.name=babywkb
user.email=xxx@users.noreply.github.com

ドメイン名はちょっとググってusers.noreply.github.comつけとけばいい慣習になってると見かけたのでそれにした。

user.nameには自分のGithubのアカウントIDであるbabywkbを入れてるので、これで問題ないだろうとPull Requestを出して、無事mergeしてもらったんだけど なぜだかOSSのコントリビューター欄に自分が居ない。 あれーー?と思い色々見て回ったらどうやらGithubAccount名がxxxの人がコミッターになっていた...

これuser.emailに〇〇〇@users.noreply.github.comを利用する場合は〇〇〇をちゃんと自分のアカウントIDにしてないと、commitログに別の人が紐づいてしまうみたい。。。 user.nameの方で紐づけられると思ってた。

xxxさんにも迷惑だし、自分もせっかくPullRequest出したのにcontoributersにアカウント乗らないのちょっと悲C。

↓みたいにGithub上でメールアドレスを非公開にするオプションをONにしたら、user.emailに何を設定すればいいか教えてくれた。

githubのメール非公開オプションをON

これに懲りずまた、気になる部分あったらPullRequest出していこう。

ちなみに今回PR取り込んでもらったのは↓。自転車で遊ぶ人ならおなじみのサービスのMCPなので、よかったら改善にご協力オネシャス。 github.com

「他者と働く──「わかりあえなさ」から始める組織論」を読んだ

他者と働く──「わかりあえなさ」から始める組織論を読んだ。

自分の視点からは理屈が通っている提案なのになぜか相手が動いてくれない、そういった時に 相手を観察し、対話をすることで解決策を見出そうという話。

色々実例や細かなマインドセットの構築方法について記載はあるが、 自分としては要は相手の視点も踏まえて解決策を見出そうという話だと読めた。

本の中で"新規事業に関わる社員と収入源たる収穫期の事業(既存事業)に関わる社員との溝"についてのエピソードが紹介されていた。 新規事業内に人や予算を割くことに既存事業は冷ややかな目を向けるというもので、 自分はこれまでの経歴的に、新規事業側に関わる立場で働く時間が多かったのでしっくり来るものがあった。

今は別の環境にいるが、 当時新規事業側に関わる立場として、新規事業内で既存事業のための実験場や人材育成機関にされることへの組織や会社への不満がとにかくあった。 その不満の解決策として取ったのは不満のエスカレーションだった。 「新規事業へ本気度があるのか」「どういう姿勢なのか」と詰めたところで結果はもちろん解決しなかった。

エスカレーション先の相手は収入源たる収穫期の事業のマネジメントと新規事業のマネジメント両方を兼ねていることはわかっていて そのうえで新規事業側の視点の正義をぶつけたが理解されないという失望だけ残った。

本の中では収穫期の事業としてどういう成果のプレッシャーを受けているのかを観察し、落とし所や解決に向けてステークホルダーとの関係を作るエピソードが紹介されていた。 おそらく当時自分がやるべき解決策はこの観察とステークホルダーとの関係構築だったのだが、できていなかった。

エンジニアとしてシステムの設計・実装というスコープに閉じこもることが許されてしまう環境に甘えていた部分が多分にある。(別に許されてはいなかったのかもしれないが)

知らない上位者や知らない別のチームの人間と関係を作りに動き出すというのは精神的な体力を使う行為で、自分はそれを避けるフシがある。 別に人見知りするタイプではないし、人前で話すことも苦ではないのだが、"能動的に見知らぬ人とビジネスのための関係を構築する"というのが経験値的に弱い。 どこかに恥ずかしさや気後れがあるんだと思う。

これを避けて通り続けていては、上で書いた"やるべき解決策"を実行できないわけで、 であるならエンジニアとして自分の視点では正しいと思うのに相手が動かないという課題の解決には動けない。

いい加減腹をくくって"能動的に見知らぬ人とビジネスのための関係を構築する"ということができるようにならないといけないな、と思うなど。

今年の一つのテーマにしよう。

関ジャバ'19 8月に参加した感想

この記事は『関ジャバ'19 8月』でのセッション内容を説明するもの、及びセッションで扱った技術内容を解説するものではありません。 私がイベントに参加して興味を持ったこと、感じたことを纏めた備忘録・日記を公開しているだけです。なるだけ誤った情報を書かないつもりですが、誤った情報を書いていれば指摘してもらえたら嬉しいです。

今回はセッションが3つ。おもったより日記が長くなっちゃったのでまずは①②の感想書いて
今週末に③の感想書こう。

①Reactの公式ドキュメントが読めるようになった / ふじたさん(30min)
Kubernetesでやらかしたりハマったりした話 / SusumuYamadaさん(30min)
③リフレクションで遊ぼう / うらがみさん(60min)

Reactの公式ドキュメントが読めるようになった / ふじたさん(30min)

React式ドキュメントの話

開発経験がないまま公式ドキュメントを見ててもピンと来なくてそっ閉じしてたけど
実際にコード書いてレビュー受けてを繰り返したら公式ドキュメントもよめるようになったって話。

自分が公式ドキュメント読みに行く、読めるようになるまでどういう経過だったかなぁと振り返ると
1.エラーに困って公式ドキュメント見に行ってピンとこない
2.関連してそうなブログ記事を探して見様見真似で対策
3.対策がうまくいったら後で裏とりの意味で公式ドキュメント見る
って流れで公式ドキュメントとお付き合いして読めるようになるケースが多い気がする
(裏取りきらずに完結しちゃってることもいっっぱい有るので自信もって言えないけど...)

JSXについての話

JSXとは〜って話。

JSXで書いておけばユーザーの入力値もエスケープされるからXSS避けるメリットがあるんだなぁ。
あえてエスケープせずに素のHTMLとして表示させるのもVueにもReactにもあったなぁっと思って調べると、当たり前だけどそこでユーザーの入力値受け付けるんだったらXSSは起こせるみたい。
同じような仕組みに対して名前がVueはv-htmlなのに対して ReactがdangerouslySetInnerHTMLっていう名前で注意を促してるのがなんか面白い。
https://ja.reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml https://jp.vuejs.org/v2/guide/syntax.html#生の-HTML

React要素の話

ReactDOMのrender()とReact.Componentのrender()があるよーって話が面白かった。 ReactDOM.renderで既存?のDOM 要素をReact 要素にするっての、どいうとこで使うんだろう?って少し気になったので調べよう。

質問タイム

AtomicDesignは開発のスピード感を損ねるのでやめたってのがどういう背景だったのか気になったなぁ。

Reactの公式ドキュメントちゃんと読まないと知らないこといっぱいだなぁ、読みたいなぁ
読むためにも実際に書いてみないとなーって気分になった。

Kubernetesでやらかしたりハマったりした話 / SusumuYamadaさん(30min)

社内システムの自動化、CI/CD環境作ったりしていてやたかした話3本立て。

DBのデータを吹き飛ばした話

kubectl deleteでpod消すけど、DBのデータは外部ボリュームマウントしてるから消えないだろうと思ってたけど、DBのデータが吹き飛んだ話。 設定をちゃんと読まないままだったのが原因だったそ ↓前に表示されたymlの内容まで読めなかったけど、これかなぁ? https://kubernetes.io/docs/concepts/storage/persistent-volumes/#recycle

k8sちゃんと使ってないので、同様の自称に気をつけようというよりは、何かと設定ファイルしっかり読んでからコマンド実行しなきゃ怖いことになると改めて思う恐ろしい話だった。

メモリの食い過ぎでアプリが落ちた話

DBコンテナが唯一乗ってるワーカーノードが落ちたことで、そこに依存してる他ノードのアプリもDB起因で落ちるようになった。原因を探るとメモリ食い過ぎで落ちた話。 アップデートの時に新しいpodが起動してから古いpodが消えるようになってるので、アップデート繰り返してるとメモリ取り切れずに落ちたとかリソースの上限を設定するってお話だったから ↓のへん気をつけるって話かなぁ https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#updating-a-deployment https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/

k8sで気をつける観点というかTIPSを実体験から聞けて楽しかった。

ingressのバージョンのズレでうまく動かなかった話

インフラチームがクラスタを準備する(Ingress Controllerのバージョン)けど
podの設定(Ingressの設定)を書くのは各アプリチーム
その結果、Ingress Controllerのアップデートに対応できずIngressの設定がうまく通らなかった話...
っていう認識でよかったのかな、ここが知識不足でめっちゃふわっとしてる...

インフラチームの設定をアプリチームは能動的に情報取りに行かなきゃわからない状況が辛かったってのがとてもなるほど。 インフラとアプリチームそれぞれで環境設定するならブリッジする人いるなーって思った。

質問タイム

k8s使っててメリットはあったか?(k8sを使ってることで採用に繋がることも含め) って質問が出てた。 答えはメリットは使ってる人が楽しいくらいしかないとバッサリで笑った。 社内技術力低下へのカンフル剤としてk8sが採用された背景らしく、k8s自体?のメリットはないそう(採用も含め)。

ただ、使ってる人が楽しいって僕的には大事だなぁって思ってるところで 苦しみつつもエンジニアが楽しんで開発やら運用やらできてる状態は 業務が楽になるのと同じくらい大事に扱われてほしいし、技術選定やチームマネージメントをする立場になったら大事にしたいポイントだと思ってる。

全般通してk8sの知識が足りないので、たぶんこういう話してるのかなー?って聞き方になったのが勿体なかった。k8sもうちょっと学習したいなぁ。

3つのセッションぜんぶ楽しかった。 この日記書くためにメモとりながらイベント参加してみたけど、まぁまぁ話は取りこぼさずに聞けたかなー。

おしまい、おしまい。

VisualStudio CodeでReactのデバッグする時の手順

3回ググるハメになったのでもう記事にしちゃいます

完全にこれに習ってるだけ

Live edit and debug your React apps directly from VS Code — without leaving the editor 🔥 🎉🎈

create-react-appは済んでる前提です。

VisualStudio Codeの拡張機能「Debbger for Chrome」をインストール f:id:babywkb:20180302235447p:plain

デバッガーのアイコンを押して f:id:babywkb:20180302235745p:plain

デバッグ▶」って書いてるとこの近くのアイコン(歯車マーク)を押したらlaunch.jsonが開く f:id:babywkb:20180302235859p:plain

launch.jsonの中をまるっと下に置き換えて

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Chrome",
            "type": "chrome",
            "request": "launch",
            "url": "http://localhost:3000",
            "webRoot": "${workspaceRoot}/src"
        }
    ]
}

npm startでかってに画面が立ち上がる

あとはお好みでコードにブレークポイント置いて開発しましょ!