自作キーボード入門記

長らく沼の入り口でふらふらしてたんですが、遂に飛び込んでみました。自作キーボード沼。

キーキャップはNP PBT

1ヶ月コースかな…くらいに覚悟して臨んでみたものの、意外と半日くらいで完成させられました。お手軽!
一週間弱仕事で使ってみていますが、概ね満足しています。まだ殆どマップはいじっていないので、これからカスタマイズするのが楽しみ〜

経緯

分割キーボード自体は好きでMD650L→MD770と使っていたんですが、

  • 60%キーボードだと~の位置がしっくりこない
  • フルキーボードだとFキーとかあんまり使わないし全体的になんか大きい

とか色々気になるところがあって、いつか試せたらいいな、くらいに思っていたんですが、

気づいたら買ってました。

買ったもの

キーボード:7sPro

既に分割キーボード自体には慣れていたこと、(外出先には持ち歩かない想定から)Macの純正キーボードと行き来しやすい配置を求めていたこと、初心者におすすめらしいことから、ハッピーな配列のこの子に決めました。

キースイッチ:Durock Silent Linear Dolphin

とにかく静音のヤツ!だけ決めて遊舎工房に行って、最終的にスタッフさんにおすすめしてもらったもの。
めちゃめちゃ静か。体感、MD770静音赤軸の半分くらい。

キーキャップは選択肢が多すぎて、一旦手持ちのをはめてみて後で考えよ…と後回し。
→ その後NP Profileのキーキャップを買ってみました!背が低くて手がスムーズに動くようになりました。いい感じ〜
NP PBT Blank KEYCAPS SET 124 Keyskbdfans.com

本当はこれが欲しかったんですがすでに売り切れていた…
NP Profile 1950S Multi-Color Dye-sub Keycapskbdfans.com

作ってみて

想像の50倍簡単だった

「面倒な部分のはんだ付けが済んでいます」の意味が買って組み立ててみて初めてわかりました。基盤にダイオード付けるのすごい大変そう!
あんなに恐れていたはんだも意外とすんなりいってしまったのは、師匠が優秀だったから*1
7sProくらいのはんだ工程だったら、工房の工作室を2時間借りるでもよさそう。

唯一の難所はスタビライザー

ビルドガイドはスクリューインタイプ(ねじで止めるタイプ)で紹介されているんですが、同梱されていたのはスナップオンタイプ(ぱちっと嵌めるタイプ)でした。

  • 嵌めるのにそれなりの力が必要なのに、ルブでそこらじゅうべったべたになってしまって発狂
  • よーし嵌まったと思ったら片方が浮いていてキーキャップが取り付けられずに発狂

みたいなことが起きました…Kailhソケットのおかげでスイッチを無駄にせずに済みました…危なかった……

おわりに

というわけで、意外と簡単に入沼入門できた自作キーボード、皆さんもいかがでしょうか💁

キャップ、スイッチで一通り遊んだら、狭ピッチ+試作基板にも手を出してみたいな〜…
e3w2q.github.io
interestor2012.hatenablog.com

*1:ikeay先生ありがとう

メンバー全員でPHPerKaigi2022に参加したよ #phperkaigi

お前誰よ

あらたまです。Cake.jpというケーキ・スイーツの総合通販サイトを運営する株式会社Cake.jpでCTOをやっています。
Cake.jpには2021年の入社以降、エンジニア組織・デザイナー組織の立ち上げを行ったり、PHPからサーバーサイドKotlinへのメインシステムの移管を含むもろもろの技術選択をしたりしてきました。

過去にはYAPC::Asiaのボランティアスタッフ、YAPC::Japanの運営、コロナ以前はRubyKaigiやRailsdmに企業スポンサーとして関わったりと、コミュニティには学生時代からお世話になっていたので、Cake.jpとしてもPHPやKotlinのコミュニティに何らか寄与できたら…という思いがかねてよりありました。
カンファレンス参加経験があるメンバーが部に多くなかったこともあり、まずは空気感を知ってもらおう!ということで、業務委託を含むメンバーほぼ全員で一般参加してきました💪

