2023年のふりかえりとコミュニティの価値

23時に寝て2時に起きて4時半に起きて6時までゲームやって8時に起きた。朝からオフィスで発表資料を作ってた。

2023年ふりかえり

今年を振り返ろう!LT大会 に参加した。

昨日の夜に LT 資料を作ろうと思っていたものの、晩ご飯食べたら面倒になってそのまま家で休んでいた。朝からオフィスで作り始めたら徐々に興が乗ってきて想定した以上にちゃんと作ってしまった。やぎさんにレビューしてもらっていくつか気付きを得た。ちょうど レビューサービス あったら依頼したいなと思ったところだったのでレビューしてくれる人の存在に感謝した。この資料はまたなにか他の機会のイベントでも再利用しようと思う。カフーツさんの忘年会で発表してもよいかもしれない。

北海道から 西原さん という方が参加されていた。全国あちこちのコミュニティに参加して、そこに集う人たちの支援や盛り上げ、ひいては日本の IT コミュニティを活性化させて、世の中をよくしようといった取り組みをされている。これは私も共感するところで大半の人はコミュニティ活動に関心がない。それ自体は個人の好みで構わないのだけど、プログラミングを学びたいと考えているのに行動できない人たちというのが一定数いて、そういう人たちの受け皿として IT コミュニティで始めるのはよいことだと思う。西原さんはまさにそういう活動をされている。とても尊いことだと思う。

短時間にも関わらず、いろいろな話しをして私自身収穫もあったし、西原さんから学ぶことも多かった。企業のテックブログを読む会を毎週30分、社内外でやっていると話されていた。これは素晴らしいなと思ってすぐに反応した。歳のせいか、私は自分で勉強しなくなっていて、本も仕事も課題も積みまくりで、毎日どうしよう?と途方に暮れながら帰っておいしい晩ご飯を食べてゲームしている。そんな自分で勉強しない人向けにこういったコストなく、無理なく、続けられるイベントはありがたい。やり方を学ぶためにオンラインで参加してみようと思う。

コミュニティ活動の価値というのは、多くの企業もコミュニティを盛り上げようとやっていることから明らかに大きな価値があるにも関わらず、その価値を十分に明文化できていない。私自身、コミュニティからたくさんのモノや影響を受けているにも関わらず、うまく明文化できていないところがある。課題管理の研究の延長上でコミュニティの価値にも追究していきたいと考えている。

4年目の創立記念日

23時に寝て2時に起きて5時に起きて8時に起きた。税理士さんの件がもやもやしていてあまりよく眠れない。

ストレッチ

今週もとくに負荷のかかることがあったわけではないため、とくにどこも悪くなく、張りや疲労を感じることはなかった。トレーナーさんもどこも悪くないという前提の上で上半身の肩が前屈みになってしまっているために肩甲骨周りが硬くなりがちであるとアドバイスしてくれた。寒くなると上半身の硬さが目立つようになるのかな。いつも通りの体調管理の一環としてストレッチを受けた。今日の開脚幅は開始前155cmで、ストレッチ後158cmだった。

創立記念日

今日が会社の創立記念日。無事に4周年を迎えた。過去20年、3年以上1つの会社で働いたことがない私が自分の会社では4年を迎えられたことも少し感慨深い。今日は土曜日なので創立記念日が休日ではあるけれど、やはりオフィスでいろいろ作業をしている。

2年目に経営的に大きな失敗をやらかして、その反省もあるせいか、3年目は財務が安定してきた。この3月で 経営セーフティ共済 の掛金上限の800万円の積み立てを完了する。事例紹介 にも毎年1社新しい顧客が増えている。達人プログラマーではないが、毎年新しい顧客と取り引きをするというのもよいかもしれない。とはいえ、いまの取引先とのお仕事の契約が終了したら課題管理の研究や自社プロダクトの開発に取り組む。目安としては3年間を考えている。役員報酬をカットすれば財務的には5年はいける。意味もなく起業した私にとってはうちの会社は社会になにを還元するのかを問う期間として3-5年ぐらいを想定している。法人としてモラトリアムのようなものかもしれない。個人としても40代になって3年といった時間を自由に使えるというのはとても貴重な時間になるのではないかと考えている。自分で会社をやると年単位で時間を自由にできる。これだけでも起業する価値はあるのではないかと思う。

