はじめに

久々にプログラミングが面白い。 そのきっかけは、先月から使い始めた Emacs だ。

これまでも新しい環境に手を出しては、 1〜2週間で結局IntelliJに戻ってしまうことが多かった。 けれど、今回はもう1ヶ月以上Emacsを使い続けていて、これが驚くほどにしっくりきている。

自分が使っているのはDoom Emacs。

導入直後から、熟練者が作り込んだような環境で快適に使える。

プログラミングのあり方の変化

AIの進化で、プログラミングは「コードを書く」ことから「自然言語で指示をする」ことに変わってきている。 すると、一周回って「プログラミング言語特化のIDE」よりも「テキストエディタ」の方が相性が良いように思えてくる。

以前は「プログラミング=検索」だった。ブラウザで調べて、IDEでコードを書くのが自然な流れ。 IDEの補完機能は、余計な検索の手間を減らしてくれる便利さもあった。

ところが今は、AIが答えてくれる、代わりに検索してくれる。ブラウザを開く機会は激減し、作業も途切れない。

また、CLIで使えるCoding Agentが出てきてからは、CUI中心のワークフローがとても快適になった。 小さなツールをすぐに作れるし、複雑なコマンドもAIに頼れば容易に組み立てられる。

そう考えると、ターミナルで動くVimやEmacsのようなエディタは、AI時代の開発環境にむしろ親和性が高いと言える。

普段の使い方

最近の自分の開発スタイルは Tmux + Emacs の組み合わせだ。 プロジェクトごとにTmuxセッションを立て、その中でターミナル版Emacsを起動している。

「すべてをEmacsで完結させる」という原理主義的なやり方も試してみたけれど、自分には少し難しかった。 Git操作はMagitを使っているけれど、他はCLIツールを普通に併用するくらいが使いやすかった。

一方で、メモやOrg-modeの利用はGUI版Emacsに任せている。 GUIなら画像を表示できるし、別ウインドウでメモを開きながらコードを書けるので、整理や記録に向いている。

VimではなくEmacsを選ぶ理由

Doom EmacsはEvilモード前提で作られているところがあり、操作感はほとんどVimだ。 ならVimでいいじゃんとなりそうだけれど、あえてEmacsを選ぶ理由は、Org-modeにある。

メモもコードもTODOも、すべて一つのファイルにまとめられる。 アウトライナーとしても使いやすく、 数多のメモアプリやアウトライナーを渡り歩いた自分にとって、これは決定打だった。

ちなみに、この記事もOrg-modeで書いていて、uniorg を使って変換し、ブログ記事として公開している。

さらに、Doom Emacsは、 キーバインドや操作感が絶妙で、 ここまで練り込むのにどれほどの時間が費やされたのかと思うと敬意を抱かずにいられない。

加えて、Emacs Lispも面白い。 昔は入門書を読んでも実用レベルに届く気がしなかったが、 いまはAIに支えられて最初からもりもりEmacs Lispを書いてカスタマイズできる。 最近はAIの出力を教材代わりに学んでいる。

プログラミングの楽しさを思い出す

Tmuxでエディタとターミナルを切り替えながら開発していく。 その操作感そのものが「ゲーム的な楽しさ」を持っていることを思い出した。

振り返れば、最初にプログラミングに熱中していた頃は、Emacsを使っていた。 年月を経てまたEmacsに戻ってきたことに、不思議な縁を感じる。

最近の人気の開発環境は便利ではあるものの、どこか窮屈に感じる部分がある。 ましてやAIによって、「僕らの楽しかったプログラミングはいずこに?」という気持ちになっていた。

一方、Emacsにはカスタマイズの余地があり、創意工夫できる余白が残されている。 AIに仕事を奪われ続ける昨今だけれども、 Emacsで「創意工夫の楽しさ」を感じることに希望を感じている。

開発時に Mac でローカルサーバーを立ち上げて、モバイルデバイスで動作確認したい場合がある。