リアル会場はやっぱりいいですね

オンライン参加でいいかな〜練馬遠いし…ともだもだしていたんですが、前夜祭クロージングの「あの空気感」に触発されパブリックビューイング券をポチ。

初日は社内では自分のみパブリックビューイング参加、かつPHPのコミュニティは初めましてなのでめちゃくちゃ緊張していたんですが、過去の別カンファレンスでお世話になっていた方々の顔がちらほら見えたので、即死を免れました🙏

アンカンファレンス最高!

トーク後の雑談(ありがとうございました)やスピーカーのトーク中の副音声ツッコミ、アンカンファレンス会場がむちゃくちゃ楽しかったです。
BASE川島さん・メルカリ安達さん後藤さんのアフタートークも大変勉強になりましたし、いろいろな意味で勇気がもらえました…。

2日目には、前回の Server-Side Kotlin Meetupで登壇した資料を使ってリプレイスの話をLTさせてもらいました。
PHPのカンファレンスでKotlinの話して怒られないですか」と軽く聞いたら「いいんですよなんでもありなんですから!」と返してくださったおかしょいさん、場を企画してくださったOystersの皆様、ありがとうございました!

惜しむらくは、オンラインでのアンカファレンス(ytakeさんの「イベントソーシングアンチパターン」)を見逃してしまったこと。会場迷子になってたら終わってしもてた…

ハイブリッドも最高!

リアル会場がありながらも「うっかり寝坊しても会場にたどり着くまでは生放送で追いかけられる」安心感がすごかった…*1
ハイブリッドだからこそ、DiscordやTwitterにもより感想が流れやすく、スピーカーにとっては反応がより見やすく、参加者は気軽に質問ができる環境になっていたのではと思います。
運用負荷はどえらかったと思うんですが、ぜひ次回以降もハイブリッドで…!と欲張ってリクエストしたくなる素敵なUXでした。

懐の広さ=インクルーシブを感じた

長く続くイベントってどうしても参加者が固定されがちというか、意図せず内輪ノリが強くなってしまうことがあると思うんです。
初参加でしたが、PHPerKaigiはそのあたりの塩梅がすごくちょうどよいと感じました。
もちろん「いつメン」の皆さんもいるんですが、長くコミュニティに携わっているそういった方々が、自社の若者を連れてきたり、登壇してもらったりという事例が多くあったからかもしれませんね。
参加者の皆さん、運営の皆さんの不断の努力なくしてこの風通しの良さは成し得ないと思います。敬意しかない!微力ながらその輪を繋げる一助になれればと強く思います。

というわけで、とても楽しいお祭りでした。ありがとうございました!また来年!

余談:アフタートークやってます

Meetyで特集が出ているのを見つけたのでのっかってみました!みなさんとお喋りしたい!お気軽にポチっとどうぞ!

meety.net

*1:ちゃんと起きられました、よかったですね

サーバーサイドKotlinでマルチモジュールをやる2022

(もともとあった)マルチモジュール構成を更にリファクタしたときにドハマリしたので備忘がてらメモ。

環境

  • JVM 16.0.2
  • Kotlin 1.4.31
  • Gradle 7.0.2

完成形

これを

├── appA
│   ├── presentation
│   ├── application
│   ├── domain
│   ├── infrastructure
│   └── src
└── appB
    ├── presentation
    ├── application
    ├── domain
    ├── infrastructure
    └── src

こうした

├── appA
│   ├── presentation
│   └── src
├── appB
│   ├── presentation
│   └── src
└── modules
    ├── application
    ├── domain
    └── infrastructure

パッケージの最適な配置がどうなるか手探りな状態でappAの開発を始めたけど、appBも順調に伸びてきて、そろそろ共通で使えるようにしようかな、みたいなところが動機。
CQRSの考え方を取り入れていて、module/domain, module/infrastructureの下は更にcommandまたはqueryでサブモジュールになっている。(フラグ)

tips: モジュールのおまとめにディレクトリを使う

親のディレクトリはモジュールでなくていいとき
マルチモジュールのすゝめのkts版