いまのお仕事でも課題管理の POC の1つとして実践している。想定通りと言えるほどの成果には至っていないが、時代背景や世の中の変化にも追随しながら私の頭の中にあるイメージと現実とのギャップを補正していく必要があるのかもしれない。

完璧ではないが、最善は尽くしている。うぬぼれるな、矢口

と赤坂先生に叱責されたい。

過去の創立記念日

税務相談のストレス

23時に寝て2時に起きて6時に起きた。晩ご飯食べてから作業するつもりが、疲れて寝てしまった。今日は権限管理のバグ修正したり、昨日の mongodb の調査を継続していた。

テックブログ公開

先日 テックブログレビュー を終えていた記事を公開した。

隔週の雑談

顧問のはらさんと隔週の打ち合わせ。今日の議題はこれら。

1ヶ月に1-2件ほど折りにふれて税務相談をする機会があったりする。税理士さんとは chatwork でやり取りしている。これがストレスになってきたのではらさんに相談にのってもらった。はらさん曰く、税理士さんの普通と it 業界のうちらとは業務の取り組み方への姿勢がまったく異なるため、最初の1年はストレスや不満がたまるのはあるという。士業という資格によって独占的に保護された業務のせいか、顧客に寄り添った対応をしてくれるわけではなく、質問の背景や意図を汲んでくれなくて質問に回答を返して終わりといった、チャットなのにメールのような一問一答のようなやり取りになっている。さらに回答が意図した内容ではなくて、追加で質問や背景の説明をしても、すぐにレスポンスが返ってくることはなく、数時間たってから回答が1つ届き、それも期待したものじゃないとさらに質問して、さらに数時間といったやり取りで5-6往復するのに数日かかるのがざらにある。はらさんがいうには税理士さんの回答を得るのに1週間ぐらいかかったり、問い合わせを数日放置されるのも普通とのことらしい。

先方も複数の顧客を相手にサポートしていて、やり取りに時間がかかることそのものは理解できるが、チャットのやり取りがコンテキストを理解しているようにみえなくてコミュニケーションが成立していない。例えば、当社への税理士報酬の支払いの対応について問い合わせたら次のような回答が戻ってくるだけ。

報酬の額と消費税等の額が明確に区分されている場合には、その報酬の額のみを源泉徴収の対象とします。

こんなことはググればすぐに分かるし、私も顧問報酬は原則として源泉徴収することを知っている。その上で税理士さん事務所の、当社への顧問報酬の明細について尋ねているのにこういった回答が返ってくるだけ。

インボイス対応もあるため、請求書に明細を書いて pdf で送ってくださいと依頼して2日間返信なく放置されている。税理士さん事務所の顧問報酬の明細や請求書について尋ねても即日に返信がこないような状況になる。はらさんが言うには税理士さんの対応とはそんなもんらしい。チャットでやり取りしている意味がないし、こんなググればわかるレベルのやり取りしかできないなら解約も検討している。もう1つ懸念に思っているのは本人がチャットで回答していないのではないか?と考えている。チャットなのに1問1答でしか返信がこないし、コンテキストが伝わっていないと感じることも多々ある。とてもベテランの税理士さんが答えているとは思えないコミュニケーションの齟齬がある。

ちょっと調べてみると、税理士は日本税理士会連合会に登録する必要があり、税理士試験に合格しても登録していない場合は税理士としての独占業務を行うことができないらしい。また税理士事務所で働いている人の中には無資格職員もいて税務補助をしている可能性もあるという。必ずしも税理士資格がなければダメだというわけでもない。無資格職員でも経験を積んで税務に詳しい人もいると思う。一方でチャットでベテランの税理士のふりをして職員が回答しているのであれば、それはそれで信頼関係や偽証などの問題がある。そこら辺も聞いてみようと思う。次の記事では無資格職員が税務相談をしている事務所もあると書いてある。

mongodb のレプリカセットのデプロイ調査

4時前に寝て6時半に起きた。1時過ぎまで作業して、帰って少しゲームして、うまく眠れなくてだらだらしていた。

mongodb のレプリカセットの調査

以前 mongodb でトランザクションを使うときにレプリカセットが必要 なことがわかった。他機能の開発途中だったので一旦後回しにしていたものを回収している。状況によってはメンバーに委譲してもよかったんだけど、私が遊撃で出張ってみることにした。実際に調べてみてコンテナの運用も考慮するとけっこう難しいことがわかってきた。