サーバーを 0.0.0.0 で立ち上げると、同一 LAN 内であればプライベート IP アドレス(192.168.0.0/16)を使用してサーバーにアクセスできるけれど、 セキュリティ設定などで、localhost のみが許可されていて、プライベート IP アドレスを使うことができない場合がある。

このような場合、iOS デバイスと Mac 間で SSH ポートフォワーディングを確立することで、 iOS デバイスから直接 localhost のアドレスを使って Mac のローカル環境にアクセスできるようになる。

接続手順

1. Mac の共有設定から「リモートログイン」をオンに変更

システム設定から共有設定を開き、「リモートログイン」をオンに変更する。 これにより、Mac に SSH を通じて接続できる。

Mac の共有設定

2. iOS デバイスに WebSSH (SSH クライアント) をインストール

App Store から WebSSH をインストールする。 このアプリを使うと、VPN-Over-SSH 機能により、 Chrome や Safari などの他のアプリで、ポートフォワーディングの設定を継続して利用できる。

WebSSH のインストール

3. ポートフォワーディングの設定

WebSSH を起動し、以下の手順でポートフォワーディングを設定する。

  1. Mac の共有設定で確認した以下の情報を入力
    • ホスト名
    • ユーザー名
    • パスワード
    • プライベート IP アドレス
  2. Port Forwarding の設定
    • 例:Mac 側で localhost:3000 を使用している場合は 3000:localhost:3000 と入力
  3. VPN-Over-SSH を有効化
    • この設定により、他のアプリ使用時も SSH トンネルが維持されます
WebSSH のポートフォワーディング設定

4. ポートフォワーディングの確認

ポートフォワーディングの設定が完了したら、iOS デバイスで WebSSH を開き、 ポートフォワーディングの設定が反映されているか確認。

設定の確認

最近、個人サイトを毎日少しずつ整えていってるのだけど、 だんだん改善してきてよくなってきたなと思う。 ここでは、個人サイトを持つことについて、 個人的に思っていることをまとめてみる。

個人サイトの良い点

まず、自分でサイトの発信内容やデザインを完全にコントロールできるのが良い。

noteでも記事を更新しているのだけど、 noteだと画面の折り返しの横幅が狭すぎて、なかなかコードが読みにくい。

あるいは、技術系の記事ならQiitaやZennでも投稿できるし、 たぶんその方が見てもらえるのだけど、技術記事以外を置くことができない。

個人的には、技術系の記事もそれ以外の記事もまとめて置きたい気持ちがある。 個人サイトは、なんとなく人となりがわかるような、そういう場所にしたいと思っている。

どうやって個人サイトを作るか

個人サイトを作るにはいくつかの選択肢がある。 まず、WordPressWixSTUDIO などの、ノーコードのサイト制作ツールを使う方法がある。 これらは手軽で多くの人にとって便利な選択肢だと思う。

だけど、自分でWEBサイトを手作りすることも選択肢の一つとして提示したい。 静的サイトジェネレーターを使うと結構簡単に作ることができる。

静的サイトジェネレータ

静的サイトジェネレイターで有名なものには、 AstroHugoJekyllなどがある。

このサイトは、Next.jsの静的サイトジェネレート機能で作っている。

デザインのテンプレートを使えば、最初からそれなりのデザインのサイトを作れるし、 カスタマイズしたい場合は、ソースコードレベルで自由にカスタマイズできる。

あと、静的サイトは、コスパがよい。 このサイトは、Cloudflare Pagesにデプロイしていて、 ドメイン代(約1000円/年)を除けば運営コストがかかっていない。

ただし、敷居が高いのは間違いない。 また、WordPressなどでプラグインですぐに作れる機能も いちいち開発が必要になってくる。

ただ、そういう作る過程を楽しみたい人には、おすすめと言える。

2024年10月23日