// settings.gradle.kts
val modulesDir = File("modules")
project(":application").projectDir = File(modulesDir, "application")

参照時は :modules:application ではなく :application となる点に注意

tips: モジュールお引越しは先にpackageをrenameする

新しくディレクトリを切って、そこにsrc以下をmvすると依存する側が自動で追ってくれないので、IntelliJのProjectペインから該当packageを右クリック→refactor→renameでパッケージを変更してからmvすると多少楽だった。
importが追随しきれていないこともあるので、その場合は一括置換をかけてrebuildした。

ハマった: ネストしたモジュールがimplementationできない

A: 事前に親モジュールを評価しておく

// modules/application/build.gradle.kts
evaluationDependsOn(":domain:foo")

dependencies {
    implementation(project(":domain:foo:query"))
}

ハマった: jar filenameのデフォルトが最下位のサブモジュール名

:domain:foo:query :infrastructure:foo:query と最下位モジュール名が同じ場合に、 docker compose build が以下メッセージで失敗した。

Entry BOOT-INF/lib/query-plain.jar is a duplicate but no duplicate handling strategy has been set.

IntelliJでのビルド、実行はできる状態。はて…?

ググるGradle7へのお気持ちtasks.Copy:duplicatesStrategy をEXCLUDEなりに設定しろというのが出てくるけど、EXCLUDE(重複した場合に取り除く)でもINCLUDE(重複した場合に上書きする)でもWARNでも、設定すると依存先のクラスがないと言われてエラーになる。

Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'XXX' defined in URL [jar:file:/app.jar!/BOOT-INF/lib/presentation-plain.jar!/path/to/Controller.class]

query-plain.jar を見てみよう

$ docker run --rm -it container-name:latest sh

/ cp app.jar tmp/
/ cd tmp/
/ jar xvf app.jar
/ cd BOOT-INF/lib/
/ jar tf query-plain.jar
# domain(or infrastructure)しかない!

諦めてモジュール名を変えてもいいけど、絶対それっぽい設定値あるでしょ…とドキュメントやbuild.gradle.ktsをコネコネしてそれっぽい着地を見る

// modules/infrastructure/foo/query/build.gradle.kts

tasks.jar {
    enabled = true
    // 生成されるjar fileの名前が一意になるようにする
    archiveBaseName.set("infrastructure-foo-query")
}

※ 直接文字列で指定しなくてもなんらかいい方法ありそう
※ Groovy式だと直接代入しているが、Kotlinで記法が変わった様子

参考

Spring Boot 2 (Kotlin) + Gradle Kotlin DSL でマルチモジュールを実現してみた - Qiita ServerSide Kotlin Appをマルチモジュール化する - ロコガイド テックブログ

CentOS7@Dockerで curl: (77) Problem with the SSL CA cert (path? access rights?)

3行で

  • CentOS7環境でhttpsなサイトにcurlすると curl: (77) Problem with the SSL CA cert (path? access rights?) で失敗する
  • OSのバグなので
  • touch /etc/sysconfig/64bit_strstr_via_64bit_strstr_sse2_unaligned すると通る

詳細

M1 Macでのdocker run時のみに該当の事象が起きて七転八倒していた。
証明書周辺の問題はわりによくある*1のでノイズも多く、原因を探し当てるのに大変苦労した🥺

ログはこんなで