mongosh からは Replication Methods を使ってレプリカセットの操作ができる。これはユーティリティのようなもので mongodb としての低レベルのコマンド操作は Replication Commands になる。mongo-go-driver はレプリカセット向けのユーティリティを提供していないため、Replication Commands を RunCommand() の低レベル API を使って自分で実装しないといけない。

例えば、レプリカセットの初期化をするときは次のように replSetInitiate というコマンドを適切なパラメーターで呼び出す。あまりドキュメントで丁寧に説明されていないので試行錯誤でエラーメッセージをみながら実装することになる。とくにはまるのが mongod のサーバーは --replSet myrs のようにレプリカセットを指定して起動させるものの、初期化コマンドを実行するときはまだレプリカセットを設定していないため、レプリカセットを指定せず、且つ direct パラメーターをセットしないと mongod サーバーに接続できない。この微妙な設定を把握するのにはまった。これが正しい手順かどうかもわからないが、ググったりしているとフォーラムでそういったコメントが散見されたりする。おそらく mongosh の Replication Methods を使うと、クライアントからサーバー接続は裏方でよしなにやってくれるのでそっちの方が簡単ではある。

func (r *ReplicaSet) Initiate(ctx context.Context, config bson.M) error {
	client, err := r.connectDirect(ctx)
	if err != nil {
		return fmt.Errorf("failed to connect with direct: %w", err)
	}
	defer client.Disconnect(ctx)

	var result bson.M
	cmd := bson.D{{Key: "replSetInitiate", Value: config}}
	if err := client.Database(r.db).RunCommand(ctx, cmd).Decode(&result); err != nil {
		return fmt.Errorf("failed to run replSetInitiate(): %w", err)
	}
	log.PrettyPrint("completed to initiate", result)
	return nil
}

func (r *ReplicaSet) connectDirect(ctx context.Context) (*mongo.Client, error) {
	opts := options.Client().
		SetAuth(options.Credential{
			Username: r.config.User,
			Password: r.config.Passwd.String(),
		}).
		SetHosts(r.config.Hosts).
		SetDirect(true) // must be true
	return mongo.Connect(ctx, opts)
}

func InitSingleReplicaSet(
	ctx context.Context, cfg *config.MongoDB,
) error {
	rs := NewReplicaSet(cfg)
	initConfig := bson.M{
		"_id": cfg.ReplicaSet,
		"members": []bson.M{
			{"_id": 0, "host": "localhost:27017"},
		},
	}
	return rs.Initiate(ctx, initConfig)
}

さらに mongod サーバーを起動するときに --replSet--keyFile (認証が必要な場合のみ?) という2つのパラメーターを指定する必要がある。--replSet はレプリカセットの識別子を指定する。そして --keyFile は共通鍵を指定する。この共通鍵を生成するには次のようにする。

$ openssl rand -base64 756 > my-mongo-keyfile
$ chown mongodb:mongodb my-mongo-keyfile
$ chmod 400 my-mongo-keyfile

普通のサーバーインスタンスならすぐできることだが、コンテナの運用において面倒なのが owner とパーミッションを設定しないといけないところ。mongo のコンテナは mongodb ユーザーで起動するため、root でマウントされたファイルシステムには書き込みできなかったりして keyFile の配置をどう扱えばよいのかが難しい。docker hub の mongo の issues でもどうやって設定したらいいの?って議論が発散している。mongo 本体が公式のスクリプトや仕組みを提供していれば済む話しだけど、どうもそうではないみたい。だから泥臭い方法で自分でなんとかしないといけないようにみえる。

dockertest でもレプリカセットの設定について次の issue として登録されている。mongo のコンテナを使ったテストの場合、dockertest のレイヤーが挟まるのでさらにわかりにくくなっている。テストを動かすためにどういった設定が必要かは把握できたのでなにかよい方法を考えてコントリビュートしたい。

記事のレビューをしてもらえるサービス

疲れて18時過ぎにはお仕事を終えて、帰ってきて晩ご飯も食べずにそのまま寝てた。22時頃に起きてそれから晩ご飯を作って食べて、また夜に寝て6時に起きた。

テックブログレビュー

顧問のはらさんに昨日書いたテックブログのレビューをお願いした。一通り、調査した内容の範囲内で書いてはいるが、私はフロントエンドについて明るくないため、大きな勘違いや誤りを含んだ文章を書いてしまう可能性がある。本来テックブログは会社のブランディングにつながるものだが、誤った記事を書いてしまうと逆効果になってしまう。自社なら私の責任で済むが、他社のテックブログでブランドイメージを毀損するのは申し訳ない。