ゆっくり急げ

「ゆっくり急げ」という言葉がある。 これは、2000年以上前に初代ローマ皇帝アウグストゥスが好んで使った言葉らしい。

この言葉が、現代まで残っているのは、 人類はいつの時代も、たいてい急ぎすぎて失敗することが多いということなのだと思う。

日本においても、「急がば回れ」とか、「急いては事を仕損じる」のようなことわざがあるけれど、 「ゆっくり急げ」という言葉は、急ぐことも重要と言っているのが違いだ。

単にゆっくりやれば良いというわけではないという部分が、 絶妙な表現で個人的に結構好きな言葉だ。

「ゆっくり急げ」は、開発現場でも役に立つかもしれない。 プログラミングは、急ぎすぎても、ゆっくりすぎても失敗する。

プログラミングでは「ゆっくり」は以下を意味するかもしれない。

  • 読みやすいコードを書く
  • ユニットテストを書く
  • リファクタリングを行う
  • ドキュメントを整備する
  • CI/CDを導入する

その意味では、「急げ」は次のように解釈できそうだ。

  • 早めにリリースする
  • 早めにユーザーのフィードバックをもらう
  • 必要最小限な機能を実装する
  • 無駄な抽象化を行わない

コードの品質もリリース速度もどちらも大事というのが 「ゆっくり急げ」ということになる。

僕はある程度プレッシャーがなければ、「ゆっくり」作業する方に偏りがちだけれど、 ただ、時間的なプレッシャーがかかると、品質どころじゃなくなって、急ぎすぎてしまうこともある。

余裕がないときこそ「ゆっくり」、余裕があるときは「急げ」の精神で、 普段から「ゆっくり急げ」を意識したいと思う。

昔から思っているのだけど、いいものを作れるかどうかに必要な要素として、作るのにかかる膨大な時間を受け入れているかどうかがあると思う。

もちろん知識やスキルの要素もあると思うのだけど、膨大な作業時間を本心で受け入れているかどうか、というメンタリティの要素が実は大きいのではないかと思う。

昔、僕は以下の動画を見てかなりのショックを受けた。この方は半年間迷路を書き続け、最後に燃やしてしまう。

この動画はほとんど精神力との戦いだけど、こういうことが実行できるかどうかの精神力が、ものづくりには必要なのだと思うようになった。

自分の場合で考えてみると、プログラミングを覚えたてのときは、バグでなかなか動かないみたいなことがあると、1日無駄にしてしまったように感じていた。最近はこの程度はもはや何も思わなくなったし、なんなら1日で終わればラッキーと思うかもしれない。経験を積んだ結果、作業そのものに対する苦痛に鈍感になってきたように思う。

そうなると、何かものを作るのに半年とか1年とかの期間がかかるのは普通という感覚になってくる。そういう感覚になって、はじめてそれなりに大きなものを作れるようになったきた。僕の場合は10年くらいやってきて、ようやくそう思えるようになったかもしれない。

だけど、たまにこの痛みに最初から鈍感な人がいて、そういう人は、いきなり超大作を作り上げてしまったりする。そういう人は色々な分野で、何を作ってもすごいものを作り上げてるイメージがある。

そういう作業量に対する先天的な苦痛への耐性が、ものづくりの才能なのかもなあと思う。

フリーランスとして働こうと決意したものの、 退職時点ではまだ具体的な案件が決まっていなかった。 なので、本当にやっていけるかどうか確かではなかったのだけど、 「なんとかなるだろう」と深く考えずに退職を決めてしまった。

その後、カンファレンスで知り合った方や、昔の同僚から仕事を紹介してもらい、 無事にフリーランスとしての仕事をスタートすることができた。

これまでやってきた仕事は、ほとんど知人経由か、仕事で知り合った人経由なので、 フリーランスとして活動するためには、どこかの会社に入って仕事のつながりを作っておくのは、結構重要なのかもしれない。

