UGA Boxxx

つぶやきの延長のつもりで、知ったこと思ったこと書いてます

【Zod】Always-Valid Domain Modelを実装する話

ZodでAlways-Valid Domain Model を実現する話し

zenn.dev

Always-Valid Domain Model とは

  • ドメインモデルがそのライフタイムにおいて常に有効な状態を保つように設計されるアプローチ
  • ドメインモデルの不変条件をクラスのコンストラクターやメソッドを通じて強制し、不正な状態になることがないようにする
  • これにより、アプリケーションの堅牢性が向上し、ランタイムエラーの発生が減少する

ドメインモデルの有効な状態とは、例えば「数量」における「自然数」で「上限付き」が検証されていることを指す

このAlways-Valid Domain Modelの考えは、RefinementsとBranded Typeという2つの方法論を実装すると実現する

  • Refinements(値の制約): 例えばnumber型に「自然数」「上限付き」の制約を加えた値として表現すること
  • Branded Types: (同じ構造の型を区別する): 「価格」などの他のnumber型と混同されないように、これらの数値を型レベルで区別する

それぞれzodを使って実装できる

  • Refinementsはzodの本領なところだと思おうので説明は割愛
  • Branded Typesはzodのbrandを使うと実装可能なことを知った

他に記事での気づきは、Result型の定義にNeverThrowを使っているところ

github.com

これまで自分で定義していたので、これに置き換えるの良さそう

ドメインモデルを検査し、それをイミュータブルに保つやり方の良い参考記事だと思った

【組織づくり】ポジションをつくらない組織づくり

昨日書いたCTOの役割変化の話とはうって変わって、そもそもポジションをつくらない話も面白かった

zenn.dev

以下まとめ

なぜポジションをつくらないのか?

  • 特定のポジションがあるとそのポジションを目指すことが社内で正当化されちゃう
  • 会社のビジョンと個人のビジョンがポジションを設けることによって変わってしまう恐れがある

「期待合わせ会」で期待値調整

  • 今期自分がやること
  • 他の人に頼ること
  • 心配ごと
  • こういう時は助けて

大きく2通りのエンジニアキャリア

  • 事業を前に進めるエンジニア(Ace)
  • 事業を進めやすくするために仕組みを整えるエンジニア (Captain)
  • AceとCaptainの両輪でプロダクトづくり

プロダクトや技術スタックなどの意思決定

  • その都度リードしたい人が素案をまとめて、チームと相談して決める

ポジションが欲しくなったら

  • 「PdMっていらないんだっけ?」ってなったらそのポジションに期待することを言語化
  • それはそのポジションをつくらないと解決できないことなのかを考える

基本的に自分で考え、チーム内で期待と役割を調整し、チーム全員で協調しながら実行できる人がいないと厳しい気がするが、そのような人たちが集まったなら確かにポジションなしでやれるかもしれないと思った

【組織づくり】CTOとしての役割の変化の事例

CTOという役割がどのように変化していったかの事例がとても参考になった

speakerdeck.com

CTOの役割の変化が以下のように変わっていったという話

開発 -> テックリード -> PdM/PjM -> 採用

変化の軸は企業価値最大化という目標に対して、一番ROIが高そうな仕事をすること
企業価値とは時価総額+有利子負債
 これを最大化させ、持続的に事業を成長させ資本市場から評価いただく必要がある
 →ミッション・ビジョン実現のため
→最大化につながる仕事を判断するために
 →事業計画の要素分解でボトルネックを探る
  →売上目標・粗利・営業利益などを理解

他に参考になったのは以下のテンプレートで、その役割の時に何をやっていたかがわかりやすかった

例えば2020年は

当時の状況
経営 CEO/COOに全振り
開発業務 とにかく早くプロダクトリリースするために機能開発
プロダクトマネジメント 店舗へ行ってヒアリングしたデリバリーサービスのAPI仕様を確認したり
プロジェクトマネジメント タスク管理はほぼやらず自律的に開発。デイリーMTGのみ実施
ピープルマネジメント Twitter DMやリファラルを活用して採用活動
テクノロジーマネジメント
CS 経営陣3人で24h365d電話対応