私が書いた記事をレビューしてもらえる有識者を、報酬をつけて広く募るみたいなサービスがあってもいいなと思えた。うちの会社がもう少し財務的に余裕ができたら、いくつかの分野の有識者と顧問契約して、技術に限定せず、いろんな分野で私の書いた記事のレビューをお願いできるような体制にしたい。余談だが、レビュー依頼して誰もレビューしてくれないというのも書いていて寂しい。よい開発文化をもつチームならそんなこと起きない。私を書いた記事をメンバーが率先してレビューしないという時点でまだまだうちのチームには改善の余地がある。

円形テーブル受け取り

ジモティーで「籐 ラタン 木製丸テーブル」を500円で購入した。使わないときは折り畳みできる。省スペースでよさそうにみえた。現物を受け取ってみて、使用感はあるが傷んではいない。まだ実家の離れで使うか、自分の部屋で使うかは決めていない。しばらく部屋で使ってみてその用途や感触で決めようと思う。問い合わせしてから2日後に近所のローソン前で待ち合わせをして受け取りできた。近所だと、引き取りの調整もしやすいし、軽いものだったので自転車の荷台に乗せて運んできた。これまでジモティーで6件の取引を行ったが、いまのところ、トラブルはない。

全員採用のためのテックブログ

2時に寝て6時に起きて9時前に起きた。寝るのがどんどん遅くなってきて生活のリズムが乱れてきた。

sveltekit に関するテックブログ執筆

先日の続き の続き。

調査は一区切りついたのでテックブログを執筆することにした。今日はほぼ丸一日記事を書いていた。本当はマネージャーの私が1-2週間も技術調査して、テックブログを一生懸命書くみたいなことをやるよりも、他に大事なプロジェクトの遊撃やマネジメントに時間を割くべきではある。一方でメンバーに課題に取り組むにあたり、設計をどうするのか?設計をするためには調査が必要であること、調査した内容をアウトプットする重要性などの模範を示したいという意図で書いた。そして、メンバーにも開発の隙間にテックブログを書くことを業務として指示しようと考えている。

なんのためにテックブログを書くか?という目的は、業務においては明確で採用のために書く。プログラマーが採用において協力できることは限られる。その中でもテックブログというのは費用対効果が高く、会社のブランディングにもつながり、よい開発文化を醸成することにもつながる。プログラマーの採用がとても難しくなった昨今「全員採用」というキーワードもよく聞くようになった。一見プログラマーは採用において無関係だし、実際にそういった業務をやらなくても咎められることはない。しかし、自分のできることで採用に協力したいと考えたとき、できることの1つにテックブログを書くというのは悪くない選択肢だと私は考えている。少なくともテックブログを書けない (書かない) 人たちにとやかく言われたくない。

svelte コンポーネントの実装は簡単

1時に寝て何度か起きて7時に起きた。日曜日の夜に業務スーパーへ行ったら生鮮系は売り切れているのが多かった。日持ちするようなものを購入した。呪術廻戦ゲーム の初心者ミッションをクリアしたのでゲームの時間を減らしていく。

kit/vite アプリケーションのデバッグ

先日の続き の続き。

ある kit アプリケーションの svelte コンポーネントから外部の kit アプリのコンポーネントやモジュールを埋め込むことができるかどうかを調査した。ドキュメントの Loading data をみながらコンポーネントを書いてみる。フロントエンドの開発はすべてメンバーに委譲しているので私はほとんど開発していない。ドキュメントみないとまったくどう実装していよいかわからない。

svelte コンポーネントをレンダリングするときにサーバー側で動かすのは +page.server.ts に、クライアント側で動かすのは +page.ts に実装する。今回の場合、外部の node.js プロセスに起動したサーバーに対してリクエストして index.html に相当するものを取得するのでサーバー側で取得したレスポンスから html を取り出して、それをコンポーネント側でレンダリングする。+page.server.ts は次のように実装する。

import type { PageServerLoad } from './$types';
import { apps } from '$lib/index';

export const load: PageServerLoad = async ({ params }) => {
	const res = await fetch(apps['kit-demo1'].entrypoint);
	const html = await res.text();
	return { html };
};

この html をクライアント側の +page.svelte から参照してレンダリングする。