• • •

フリーランスになってからは、なんでもやります的なスタンスだったので、 色々な案件に関わることができた。

例えば、

  • React Nativeでのモバイルアプリ開発
  • Flutterのモバイルアプリ開発
  • Railsバックエンドの開発
  • PythonでのAPI開発
  • GoでのバックエンドAPI開発
  • Next.jsでのWebサイト開発
  • TerraformでのAWSインフラ構築
  • 機械学習・AI関連の機能開発

などがあった。

最初の方は調査とアドバイスがメインの技術支援の仕事もしたけれど、 やっぱ自分でコードを書く仕事の方が面白いし、貢献できるので最近はやっていない。

フリーランスの仕事は会社員の業務と違って、ほぼ全ての業務時間がプログラミングなので、 それが自分には合っているなと感じている。

• • •

フリーランスでの仕事は思った以上に順調な一方、 元々の目的の個人プロジェクトが後回しになりがちだった。

元々は週3日を業務、残りの週4日を自分の開発と考えていたのだけど、 いつの間にか週7日稼働しているみたいな状態になることもしばしばで、これがこの2年間の大きな反省点だった。

まず、僕の場合、単純に開発スケジュールが厳しくなってきたりすると、 ついサービス精神で稼働を増やしてしまいがちということ。

また、個人プロダクトの方は、収益が発生するかどうかもわからないし、発生するとしても未来の話なので、 現実的に収入が得られる仕事の方が優先度が高くなりがちだった。

• • •

ソフトウェアの個人開発プロジェクトは、途中で頓挫したり、あんまり良いアイデアが思いつかなかったのだけれど、 最近は、ハードウェア制作を重点を置いていて、ミニロボットの制作を目標として、こちらで活動している。

また、去年から関わっているミーアのプロジェクトに関わっていて、 こちらもハードウェアプロダクトの開発を行っている。

今後はハードウェア開発もできるソフトウェアエンジニアとして、活動していこうと思っている。

僕は元々プログラミング自体が目的みたいなタイプだったので、 実際のアプリ開発、サービス開発にあまり興味がなかったのだけれど、 社内ハッカソンに参加したことがきっかけで興味を持つようになった。

ヤフーでは毎年Internal Hack Dayという社内ハッカソンがあり、 社内だけの人間で、2日間かけて自由に新しいプロダクトを作って発表するイベントがある。 僕は退職するまで毎年このイベントに参加していた。 当時、ヤフーでの最大の業務はハッカソンに参加することですとチームミーティングで冗談で言っていた気がする。

僕の中では、プログラミングは一人でやるか、もしくは大人数で取り組むことが多かったのだけど、 ハッカソンでは、小さいチームを作って、自分たちのアイデアを実現するものなので、 普段やっていることとは違う楽しさがあった。

3回目の挑戦では、良い仲間に恵まれ、類似画像検索のサービス「ウユニ塩湖に行きたいんやが」を作り、 入賞することができた。 これは海外の観光スポットの画像に似た近場の観光地を提案してくれるサービスで、 予想以上に高性能なサービスが完成し、ヤフーのテックブログでその取り組みが紹介されたりもした。

ハッカソンが終了した後も、同じチームでさらに何か新しいものを作りたいという話になり、 ソフトバンクグループのソフトバンクイノベンチャーという新規事業立ち上げのためのプログラムに応募し、 そこに出向して新規事業に挑戦することになった。

• • •

ソフトバンクイノベンチャーでは、「BAKOON!」というエクササイズサービスを作っていた。

BAKOON!

これは、アプリ上にライブ配信の機能を実装して、アイドルと一緒にエクササイズをするサービスで、 モーションキャプチャ機能などを使って運動量を可視化したり、スタンプでコミュニケーションを行うサービスだった。

BAKOON!はReact NativeとFirebaseを活用したサービスで、技術的な取り組みは以下の書籍で詳しく紹介されている。