$ curl -vvv https://github.com
* About to connect() to github.com port 443 (#0)
*   Trying 13.114.40.48...
* Connected to github.com (13.114.40.48) port 443 (#0)
* Initializing NSS with certpath: sql:/etc/pki/nssdb
* Closing connection 0
curl: (77) Problem with the SSL CA cert (path? access rights?)

CAfile, CApathの出力まで至らずにコネクションがクローズされているのが謎。
wgetは普通に通ることが確認できたので証明書には問題がないと判断し、インターネットの海に溺れていたところ以下を発見し解決に至った。

There was a curl bug in the distribution image of CentOS-7.

う、嘘だろ…

$ touch /etc/sysconfig/64bit_strstr_via_64bit_strstr_sse2_unaligned`
# 通るようになった!
$ curl https://github.com

なお今回の事象においては、同様のエラーメッセージで雑にググると出る

  • ca-certificates, opensslのreinstall
  • yum update, upgradeその他
  • update-ca-trust

なんかは関係ないので注意。んなわけないだろと思いつつも一応試したりして無駄足を踏んだ。

なおいまのところM1マシンでしか再現していないのも謎。でもとりあえず原因も特定できたし、もうゴールしてもいいよね?

*1:やれルート証明書がないだの、中間証明書がないだの、自己署名だと通らないだの

【読書メモ】モノリスからマイクロサービスへ

www.oreilly.co.jp

切り口

「必要十分な」ドメイン設計は有効

  • 正しい境界を設定することがマイクロサービス化の一歩
  • 「集約」の手法が有効
    • 実際のドメイン概念の表現
    • 一般的にライフサイクルを持つのでステートマシンとして実装できる
    • 外部からの不正な状態遷移要求に対してバリデーションができる
  • 集約の集合が「境界づけられたコンテキスト」
  • 集約もコンテキストも一種のサービス境界として機能する
  • あくまでも「切り出し」を目的としたDDDの適用であって、DDDをやることが目的でない点に注意
    • 分離の起点を判断するのに必要十分な情報を得られればよい
  • イベントストーミングの手法を取り入れるのもよい
  • 分離しやすさと、分離によって得られる利益で4象限を作ってもよい

マイクロサービスは手段の一つでしかない

  • ビックバンリプレイスの後には何も残らない
  • メリットとデメリットを見極めて小さく始める
    • FBが早く得られるほうが学びを得やすい・活かしやすい
  • マイクロサービスだけが正解ではない
    • モジュラーモノリスとか
    • そのままでは「分けないほうがいい機能」とか
      • 依存するサービス量が多い通知機能のようなものとか

進め方

コンウェイの法則

  • フロントエンドチーム、バックエンドチーム、DBAチームと組織が分かれているなら、アプリケーションもそのようになっている
    • これは新機能開発のときにすでにある層を横断して開発をしないといけないことを表す
  • 新しいプロダクトの形に合わせて組織も再編し、責任を再割当てするとよい

John Kotterの組織変革のための8段階のプロセス

by 企業変革力

既存システムのどこに痛みがあるのかを考える

  • 現在のペインポイントがどこで
  • どのような変化をもたらすことを望んでいるのか(ビジョン)
  • どのように目標に到達するのか(戦略)
  • をまとめる

本番投入しないとわからないことのほうが多い

  • どんなに小さい規模でもデプロイまでを1セットにする
    • たとえアクセスがこないとしても
  • ランチアウトした先で長く作業をしすぎないこと

あとは小見出しの通り

  • 一人でがんばってもうまくいかないのでビジョンと戦略をまとめて巻き込む必要がある
    • これは偉い人の口から言う
  • 小さく初めて短期的成果を得る・そこから学ぶ
  • 定期的なチェックポイントを設ける
    • 定量的・定性的
    • 定性面では「このPJ、うまくいってると思う?」とかでもいい
  • マイクロサービス化は「可逆的」
    • うまくいかなければ戻せばいい
    • 大きな投資を伴うような選択の場合は可逆性が失われるのでちゃんと判断しよう
  • 正解がわからない場合は「わからない」って言っていい
    • 解決したい問題と迷っているポイントが周りに伝わるということが利点
    • そのうえでその時点で正しいと考えている対処を始める
    • 時間(がもたらすスキルや経験)が解決の糸口になるかも

移行手順

後戻りの道を潰さない

  • リファクタを行うなら、リファクタ前後の実装を残して切り替えられるようにしておく
    • プロキシなどを用いる
  • 移行中のサービスがあるなら、完了するまではそこへの新しいフィーチャやバグ修正が入らないようにする
    • 移行とフィーチャの取り込みを並行してはならぬ
    • ロールバックしにくくなるので
  • とにもかくにも「インターフェイス

事例:Squareの「注文」

  • 客にとっての注文と
  • レストランにとっての注文と
  • 配送ドライバーにとっての注文
  • それぞれニーズが違うのに、ひとところにまとまってしまっており、デリバリー衝突が起きていた
  • 分離によってワークフローを分割しつつスケーラビリティと堅牢性を担保しようとした

まとめのまとめ

  • 十分な余裕をもって、合理的な意思決定をするために適切な情報を収集する
    • 誰かの真似をするのではなく、自分の問題とコンテキストについて考え、選択肢を評価し、前進しよう
    • 後から変更が必要になった場合には、変更を受け入れる姿勢を持ちながらそれを行うこと
  • マイクロサービスとそれに関連する技術やプラクティスの多くは段階的に導入することが重要
    • 他人の事例から学ぶべき教訓があるのは事実だが、自分のコンテキストでうまく機能するアプローチを見つけるには、時間をかけなければならない
    • その道のりを管理可能なステップに分けていくことで、アプローチをその都度適応でき、成功の可能性を最大限に高められる

DDD開眼した

新しいパラダイムを自身にインストールするときは、なんであれ少なからずブレイクスルーのような瞬間があると思うけど、今回はかなりのアハ体験を得たので拙いながらも書き記しておこうと思う。

まず読んだ本を雑にまとめたうえで、LL脳な自分が感じた壁とそれがどう取り除かれたかを書き散らかしていく。完全に自分向けのメモなので例とかコードとか置いてないけど、少しでも雰囲気が残せたら嬉しい。
なおご多分に漏れず肥大化したモノリスコードベースの分割したさがこれらのモチベーションの源泉で、モジュール化のアプローチと真逆に位置するRoRでなんとか責務を分解しようとした七転八倒*1の結果得た開眼なので、正攻法な理解ではないかも。

読んだ本

クリーンアーキテクチャ

Clean Architecture 達人に学ぶソフトウェアの構造と設計【委託】 - 達人出版会

前半はSOLID原則とデザインパターンのおさらい、後半はモジュール化とレイヤードアーキテクチャで責務を分割してっょぃアプリケーションを作ろうな!という本(雑)。
責務を分割するというのはどういうことなのか、インターフェイスとはなにか(その利点はなにか)、モジュール化による旨味はなにか、の答えがそれぞれ書いてある。

実践DDD

実践ドメイン駆動設計(ヴォーン・ヴァーノン 髙木 正弘)|翔泳社の本

いきなりエヴァンス本を読むと挫折するだろうとしてこちらを読み始めたものの、エヴァンス本で出たワードの解説は特にないので結局路頭に迷う。読了後なにもかもわからんが!?となったあなた、それは間違いではありません。
概念の説明と各論すぎる各論(Hibernateの使い方とか…)が入り混じっている・章どうしの循環参照が激しいせいで大変読みにくい。本書のリファクタリングを要求します。

とまあ、愚痴は置いといて、ひとまず1周流し読みする→インターネットで情報を補強しつつ試し書きする→もう1周読む、を繰り返すことで開眼に至った。

なお「ドメイン駆動設計 モデリング/実装ガイド」を間に挟むと開眼までのサイクルが早くなる。実践・実践DDD本といった内容。大変お世話になりました…。
ドメイン駆動設計 モデリング/実装ガイド - little-hands - BOOTH

モノリスからマイクロサービスへ

O'Reilly Japan - モノリスからマイクロサービスへ

モノリスに境界をもたせるのにDDDの集約やコンテキストを使うとよい、あくまでも分離のためにDDDのエッセンスを適用しよう、といったことが書いてあった。
境界を炙り出すのにイベントストーミングをやってみるというのは、今までは違う観点で機能やビジネスロジックを捉えることができてよいアプローチだった*2
一周してみて、一度アクセルを踏み切らないと(DDDをひととおり試してみないと)「エッセンスの適用」は難しいよと思っているところ。

壁たち

インターフェイスとモジュール

インターフェイスとは無縁の人生だった*3のでこの概念を取り入れるのに結構苦労した。「依存関係が逆転する」の意味と、main関数は究極の詳細であるということが腑に落ちたタイミングで分かった気になり、社のテックブログに寄稿した2エントリでの経験を経て乗り越えた。

Pure Kotlin+gRPC(protobuf)なWebアプリを作るまで - ロコガイド テックブログ
ServerSide Kotlin Appをマルチモジュール化する - ロコガイド テックブログ

当たり前すぎるけど、やはりインターフェイスとモジュールの概念のある言語で一通り書いてみるというのが大事。

DDDの戦略と戦術

実践DDD本の2-3章を読んでも戦略DDDとはなんぞやというのが分からないと思うけど、ざっくり理解をまとめると、「戦略」は設計思想なので、要件に対して責務を組み立て(ないしは分解)していく過程で使うといいという話だった。
LL脳は要件が与えられるとついついデータモデルから考え始めてしまうけど、これを押し留めて世界観を描くところからやると違ったアプローチが取れる。CQRSなんかが「違ったアプローチ」のいい例。
一方から入稿されたデータを他方で加工して閲覧させるようなユースケースの場合、このCQRS的なアプローチがとても有効なように思う。
「戦術」は立ち位置としてはクリーンアーキテクチャ(の実践)寄りというか、実際のコードをどういう粒度でモジュール化/カプセル化するかという話。前提(思想)がついてこないと片手落ちというのも頷ける。

クリーンアーキテクチャとDDDの折り合い

どちらも微妙に同じワードが出てくるからたちがわるい(エンティティとかサービスとか)。あとDDDでサービスが2種類出てくる(アプリケーションサービスとドメインサービス)のも厄介。「ドメイン駆動設計 モデリング/実装ガイド」にはこの混乱を避けるためにアプリケーション(サービス)層を「ユースケース層」として命名せよと書いてあった。頭いい…。
クリーンアーキテクチャでもDDDでもレイヤードアーキテクチャをベースに語られているけど、本当にDDDを実践するのならオニオン的アーキテクチャのほうがいいっぽい。
なおプロダクションコードでクリーンアーキテクチャをベースにDDDのエッセンスを少しずつ取り入れていってみたら、最終的に完全にDDDに飲み込まれた。中途半端な援用は混乱を招く。

エンティティと値オブジェクト

ここが最後の難関だった。端的に言うと「データストアから引いてきたデータをそのままエンティティにするのは間違い」ということなんだけど…。
正規化・非正規化のプロセスが挟まったとしても、データストアにあるデータをエンティティとしてマッピングする必要はなくて、エンティティと取るか値オブジェクト(プレーンなクラス)と取るかは「そのコンテキスト/ユースケースの中で状態を遷移させる必要があるか」で決めたらいいということ。そしてエンティティに紐付いたプロパティ(としての値オブジェクト)は、永続化に必要なデータ以外も持ちうるということ。
つまりユースケースの中で「エンティティを組み立てるのに必要な情報」と「エンティティを永続化するのに必要な情報」は必ずしも一致するわけではないと。これが腑に落ちるとドメインオブジェクトの表現力が格段に上がるなと実感した。
そしてここにたどり着くまでに随分時間がかかったことで「データモデル第一主義脳」の呪いが思いの外強いことにも気づくなどした。こわいね!

*1:この話ももう少しまとまったら記しておきたい

*2:なおこのアプローチはドメインエキスパートらとワイワイやることが推奨されているけど、いろいろあって1人でやりました 寂しい!

*3:golangがっつり書いたのもisuconくらいなもんで

assets:precompileでフロントエンドテストがコンパイルされたらtsconfigを疑え

環境

起きたこと

  • jest @testing-library/react あたりを使ってフロントエンドテストを書いた
    • もちろんdevDependenciesで
    • React Componentのテストの拡張子は xxx.test.tsx とした
  • テストファイルは app/javascripts 以下ではなく test ディレクトリを掘って配置した
  • production環境下の assets:precompiletest 以下のファイルもコンパイル対象になり、依存ライブラリを見つけられずにビルドがコケた

対応

webpacker.yml でも config/webpack 以下でもなく tsconfig.json 以下に以下を追加する

  "exclude": [
+    "test",
     "node_modules"
  ]

tsconfigのことすっかり忘れてwebpackerにexclude optionないじゃん〜と困ってしまった。せつない