<script lang="ts">
	import type { PageData } from './$types';
	export let data: PageData;
</script>

<div>{@html data.html}</div>

これで一応は意図した kit アプリケーションを埋め込むことはできるが、実際にはスクリプトなどはなにかが競合して動かないようだ。これは node.js から取得するスクリプトやスタイルなどが複数の kit アプリケーションで競合してしまうからではないかと推測する。

これが ssg ならば adapter-static を使ってビルドして、その成果物を static ディレクトリ配下に置くだけでそのまま動く。これは特別ななにかではなく、kit アプリケーションとして意図した振る舞いにはなる。これが出来て嬉しいことはあまり思いつかないが、想像した通りに動くかどうかの検証のために確認した。

次のリポジトリに調査した内容のサンプルコードを作った。ここまでの調査内容でまたテックブログを書いてみようと思う。

日本ナレッジ・マネジメント学会にオンライン参加した

0時に寝て5時に起きてゲームして9時に起きてまたゲームしていた。午前中は家でだらだらしていてお昼からオフィスで作業していた。

ナレッジ・マネジメントの年次学会

夏ごろに 日本ナレッジ・マネジメント学会へ加入 していた。学会誌を読んだりする程度しか活動できていなかったものの、年次大会をオンラインで視聴していた。

お昼からみたのでパネルディスカッションの後半と研究発表をいくつか視聴した。パネルディスカッションは ai についての議論をしていたが、私からみてとくに目新しい議論はなかったように思える。研究発表はいくつか関心のあるものがあって、自分たちの研究について話す前に先行研究としてこれやあれがあって、それらを踏まえて、自分たちはこういう研究をしていると聞ける。それまでの歴史や先行研究でわかっていることなども知ることができて、自分で一から調べるよりも調査時間を短縮できる。聞いていていくつかおもしろそうな論文もあった。また時間ができたときに調査するための issue として作っておいた。すでにそういった issue はたくさんあるのだけど。

おもしろかった発表内容の1つにリーダーシップ論は 野中郁次郎 先生ともう1人 (名前を聞き取れなかった) を除いたら、すべて米国からきたものだという。日本の地域性の高い問題においては米国由来のリーダーシップ論ではうまくいかないケースがあるといった話しをされていた。他にも実践知と叡智の違いとか、賢慮がどうこう、気付きの定義、技術と技能の定義といった用語の定義を明確にして議論をするといったところが学術研究とビジネスの大きな違いのように思えた。そして、学術研究においてもそれが定説としての認知度または実績がなければ、さまざまな仮説や研究があることから、自分たちはこの単語をこのように解釈して使っているとか、それぞれの派閥によって同じ言葉を別の意味で解釈していたりする。知識に関する用語が乱立して、結果的になにについて話しているのか、わかりにくくなってしまうといった雰囲気も感じることができた。野中先生の提唱する「フロネティック・リーダーシップ」には、実践知の起源として、アリストテレスが分類した3つの知識の一つ、フロネシスにあり「賢慮」とも訳されるらしい。実践知の要素には倫理が含まれていて正しいことを行うための判断も指摘されていた。来年ぐらいからうちの会社もこういった研究に時間を割いていきたい。また資料が公開されたふりかえりをしてみようと思う。

ミニカンファレンスみながら作業してた

1時に寝て7時に起きて朝からゲームしてた。お酒飲んだせいで眠りが浅かったような気がする。

ストレッチ

先週は前日に車であちこち移動して、長時間運転していたのもあり腰に負担がかかっていた。今週は東京出張だったものの、すねの外側の筋やふくらはぎの張りをやや感じた程度でとくに悪いところのない状態に戻ったようにみえる。寒くなってきたのもあり、上半身の肩周りの動きが堅くなっているとトレーナーさんは話していた。今日の開脚幅は開始前153cmで、ストレッチ後158cmだった。

ミニカンファレンスのオンライン参加

オンラインのミニカンファレンスを視聴しながらいつも通りの作業をしていた。あまりちゃんと聞いていたわけではないけど、要所要所でおもしろい発表もあった。

tinygo の中の人が自作キーボードに関する発表をしていて、その人は明石市に住んでいるので、明石市から三ノ宮にかけてイベントをやりたいと話されていた。go について話す相手がいると私も嬉しい。またなにかの機会でご一緒できればと思う。

SakeBash と LT 発表