このサービス以前では、これまで本格的なモバイルアプリ開発の経験がなかったので、 モバイルアプリ開発、WEBフロント開発も初めての経験だったのだけれど、 基礎から学びつつ、なんとかリリースまでこぎつけることができた。

あと、エンジニアも含めてプロダクトについて議論したり、 マーケティングやビジネス面のKPI部分の設計なども考えていたので、 単純にプログラミングだけでなく、プロダクト全体を考える経験ができた。 ビジネスやマーケティングは思ってた以上にデータ分析的で、自分の数学的知識とかを活かせるなと思った。

リリースして1年くらい運用し、いろいろ頑張ったのだけれど、結局サービスはクローズしてしまった。 ただ、0→1でサービスを作った経験によって、僕がプログラミングでどういうことをしたいのかという部分が変化したと思う。 プログラミングそのものの楽しさよりも、アイデアを自分で決め、それを実現するものづくりが楽しいと感じるようになった。

自分でプロダクトを作るという新たな目標が生まれ、 開発時間を捻出するために、フリーランスとして独立することになった。

新卒で入社したヤフーには大きなデータサイエンスの部門があり、 僕は入社時の面接時にはその手の話しかしていなかったので、てっきりデータサイエンス系の部署に配属されるものだと思っていたのだけど、 気づいたらヤフーショッピングというサービス部門の検索チームに配属されていた。 希望の配属先ではなかったのだけれど、振り返ってみるとここでサービス開発に関われたのは良かったと思う。

当時は、検索APIの機能追加や検索エンジン自体の運用が主な業務だった。 コードベースが大きくて歴史もあったため、まずはそのコードを読み、理解することから始めた。

それまでソフトウェアの設計本などで語られる内容について、イマイチピンときてない部分も多かったのだけど、 実際に大きなシステムを見ることで、なるほどこういう複雑なシステムに対処する話だったんだな、というのがわかった。

また、社内で使われていた技術の多くは、外部のOSSよりも性能が高くて驚いた。 たとえば、今はOSSになっている全文検索エンジンのVespaや NoSQLのmdbmなどは、かなり高性能だった。 OSSで人気のものが性能面でベストだと思っていたけれど、実際は内部のツールの方が優れていたことを知った。

あとは、チーム単位でAPI、サブシステムを管理していたので、 機能開発にはチーム同士の連携が必要だった。

最近は多くても2~3人のチームで、調整などの仕事はほとんど発生しないのだけど、 当時は、新機能開発時やAPIのクローズ時に他のチームお願いしに行くことがよくあった。 こうした組織的な開発は、プログラマとしての社会人経験として結構大事かもしれない。

• • •

また、当時、開発だけでなく運用も担当していたことは結構重要な経験だったように思う。

開発と運用が分離していると、それぞれの都合の押し付け合いみたいになってしまうし、 バランスを取るにはどちらも担当するのがベストだと思うようになった。

仕事で思い出に残ってるのは、年に1度のセールの日を迎える前に、 リクエスト数の予測、サーバー台数の調整、負荷試験の実施など、色々対策を行うのだけど、 こういう経験は、大規模なサービスならではだったなと思う。

最近は小規模なサービス開発が多いので、こういうことはあまりやってないけれど、 いつかこの経験が役に立ちそうに思う。

• • •

会社に入ってからはJetBrainsのIntelliJ IDEAを使うようになった。 大学時代はEmacsを使っていたが、大学の後半ではVimをメインで使っていた。 しかし、実務で対応すべき言語が増えるにつれ、だんだん状況が変わった。

業務では、PHP、Perl、Java、Go、Pythonなど、多くの言語を使う必要があり、 セットアップが不要で幅広い言語に対応できるIDEを好むようになった。