で、2023年~は以下になったとのこと(その間もある)

当時の状況
経営 組織・事業拡大に伴い経営チームでの会話内容増加。CTOとしてプロダクト戦略や採用計画の検討
開発業務 自分で手を動かすことはほとんどない
プロダクトマネジメント 業務文書を定義して社内メンバーに権限移譲
プロジェクトマネジメント 業務文書を定義して社内メンバーに権限移譲
ピープルマネジメント とにかく採用
テクノロジーマネジメント 新プロダクトはテックリードに。一部の重要な開発のみ相談
CS なし

自分もこれを参考にまとめてみようと思う

【サービスデザイン】サービスブループリント

以前あるイベントで拝見した、樋口さんのスライドで「サービスブループリント」が紹介されていたので調べた

speakerdeck.com

スライドでは

ビジネス・サービスに関係する全てのステークホルダーのタスクと連携を可視化し、フロントヤード・バックヤードの課題抽出や最適化に活用するためのツール

とある

Miroにテンプレートがあった

サービスブループリントとは?作り方と例【テンプレート付き】 | Miro

自分なりにサービスブループリントをまとめると、

  • 1984年にリン・ショスタックによって考案されたフレームワーク
  • サービスの設計、分析、改善を目的としたツール
  • 顧客の体験(カスタマージャーニー)を視覚的に表現し、サービス提供の背後にあるプロセスや相互作用を明確にする
  • これにより、企業や組織は顧客にとって価値のあるサービスを効率的かつ効果的に設計、実施、管理できるようになる

サービスブループリントの主な要素

  • Customer Actions: 顧客がサービス利用時に取る行動を時系列に沿って描写
  • 接点(タッチポイント): 顧客がサービスと直接接触する点
    ウェブサイト、店舗内の案内板、顧客サービスとの会話などが含まれる
  • 可視化ライン(ライン・オブ・ビジビリティ): 顧客から見えるサービスの要素と見えない要素を区分けする
    このラインより上が顧客に直接見える部分、下が内部プロセスになる
  • 内部相互作用: 従業員やシステム間の相互作用を示す
    これには、顧客には直接見えないサポートプロセスや裏方作業が含まれる
  • サポートプロセス: サービス提供を支える裏方のプロセス
    在庫管理や配送、ITサポートなどがこれに当たる

サービスブループリントの作成手順

  1. サービスの範囲の定義: 分析するサービスの範囲や目的を明確にする
  2. 顧客の行動のマッピング: 顧客が取る行動を時系列に沿って列挙する
  3. タッチポイントと物理的証拠の識別: 顧客がサービスとどのようにして接触するか、そしてその証拠を特定する
  4. 内部プロセスのマッピング: 顧客の体験を支える内部の相互作用とプロセスを識別し、マッピングする
  5. 分析と改善の機会の特定: ブループリントを使用してサービスのギャップや非効率性を特定し、改善の機会を見つける

サービスブループリントでサービスの全体像を理解し、改善点を発見するためツールだとわかった

ただ、実践してみないとやり方がいまいちわからないので練習してみる

参考

サービスブループリント:定義 – U-Site

【システム開発】一般的に普及してるレイヤーを使用した設計はソフトウェア設計として破綻しているという話

一般的に普及してるレイヤードパターンの設計はソフトウェア設計として破綻しているよという話

medium.com

理由

  • レイヤーというのは何らかを抽象化したものをさすが、レイヤー化することが抽象化したことにはならない
  • 一般的に普及してるレイヤー化は機能依存していて、テスト容易性も可読性も拡張性も下げている

一般的に普及してるレイヤー化というのは、例えば以下のようなレイヤーに分けることをいっている

  • プレゼンテーション層
  • アプリケーション層
  • データ層