2時に寝て4時に起きて5時に起きて7時に起きた。呪術廻戦ゲーム の初心者ミッションだけクリアしたらやめようと思って、なかなかミッションのレベルが高くて届かない。あともうちょっと。

SakeBash

神戸に acall (アコール) さんという会社があって、コロナ禍が始まる前に SakeBash イベントを開催されていた。2019-2020年にかけてのとき。それ以来ずっと開催されていなかったイベントを復活させたみたい。コロナ禍が acall さんの転機になったようで、オフィスのオートメーションに関するプロダクトを開発していて会社が大きくなっているらしい。

acall さんは go でプロダクト開発をしていると知っていたので神戸で go の話しができると思って次の LT 発表をした。発表したら向こうから声をかけてくれて go の話しができるかな?と期待したのだけど、あまりそういう雰囲気でもなかった。私はコミュ障なので知らない人に話すのが苦手。

acall さんの社員でしやさんという方が宮崎県から来られて、始めて LT 発表するということで応援しながら聞いた。

私の周りでも flutter を採用している人たちがいるので気にはなっていた。最近は flutter web というのも出ていて、flutter でフロントエンドとスマホアプリの両方に対応できそうにも思えた。次にスマホアプリ対応が必要になったら flutter を採用してみてもいいなと思えた。acall さんの制度をみていてユニークだなと感じたものに「いまあい旅費」がある。リモートワーク前提の会社だからこそ、こういった制度の価値が出てきたとも言える。一昔前は出張するのが当たり前だったのでこんな制度に意味はなかったが、リモートワークにより出張しないことが当たり前になったのでこういう制度設計になったんだと思えた。

いま、あいにいきます旅費

リモート勤務でオフラインで集うことが難しいメンバーと「会うこと」の価値を再認識し、オフィスで自社プロダクトのプラクティスを行うことで、製品のアップデートに貢献することを目的とした制度です。自宅から100km以上離れた神戸または東京オフィスに行くための旅費を支給します。(1往復分の交通費と宿泊費/ケ月)

イベントに acall の社員さんが10人ぐらい参加していた。勢いのある会社は会社イベントに参加する人も多くてよい雰囲気にみえた。宮崎県から来られていたしやさんの他にも埼玉県から来られていたとしさんという方もいた。他にも遠方から来ていた方もいたのかもしれない。

2次会

いまひとつ飲み足りなかったので三ノ宮.dev の仲のよい人たちと飲みに行った。RailRoad no.57 というバーでよい雰囲気のお店だった。またなにかの機会で使いたいと思う。葉っぱ (ハーブ?) がたくさん入っているはちみつカクテルがおいしかった。

そこで以前コミュニティで起業相談にのった方の経営について話題になった。創業してから1年弱かけて少し前にある web サービスをリリースした。その web サービスはコモディティで、すでに競合も数社ある。あまり新規のベンチャー企業にとって勝ち目のある分野だと私からはみえない。どうやってこの web サービスを黒字化するのだろう?と私は疑問に思っていた。創業者の相談に乗った方も今後の戦略について尋ねても明確な答えは返ってこなかったようでとても不安になったと話されていた。融資を受けたという話しを聞いていたのでしばらく会社を存続できるのかもしれないけど、作った web サービスを収益化しないと次の融資を受けられることはないだろうから先行きは厳しいのではないかと推測する。外部からみえる範囲内では今後の先行きは危ぶまれる。創業して1年もたつのに、若い開発者にすら心配されるような戦略しかもっていない経営者というのはちょっと想像できない。融資を受けることが出来たぐらいだから、起業や経営について相談にのってくれる人は周りにいるんじゃないかと思うんだけど、経営目線で厳しいことを言ってくれる人はいないのかもしれない。

複数の kit アプリケーションを共存させる仕組みの考察

1時に寝て3時に起きて5時に起きて7時半に起きた。なんとなく布団に入らずにベッドの上でそのまま寝てた。それでもあまり寒くはなかった。

kit/vite アプリケーションのデバッグ

先日の続き の続き。

kit アプリケーションを kit アプリケーションに埋め込むといったことができないかどうかの調査をしている。いろいろ調べている中で kit の discussions でもそういった議論はいくつか行われている。マイクロフロントエンドというキーワードも出てくる。