業務外の時間では、ハイパフォーマンスなシステムに興味があり、 C++や効率的なプログラミングに関する技術を学んでいた。 特に、将来的にC++を使うようなプロジェクトに関わりたいという思いがあったので、 当時は業務とは別に自作プログラミング言語やインタプリタを作ってみたりしていた。

結局当時仕事でC++を使うことはなかったのだけれど、 最近ではファームウェアの開発でC++をガッツリ使っているので、この学びが意外にも役に立っている。

プログラミングを初めて体験したのは確か2008年の高校生のときだった。 当時、自分のデスクトップコンピュータを手に入れて、Ubuntuをインストールして遊んでいた。 シェルでコマンドを打ちながらコンピュータを操作するのが新鮮で、当時はとても楽しかった。

シェル経由で操作すると簡単に自動化でき、Linuxコマンドを極めればコンピューターを使いこなすことができるだろうと思っていた。 そんな感じでコマンドを並べてるだけだけど、僕にとってのプログラミングは、まずはシェルスクリプトから始まった。

その後、シェルスクリプトで遊んでいるだけではなく、本格的なプログラミングを覚えようと思った。 「プログラミングを覚えるならC言語が良い」というネットの評判を見て、 「しろうとクマ君とC言語の授業」という本を購入した。

一通り読んで「わかった気」になったけれど、今思えば当時はFizzBuzzですら怪しいレベルだったかもしれない。

その次に手を出したのが、Windows向けのゲームプログラミングの本だった。

この本を読んで、横スクロールアクションゲームをC言語で簡単なゲームプログラムを書いた。 ただ、ほぼ写経だったので、かなり無理やり動かしていた記憶がある。 一応動いていたが、十分に理解していたとは言えなかった。

そんなこんなで高校生活を過ごしていたのだけれど、大学では情報系か電気・電子系のどっちに行くのかを迷っていた。 結局どちらも学べる、電気通信大学の情報・通信工学コースに進学した。

• • •

大学1年生では、C言語のプログラミングの授業があった。 高校時代の知識はもう結構忘れていて、また1から覚え直しだった。 プログラミングの授業では課題を終わらせるのが異様に早い人がいて驚きだった。 大学にはすごい人がいるもんだなあと感心していた。 あと、授業ではプログラミングにEmacsが必須だったので、いつしか熱心なEmacsユーザーになっていた。

その後、プログラミングをもっとできるようになりたいと思ってPythonを学び始めた。 今でこそ人気No. 1の言語だけれど、当時は日本でPythonがまだあまり流行っていなくて、 周りがあまりやっていなかったことが、逆に魅力的に感じた。

Pythonを学ぶためには、はじめにみんなのPythonという本を読んだのを覚えている。 Pythonを学んで、ようやく実用的なプログラムを書けるようになった。

大学では、バイトもせず、友達とも遊ばず、家にこもってプログラミングをしていた。 一人暮らしをしていたので、何日も誰とも会わずに家にこもっていた日々だったので、 このままで自分の将来は大丈夫なのかと感じていた。

Pythonが一通りマスターした後は、低レイヤーの知識を身につけたいと思い、低レイヤー関連の本を読んでいた。 印象に残ってるのが、「Hacking: 美しき策謀」という本で、主にセキュリティ関連の内容なのだけど、この本を読んで、C言語をようやく理解したように感じた。

あとはアセンブリ言語も学んでおくべきだと思い、アセンブリ言語も学んでいた。

他にもたくさん本は読んだと思うけど、当時はアプリケーションの開発にはあまり興味がなかったので、 あまり実用的な本は読んでいなかった。

そもそも当時は、プログラマとして就職しようとも思ってなくて、プログラミングは完全に趣味のつもりだったので、 実用性はあまり気にしていなかった。

• • •

大学4年生になると、Brain Computer Interface(BCI)の研究室に入った。主にMATLABやOctaveを使ってプログラミングをしていた。 視覚刺激によって誘発される脳波を使ったBCIの実験で、PsychoToolboxというフレームワークを使ってPC上で視覚刺激を与えるプログラムを書いていた。