OSIの7階層との違い

抽象度は、大きな処理の流れを簡潔に表したものが「抽象度が高く」、より具体的な処理の流れを表したものは「抽象度が低い」

OSIの7階層はその定義通りに抽象度で階層わけされている

一般的なレイヤー化をもう一度見ると、このように階層わけされているわけではない

プレゼンテーション層 は アプリケーション層 の大きな処理の流れを関係に表したものではないからだ(他の層もしかり)

つまり、「レイヤー」という名前がついているが「レイヤー」じゃない残念な感じになっているよねと

なんて呼ぶ?

OSIの7階層よりも、レイヤードデザインパターンの「レイヤー」の方が市民権を得ているので、OSIの7階層のようなレイヤーに別の言い方をしたい

そこでこれを「Strata」と呼ぶことにする

関数型プログラミングではすでに「Stratified Design」という階層化の名前が使われており、そこから引用したとのこと

一般的なレイヤー分けの依存関係

一般的なレイヤー分けは上位レイヤーに依存している

これをよく制御の反転や依存関係の反転のような原則を用いたりして、設計上は抽象レイヤーのみに依存しているように見えるが、実装ではモックや依存注入などのツールが必要で完璧には覆い隠せていないことが多い

また、依存注入などを使うと全体が見えなくなるので迷子になりがち

コードで表すとこうなり

public static void Main(string[] args) {
    var data = new DataLayer();
    var business = new BusinessLayer(data);
    var presentation = new PresentationLayer(business);
    presentation.Show();
}

それぞれのテストに依存するレイヤーのオブジェクトが必要になる

Stratified Design

Stratified Designでは以下のように実装する

public static void Main(string[] args) {
    var data = new Data();
    var business = new Business();
    var presentation = new Presentation();
    var app = new App(presentation, business, data);
    app.Run();
}

各レイヤーはどこにも依存しておらず、最後のnew Appでアプリケーションの最上位層を表している

中を覗くと

public void Run() {
    var text = presentation.Ask_for_text();
    var n = Count_words(text);
    presentation.Display_word_count(n);
}

このようになっており、プロセス全体の概要を把握できるようになっている

さらに、Count_wordsや、Ask_for_textDisplay_word_countの詳細を知らなくても実行できるようになっている

そして、詳細をみても同じような構造になっている(さらに抽象化された関数があり入れ子になっている)

図にするとこう

ノードの関数は詳細な関数を「統合」しているだけで、リーフの関数に「ロジック」がある状態になっている

これを統合操作分離原則 (Integration Operation Segregation Principle: IOSP)というらしい

【技術本】Grokking Simplicity 本紹介

DMMF本とは別の優良な関数型思考プログラミングの実践本『Grokking Simplicity』を読んだ

対象読者

本の構成

  • 第 1 章〜第 2 章:関数型プログラミング(以下、FP)の紹介と、何が可能になるのかについて概要説明
  • 第 3 章〜第 9 章: コードをアクション、計算、データで区別することの紹介と実践
  • 第 10 章〜第 19 章: 第一級抽象化の考え方の紹介と実践

最初から順番に読むことを推奨しており、各章に演習問題もついているので進めながら理解度を増していく

コードはJavaScriptで書かれているが、推奨ではなく FP に慣れていない人に教えるのに最適な言語のため利用しているとのこと

本に出てくる要点

以下の要素を使って、シンプルに実装していくための実践本

関数型プログラミング

副作用のない純粋な関数のみの使用を理想とするプログラミングスタイル

副作用というのは戻り値以外の関数の動作のことで、例えば

  • メールを送信する
  • ファイルの読み取り

など

ただ、ソフトウェア開発で副作用がないようにすることは不可能に近い

関数型プログラミングは長い歴史があり、これらの副作用との向きあってきたので対処法がある

そのため、理想だけの非現実的なプログラミング手法では決してないことが説明される