これらの議論をみていても kit の ssr はそれ自体が1つのアプリケーションとして動かすことを前提にビルドされているため、kit アプリケーション内に別の kit アプリケーションを埋め込んだり、一部のコンポーネントを外部のアプリケーションと組み合わせて動かすことはなかなか難しいようにみえる。マイクロフロントエンドのような思想で設計されていない。しかし、既存のアプリケーションを動かしつつ、少しずつ kit アプリケーションへ移行するといった運用をしたいという世の中のニーズも根強いことが伺える。

ここで svelte.config.js でエントリーポイントを置き換えるぐらいはできる。デフォルトは / がエントリーポイントになるのを /myapp に置き換えるには次のように設定する。relative は es モジュールのインポートを相対パスで行うか、絶対パスにするかの設定も変更できる。これもデプロイ先のインフラの都合にあわせて調整できるようになっている。この設定を切り替えられるのだからエンドポイントをハックすること自体はそう難しくないのかもしれない。

kit: {
  paths: {
    relative: false,
    base: '/myapp'
  },
}

さらに調査していて、adapter-node を使ってビルドするとデフォルトでは polka というアプリケーションサーバーが起動するコードが生成される。

function polka (opts) {
	return new Polka(opts);
}

const path = env('SOCKET_PATH', false);
const host = env('HOST', '0.0.0.0');
const port = env('PORT', !path && '3000');

const server = polka().use(handler);

server.listen({ path, host, port }, () => {
	console.log(`Listening on ${path ? path : host + ':' + port}`);
});

ここで任意のアプリケーションサーバーを使いたいという issue があって、それに対する回答から adapter-node のドキュメントにカスタムサーバーについて書かれていることに気付く。

アダプタは、ビルドディレクトリにindex.jsとhandler.jsの2つのファイルを作成します。index.js を実行すると (デフォルトのビルドディレクトリを使用している場合は node ビルドなど)、設定されたポートでサーバが起動します。

あるいは、Express、Connect、Polka(あるいは組み込みのhttp.createServer)に適したハンドラをエクスポートするhandler.jsファイルをインポートして、独自のサーバをセットアップすることもできます。

handler.js さえインポートすればそのまま動くことはデバッグしていて知ってはいたのだけど、この自前のアプリケーションサーバーを hooks を使って起動すれば任意のサーバーに置き換えできると issue の中で回答されていた。kit アプリケーションは1つのサーバーが1つのシステムとして動かすことを前提に設計されているが、サーバーを複数起動することでそれらを共存できるのではないか?と考えた。検証のために node.js から子プロセスを生成するには次のようなコードで起動する。些事だけど adapter-node の生成したコードが shell を介しないとポート番号を設定できなかったので shell: true もセットしている。

import { spawn } from 'child_process';

export function start_server() {
  console.log('called start_server');
  const opts = {
    shell: true,
    env: {
      ...process.env,
      PORT: '3005',
        ORIGIN: 'http://localhost:5174',
      NODE_ENV: 'production'
    }
  };
  const node = spawn('node', ['apps/myapp/build/index.js'], opts);
  node.stdout.on('data', (data) => {
    console.log(data.toString());
  });

  node.stderr.on('data', (data) => {
    console.error(data.toString());
  });

  node.on('exit', (code) => {
    console.log(`Child exited with code ${code}`);
  });
}

この node.js の子プロセスを起動する処理を hooks で呼び出すことである kit アプリケーションを起動したときに、別の kit アプリケーションを提供するアプリケーションサーバーの node.js プロセスも起動できる。そしてパスを解決できるようにするため、さらに es モジュールのインポートパスにあわせたプロキシを実装する。

import { start_server } from '$lib/index';

start_server();

import type { Handle } from '@sveltejs/kit';

export const handle: Handle = async ({ event, resolve }) => {
  if (event.url.pathname.startsWith('/myapp')) {
    if (event.request.method == 'GET') {
      return fetch('http://localhost:3005' + event.url.pathname);
    } else if (event.request.method == 'POST') {
      const data = await event.request.formData();
      const endpoint = 'http://localhost:3005' + event.url.pathname + event.url.search;
      return fetch(endpoint, { method: 'POST', body: data });
    }
  }
  const response = await resolve(event);
  return response;
};

これは kit のデモアプリが動くことを確認するためだけに実装したプロキシで GET/POST のリクエストを localhost:3005 に起動した node.js のプロセスへプロキシしている。これで2つの kit アプリケーションが1つのサーバーで共存しているかのように振る舞うことは確認できた。この延長上に私のやりたいことが実現できるかどうかをさらに調査する必要がある。