BCI

当時は、Pythonが一番書けたので、実験アプリケーションをPythonで少し無理をして頑張って作っていた。 ctypesを使って脳波計のDLLをPythonから操作するのだけど、 使い方間違えるとブルースクリーンでPCがクラッシュすることもあり大変だった。

最終的に、実験用のアプリケーションはMATLABに落ち着き、Pythonは分析や可視化に使用するようになった。

ちょうどその頃、機械学習が注目され始め、自分もその流れに乗ってscikit-learnを使って機械学習を学び始めた。 ビショップの「パターン認識と機械学習」という黄色い本も、ゼミでこれが読みたいと言って読んでいた。

その後、データ分析もできるようになりたいと思い、Pandasを使ったデータ分析を学んだ。Pandasのスキルは、今でも時々役に立っている。

• • •

修士1年になると、アルバイトやインターンで実際に仕事としてプログラミングを始めた。 検索の改善やランキングの予測モデルを作ったりしていた。 当時は、現在のような大規模な言語モデルはまだ存在しておらず、 さまざまな手法を使ったFeature Engineeringを試し、改善していくのが面白かった。

インターンで実務に近いプログラミングをやっていると、 これまでの積み重ねで結構スキルがある程度あることが分かってきた。

大学院での研究もそこそこ成果が出せていて、研究のようなひたすら突き詰めていく作業は性に合っていたので、 博士課程に進もうかとも考えていたけれど、 インターンではほとんど実務に近い内容で、これは仕事にしても楽しそうと思った。

そんなこんなでプログラマとして就職したのであった。

最近、Goのプロジェクトで大規模なリファクタリングを行ったのでまとめる。

元々このプロジェクトでは、以下のようにフラットにGoファイルを置く構成になっていた。

- cmd/api/main.go
- user.go
- user_db.go
- user_db_test.go
- user_handler.go
- user_handler_test.go
- server.go

しばらくはこれで問題なかったが、1年ほど開発が続くと、だんだんとプログラムのファイル数が増えていき、プログラムの見通しが悪くなってきた。

また、同じパッケージに全て置いているため、循環参照の問題は発生しないが、名前が衝突して使用できなくなる問題が発生するようになった。

整理方法を検討した結果、今回は ルートにドメインパッケージを置く構成 を採用した。

ルートにドメインパッケージを置く構成とは

ルートにドメインパッケージを置く構成 とは、 リポジトリのルートにドメインパッケージを作り、依存関係に応じたサブパッケージに実装をまとめる構成のことで、 以下のBen Johnson氏による「Standard Package Layout」の記事で紹介されているパッケージ構成を参考にしている。

記事中では、「Standard Package Layout」と紹介されているものの、 この名前だと本筋と違う議論になってしまいそうなので、 この記事では少々長いが「ルートにドメインパッケージを置く構成」と呼ぶことにする。

たとえば、MySQLを使ったバックエンドAPIを想定すると、 ルートドメインパッケージでは、以下のような構成となる。

- cmd/api/main.go
- mysql/
   - user.go
- http/
   - user_handler.go
   - server.go
- user.go

ドメインパッケージのファイルはGitリポジトリのルートに必ず置く必要はないので、 リポジトリのルートに直接Goファイルを置きたくない場合や、Gitリポジトリ名がドメインを表す名前ではない場合は、 ルートドメイン用のディレクトリを作ってコードを置けば良い。

仮にプロダクト名が myapp だったとすると、ドメインパッケージが myapp となる。

- cmd/api/main.go
- myapp/
  - mysql/
    - user.go
  - http/
    - user_handler.go
    - server.go
  - user.go

myapp/user.go は以下のように定義され、プログラムの各所からこのモデルが使われることになる。

package myapp

type User struct {
    Name string
    Age int
}