コードをActions、Calculations、Dataで区別する

大前提として関数型プログラミングではコードを以下の3つに区別する

  1. Actions: 実行タイミング、実行回数に依存する
  2. Calculations: 実行タイミング、実行回数に依存しない
  3. Data: 何かを実行することはできないが、実行しなくても意味がわかる

このデータ区別が関数型プログラミング の基本になる

Stratified design

データの区別をする前に、まずはコードを「更新頻度」別にまずは整理する

そうすると幾つかの階層が見えてくる

本にあった自動ピザ作成システムを例に挙げると、以下のように「ビジネスルール」「ドメインルール」「技術スタック」の3つの抽象された階層に分けられる

更新頻度 動作 階層
ピザのメニューを毎週かえる ビジネスルール
ピザを作る ドメインルール
◯◯のプログラミング言語で実装する 技術スタック

関数型プログラミングではこのように抽象レイヤーで階層化して設計することをStratified design(ストラティファイドデザイン)と呼ぶ

階層は3層に分けられるとは限らず、プロジェクトによっては何層にもなることがある

第一級抽象化

抽象化して説明された動作は実施にはもっと処理が複雑である場合が多い

例えば、「ピザを作る」という動作は実際には

「生地を伸ばす」→「ソースを塗る」→「具材をのせる」→「焼く」

のような処理が隠れている

このように本当は大きな処理を隠して「ピザを作る」と表現し、そのレイヤー中で「ピザを作る」として扱えるようにすることを第一級抽象化と呼ぶ

これは再帰的で「生地を伸ばす」もまた大きな処理の抽象表現で、その中の動作もまた処理の抽象表現である 

最終的にはプログラミング言語が提供する機能になる

まとめ

この本は以下の要素を使って、シンプルに実装していくための実践本で、

多くのページをさいて丁寧に説明されているので読みやすい

この要素を使った関数型思考が身につけらればビジネスの変更に耐えらえる堅牢なソフトウェアが作れるかもしれない

【技術本まとめ】実践Next.jsを読んでのメモ

takepepeさんの新しい本『実践Next.js』を読んだのでメモ

AppRouterは使ったことがなかったので、この本で情報のキャッチアップをしたい

本の内容は4章まではAppRouterの基礎情報で、それ以降はかなり実践的なサンプルアプリの実装の話

自分としては以前からNext.jsのPagesRouterは使っていたので、あの機能はAppRouterではどうなるのかという差分を中心に読んでいった

全体的にかなり分かりやすくまとまっていたので、とても読みやすかった

以下はメモ

  • ハイドレーション:任意のDOMにイベントハンドラーをアタッチすること
  • Client Componentからimportされるコンポーネントや関連ファイルもブラウザ向けにバンドルされる
  • 全部に"use client"をつける必要はない→親がClient Componentなら不要
    • usePathnameを使うならClient Componentにする
    • 汎用性の高いコンポーネントは親がServerかClientのどちらにもなりうるので、"use client"は最小限に留める
  • fetch関数のデフォルトのキャッシュ設定
    • 更新頻度が低い:静的データ(何も設定しなければこれとして扱われる)
    • 更新頻度が高い:動的データ(fetch(url, { cache: 'no-store' }) を指定すると毎回取得する)
  • devモードでサーバー立ち上げるとキャッシュの挙動が把握できないので、ビルドして起動する
    →その前にキャッシュの破棄(.next/cache/fetch-cacheを消す)をすること
  • Routeのレンダリング
  • 動的レンダリングRouteになる要因は3つ
    • ① 動的データ取得が行われると動的レンダリングになる
      • { cache: 'no-store' } 有れば動的
      • ビルド時にレンダリングを試みるので、動的レンダリングの場合はビルド時にAPIと接続できないとビルドエラーになる
    • ② 動的関数の使用
      • Cookie参照、リクエストヘッダー参照、URL検索パラメータ参照など
    • ③ Dynamic Segmentの使用
      • パスの一部が [categoryName] になっている場合はcategoryNameがparamsとして取り出せるので、この機能を使った場合
  • import { notFound } form "next/navigation" 知らなかった
  • route.tsを使うとWeb APIになる
  • global-error.tsはハンドリングされなかった例外が最後に行き着く場所("use client"が必要)
  • Route Groups:グルーピングのためにフォルダを作りたいが、パスの一部としたくないフォルダはフォルダ名を () で囲む

    • この下にlayout.tsxをとかおくと、それぞれのLayoutが適用される
    • 全くリーティング対象にしたくない場合は _ をフォルダ名の接頭辞につける(例:_components)
  • 3.3 Parallel RoutesとIntercepting Routesは新しい考え方

    • @を接頭辞につけたフォルダはURLのパスとして無視される
    • 「slot」と呼ばれるノードがchildrenのようにpropsで渡される
    • @配下のSubtreeと表示しているRoute Segmentが一致したらそのノードがレンダリングされる
    • default.tsxは一致するまでの間のslotとして表示される(返すものがなければnullを返す)
    • Intercepting RoutesはRouteを「横取り」するRoute定義でParallelと合わせると、@配下のSubtreeと表示しているRoute Segmentが一致したらそのRoutesを横取りする
    • (.)とか (..) とか相対パスのように定義する
  • 3.4 静的なメタデータはmetadataオブジェクトをexportすれば良い、動的の場合は generateMetadata 関数をexportする

    • メタデータと画面表示時の2回リクエスト送ってしまうので「Requestのメモ化」を内部で行なっている(6.3節で解説)
    • メタデータは継承されるため、個々の設定は不要
      →ただし、パラメータが異なるとメモ化されない
       →パラメータの差異が出ないように目的別に関数を作り共通化すると良い
    • generateMetadata関数の第二引数のResolvingMetadataで継承された親のメタデータを参照できる。
  • 4.1 page.tsxとroute.tsは同じフォルダに入れるとコンフリクトを起こす

  • 4.2 画面と同様にビルド時に以下の要因が検出されると動的Route Handleとみなされる

    1. Dynamic Segment値の参照 (※第二引数にDynamicSegmentのparamsがある)
    2. Requestオブジェクトの参照(※第一引数にRequestオブジェクトがある)
    3. 動的関数の使用(cookies関数とか)
    4. GETとHEAD以外のHTTP関数のexport(POSTとか)
    5. Segment Config Optionsの指定( export const dynamic = "force-dynamic";)
  • 4.3 Route Handlerの良いところはNext.js自身が認証認可機能を備えたWebサーバーなので、画面側からのリクエストにユーザー情報を含めなくてもCookieを頼りにログインユーザーを参照できる
  • 6.4 Next.jsは「Incremental Cache」というデータ取得結果をキャッシュし、必要に応じて更新する組み込みキャッシュシステムが存在する。
    →デフォルトでオン。一年間有効。
  • fetch("url", { next: { revalidate: 60 * 60 }}) で有効期間変更
    このように有効期間を指定するRevalidateを Time-based Revalidation という
  • 6.6 cache関数を使うとfetch関数以外での「Requestのメモ化」ができる
    • Reactコンポーネント内では使わないこと
    • データ取得関数の引数はプリミティブ値でなければならない
import { cache } from "react";
...
export const getProfileFromScreenName = cache(() => {});
  • 6.7 fetch関数以外のデータキャッシュをしたい場合は、 unstable_cache を使う
    • fetchData: キャッシュされるデータを取得するための非同期関数
    • keyPars: キャッシュを一意に識別するためのキーとして使われる文字列の配列
    • options: キャッシュの動作を制御するオブジェクト
    • Reactコンポー内で利用可能
import { unstable_cache }. from "next/cache";
...
const data = unstable_cache(fetchData, keyParts, options)();

ここまでが8章で、9章以降は別の機会に書く