type UserService interface {
    CreateUser(uid string) (*User, error)
    GetUser(uid string) (*User, error)
    UpdateUser(uid string) (*User, error)
    DeleteUser(uid string) error
}

mysqlパッケージでは、 myapp.UserService インターフェースを満たすように具体的な実装を書く。

package mysql

type UserService struct {
		db *sqlx.DB
}

func (s *UserService) CreateUser(uid string) (*myapp.User, error) {
	// MySQLを使った具体的な実装を書く
}

httpパッケージでは、直接mysqlパッケージの mysql.UserService は使わず、myappパッケージのインターフェースを使うようにする。

package http

type UserHandler struct {
    userService myapp.UserService
}

今回は、mocksディレクトリにgomock経由でルートパッケージのモックを生成し、 サブパッケージでルートパッケージのサービスに依存する場合は、mocks以下のモックを使うようにした。

package myapp

//go:generate mockgen -destination=./mocks/user_service_mock.go -package=mocks . UserService
type UserService interface {
    CreateUser(uid string) (*User, error)
    GetUser(uid string) (*User, error)
    UpdateUser(uid string) (*User, error)
    DeleteUser(uid string) error
}

mysqlパッケージはMySQLに関連する処理に専念し、 httpパッケージはMySQLへの依存を避け、httpサーバーの機能に集中したテストを書くことができる。

- cmd/api/main.go
- myapp/
  - mocks/
    - user_service_mock.go
  - mysql/
    - user.go
    - user_test.go
  - http/
    - user_handler.go
    - user_handler_test.go
  - user.go
  - user_test.go

実際のプロジェクトでは以下のような構成となった。

- cmd/api/main.go
- myapp
  - http (HTTPサーバー)
  - grpc (gRPCサーバー)
  - rdb (MySQL)
  - s3 (Amazon S3)
  - dydb (DynamoDB)
  - iot (AWS IoT)
  - ssm (AWS SSM)
  - fcm (Firebase Cloud Messaging)
  - ... (省略)

これらのサブパッケージでは、dockertestやLocalStackを使って可能な限り実際に近い環境でテストを行なっている。

domainというパッケージ名を使わない理由

ドメインモデルは、ルートに置かずに domain パッケージを作っても実際は問題ない。

- cmd/main.go
- domain/
  - user.go
  - user_test.go
- http/
  - user_handler.go
  - user_handler_test.go
- mysql/
  - user.go
  - user_test.go

この場合、ドメインパッケージ内で定義された型を使うには、 以下のように domain.XXX の形式を用いる。


func main() {
    user := domain.User{}
}

ルート(myapp)に置いた場合は、以下のように myapp.XXX の形式となる。


func main() {
    user := myapp.User{}
}

このようにパッケージ名がプロダクト名になっている方が、よりこのデータ型が プロダクト全体で使われることを意識した名前づけになっていると思う。

他のプロジェクト構成との比較

クリーンアーキテクチャなど、何らかのアーキテクチャを参考にしたプロジェクトでは、 そのアーキテクチャのレイヤーに基づいて命名されたディレクトリが作られることがある。

- cmd/main.go
- domain/
- infrastructure/
- presenter/
- repository/
- usecase/

こういう名前付けは、特定のアーキテクチャの文脈や用語を理解する必要があり、馴染みのない人には分かりにくい。

上述のdomainという名前よりプロダクト名を使った方が良いという部分と同様で、 できるだけ直接的な命名をした方がプログラムが分かりやすくなると思う。

今回のサブパッケージを依存関係ごとにまとめる構成は、特別な知識がなくても、どこに何を置くべきかが分かりやすい。

- cmd/main.go
- mysql/
- http/
- user.go

まとめ

Goのプロジェクトでルートにドメインパッケージを置く構成にリファクタリングした件についてまとめた。 予備知識がなくても理解しやすく、テストしやすい設計を目指しやすい構成だと感じた。

26件中 1 - 10件を表示