Notion APIのセットアップして、Rubyを使ってページの本文やテーブルビューの一覧を取得する

Notionには、無料で使えるAPIがあります。

今回は、Notion APIのセットアップから、Rubyのfaraday gemを使ってリクエストを投げ、ページの本文やテーブルビューの一覧を取得するまで、を記事にしてみました。

使用したバージョン

  • Ruby: 4.0.3
  • faraday: 2.14.2

1. インテグレーション作成

この作業はワークスペースのオーナーしか実行できない点に注意してください。

www.notion.com

ここでやるのは、権限設定とAPIキーの発行、というイメージです。

「インテグレーション」という公式ドキュメントの記事タイトルですが、画面には「コネクト」という名前になっていて、少し混乱しましたが、おそらく同じことを指していると思います。

今回は以下で作成してみました。

  • コネクト名: TestConnect
  • 認証方法: アクセストークン
  • 機能: 「コンテンツを読み取る」のみチェック
  • ユーザー機能: 「ユーザー情報なし」

2. ページに接続

この作業はワークスペースのゲストアカウントだと仮にフルアクセス権限を持っていても実行できないようです。
また、ページに対して編集権限ではおそらく実行できません。
(この辺りの権限周りについては有料プランなど、色々設定がありそなのでやや曖昧です。もし設定できない場合は疑ってみると良いかもという意味で記載してみます。)

www.notion.com

作成したコネクト(インテグレーション)で読み取りたいページに移動します。

ページ右上の3点リーダーから、「接続」にマウスオーバーすると、先ほど作ったコネクトが表示されていると思います。

これを接続することで、API接続の準備ができました。

3. 実際に読み取れるかを確認

Notion APIのページには、使用できるエンドポイントが記載されています。

developers.notion.com

先ほど作成したトークンがあれば、例えば「Retrieve a page」のページで「Tri it」から、接続確認ができます。

4. Ruby(faraday)でリクエスト

先ほどの「Try it」にもRubyのリクエストサンプルコードがありますが、Net::HTTPを使う方法でした。 今回は、faradayを使ったサンプルコードを紹介します。

page_id = "36e7e3c5cd238088b01ae2e5464f0402"
url = "https://api.notion.com/v1/pages/#{page_id}"
headers = {
  'Notion-Version' => '2026-03-11',
  'Authorization' => 'Bearer ntn_xxx(トークン)'
}
response = Faraday.new.run_request(:get, url, nil, headers)

p JSON.parse response.body

こんな感じで、Notion APIを使ってRubyでNotionの内容を読み取り、取得することができました🎉

5. ページの本文にはRetrieve block childrenが使える

自分がハマったポイントとして、Retrieve a pageエンドポイントでは、本文は取得できないというものがありました。

エンドポイント名からして、ページの内容を本文含めてごそっと取れるのかなーと思ったのですが、どうやらページのプロパティ情報(更新日など)が取れるエンドポイントで、本文の取得はできませんでした。

以下のようなページをしているとき(ページタイトルが「テストのページ」)、「ここに本文が入ります。」は Retrieve a pageのレスポンスには含まれませんでした。

その代わり、Retrieve block childrenというエンドポイントが使えます。
これは、block_idを渡す、という仕様ですが、このblock_idにページID(そのNotionページのURLの文字列)を渡してあげれば良いです。

block_id = "36e7e3c5cd238088b01ae2e5464f0402"
url = "https://api.notion.com/v1/blocks/#{block_id}/children"
headers = {
  'Notion-Version' => '2026-03-11',
  'Authorization' => 'Bearer ntn_xxx(トークン)'
}
response = Faraday.new.run_request(:get, url, nil, headers)

p JSON.parse response.body

6. テーブルビューの一覧取得には Query a data sourceが使える

Notionでは、以下のような「テーブルビュー」というものが作れます。

このテーブルの中のアイテムの一覧を取得したかったのですが、上記で紹介した Retrieve block childrenでは取得できないようでした。
(「テストのデータベース」という名前のデータベースはあるよ、くらいはわかりますが、各項目(記事Aなど)は取得できませんでした。)

調べた結果、「Query a data source」(postリクエスト)を使えば取得できました。

必要になるのは、「データソースID」というもので、以下の画像の箇所から取得できます。

data_source_id = "36e7e3c5-cd23-80e4-822c-000b96b271f8"
url = "https://api.notion.com/v1/data_sources/#{data_source_id}/query"
headers = {
  'Notion-Version' => '2026-03-11',
  'Authorization' => 'Bearer ntn_xxx(トークン),
  'Content-Type' => 'application/json'
}
response = Faraday.new.run_request(:post, url, nil, headers)

p JSON.parse response.body

その他: レート制限について

Notion APIのリクエスト制限については以下に記載があり、レート制限にも言及があります。

rhgc0kvlw3.apidog.io

インテグレーションごとの受信リクエストのレート制限は、1 秒あたり平均 3 リクエストです。平均レートを超える一部のバーストは許可されます。

とあります。

「平均レートを超える一部のバーストは許可されます。」とあるように、瞬間的に1秒あたり/3リクエストを超える分には問題ないようです。
実際、連続的に20回ほどリクエスト投げてみましたが、全て200が返ってきました。

レート制限はページごと?あるいはコネクト(インテグレーション)全体で判定?など気になることもあります(ドキュメントの書き方だとインテグレーション丸ごと使えなくなりそうですが)が、そこまでの言及はされておらず、単なる興味のためにバーストまでリクエストするのも良くない気がしたので、検証はできていません。

『Googleのソフトウェアエンジニアリング』読書メモ・感想

先日、『Googleのソフトウェアエンジニアリング』を読みました。

www.oreilly.co.jp

各章ごとに、気になったトピックなど踏まえながらつらつら感想を書いていきます。

1章「ソフトウェアエンジニアリングとは何か」

「プログラミング」と「ソフトウェアエンジニアリング」という言葉の違いにきちんと言及している書籍を初めて見た気がする。
今まで自分は「プログラマー」なのか、「ソフトウェアエンジニア」なのかよくわからないなと思っていたが、この書籍の定義でいえば、「ソフトウェアエンジニア」となるし、そうでなければいけないと感じた。
つまり、技術スタックや開発チームの長期的な保守性やスケーラビリティが確保されている状態にする仕事を負っている。
特に今は仕事で新しいサービスの開発を行っているので、この点を意識し続ける必要がある。

また、Hyrumの法則(APIを作成すると、あらゆる挙動に依存するユーザーが現れる)は印象に残った。
Rubyで言えばprivateメソッドにしていても使おうと思えば使えてしまうし、インスタンス変数も直接参照することだってできる。(自分もたまにどうしてもgemをカスタムしたいときにやってしまうこともある)

Beyonceルールにも共感した。
少し書籍に書かれている事例とは違うが、とある修正をした結果、CIが通った上で他の機能に障害やバグが起きてしまったのなら、それはその原因を混入したコード(開発者)に責任があるのではなく、その機能のテストをきちんと書けていなかったことが問題という考え方をした方が良さそう。

直前に読んだ『ソフトウェアアーキテクチャの基礎』と同様に、この書籍からも(「はじめに」も含めて、)、「意思決定に絶対的な決定はなく、常にトレードオフがあって、間違える可能性もあるからなるべく早く間違いに気づいて修正できるようにしよう」というメッセージが受け取れた。

2章「チームでうまく仕事をするには」

とにかく全体的に、エンジニアは隠れていないで(一人で閉じこもらないで)、どんどんチームに相談して、失敗を早期に発見して無駄なことに時間を使わず、開発力を上げていけ、ということを言っている章。

「こいつ馬鹿だって思われるんじゃないか」という気持ちで完成前に誰かに見られるのを不安に思う気持ちとか、身に覚えがありすぎて、グサグサ刺さって、時折笑ってしまった。
ただ、Googleに勤めているような人でもこの気持ちになりながら仕事をしていると思うと、おそらく全員のエンジニアが思っているんだなという気持ちになって少し気が楽になったような感じがした。

ジャグリングの例はなるほどなと思った。(ジャグリングの技術を教えられても、自分自身を否定されたようには思わないだろう。コードへの指摘も同じであるはずだ。)
「相手が自分を攻撃するかもしれない」というのは相手を十分に信頼できていないということでもある。

自分は特にチームでの開発で成果を出していきたいと考えている人間なので、この章の書いてあることには強く共感した。
ぜひまた都度読み返したいと思う章だった。

3章「知識共有」

「ずっとアーカイブされることを恐れて質問をしたくない」は、あまり言われないがあると思う。
例えばいつでもslackのチャンネルで質問しても良いよと言われても、後々自分の質問が残っていくことを考えると、質問しづらいと思ったことが自分もある。
そんなケースを救ってくれるのが1対1のメンターをはじめ、口頭で気軽に質問できる場を用意することだなと思った。(あるいは質問を匿名でできる場を用意するのはどうだろう?と思った)

心理的安全性の確立をするには、「なんでも聞いてください!」というだけでなく、自分もその手本となることが必要だと思った。
「リーダーこそ自分のわかっていないことを表明したり素直に質問をすることが肝心で、その方がむしろ尊敬を集める」という記載はとても共感した。

ドキュメント化は、必要だと頭ではわかっていながら、どうしてもコードを書く方に戻りたくなったりして、書かずに置いてしまう心当たりがあったが、これほどまでに必要性を説明されたら、さすがににもう少し書くか…という気持ちになった。 それに関して、URLを技術名にするのはいいアイデアだと思ったし、できるなら試してみたくなった。(keycloakについてのドキュメントはxxx/keycloakなど)

また、Googleのリーダビリティの制度はさすがGoogleだし、改めてすごい世界だ…という感想。

4章「公正のためのエンジニアリング」

基本的に日本で国内向けにアプリケーションを開発しているとなかなか考えない視点の話で興味深かった。
特に、低代表の人にさらに害を与えてしまう可能性の話については、誰かに利益があるようにする裏には、誰かに不利益となるかもしれない、ということだと感じた。
そこで公正ではないことが行われるのであれば、エンジニアとしてストップをする勇気を持ちたい。

5章「チームリーダー入門」

リーダー(マネージャー)の役職に興味がないわけではない自分にとっては参考になる話がとても多かった。
PRなどで目にみえる形で進捗がある開発メンバーに比べ、マネジメントをするようになると「今日は何もしなかった」と思いやすいというのは想像に容易いし、辛い気持ちになりそう。
実際にはマネジメント業務のあれやこれやをしているわけなので、タスクをチケットに切って見える化しておくのも良さそう。

また、マネージャーは管理職と呼ばれたりするが、「管理」という言葉を言葉通り捉えるとマイクロマネジメントに陥るというのは罠すぎると思った。
その中で、「やる方法を気にするのではなく、やり遂げられるかを気にするべき」という箇所は印象に残った。
自分も以前小さいチームのリード的な役割をしていた時を思い返すと、やる方法を気にしていた場面があったような、、と反省した。

自分はマネージャーという役職に興味がないわけではない一方で、なかなかそちらの世界に踏み込めていない一因として、自分はリーダーとして相応しい知識や経験が全く足りないのではないか、というのがある。
ただ、本章を読んで、(もちろん足りない部分があるのは事実だが、)それのみを以て自分には資格がないと思うのは違うか、と考えられるようになった。
自分が勝手に想像するリーダーというのはどんな質問や障害に対しても常に一定の回答と対応ができるような人間で、自分はまだそうなれない、、と思っていたが、思い返せば、多分それができる人はほぼいないし、自分もそんなリーダーを求めているわけでもない(本章にあるとおり、わからないことをわからないと表明してくれるリーダーは良いリーダーだと感じる)ので、前向きな気持ちになった。

6章「スケールするリーダー」

ここでも『ソフトウェアアーキテクチャの基礎』で何度も出てきた「永久的な正解はなくトレードオフがあるだけなので高速で失敗してイテレーションを回せ」という趣旨の話が出てきた。

本書で出てくる「自動運転チームを構築する」というのは、良い指針だと思った。
リーダーの仕事はもっぱら組織・戦略づくりと方向づけであって、組織に何か課題が発生した時に、しばらく放っておいても自律的にその課題を解消することができるようなイメージだと理解した。(自動運転=リーダーは何もしない、のではなく、自動運転ができる状態を常にメンテナンスするイメージかなと思った)

また、緊急な仕事と重要な仕事の章は印象に残った。
以前公務員として緊急な仕事ばかりしていたときのことを思うと、(中には緊急で重要な仕事もあったけど、)確かに自分が思っているよりは重要な仕事は少なかったかも。
そのため、自分が持っているうちの「自分にしかできない決定的な重要なもの」以外の80%は投げ捨てるべきらしい。(もし投げ捨てたうちに重要なものがあればまた戻ってくる。)
些細な例だが、確かに普段電話があって取れなかった時に「もし大切な連絡ならまたかかってくるでしょ」と思って全部の電話にかけ直したりしないことをしているが、考え方としては近いのかなと思ったりした。

7章「エンジニアリング生産性の計測」

計測する価値があるかどうかの判断基準の話が面白かった。
計測した結果が良い結果だったり悪い結果だったりした時に、それぞれきちんと行動がとられなければ意味ない、というのは言われれば当然だが抜けてしまうこともあるのでは、と思った。
良い結果が出ても現状維持だし、悪い結果が出てもなんだかんだ色々理由をつけて維持されることが予想されるようなものに対して計測を行う意味はない。

また、計測するのは必ずしも定量的なメトリクスに限らないというのも参考になった。
どうも定性的な指標は嫌がられるきらいがある気がする(それが正しい場面も多い)が、例えばエンジニアへのアンケートでしか得られない情報も多いのも確かだなと思った。

8章「スタイルガイドとルール」

コーディングガイドラインのルールを作る時は「まず組織が何に価値を置くかを認識しなければならない」し、ルールを定義する場合は「どんなゴールを前進させようとしているのか」の質問をするべき、というのはなるほどと思った。
確かにこの観点が抜けるとルールの一貫性がなくなるし、好みの問題じゃんという議論が多くなってしまう気がする。

本題とは関係ないが、Googleは「20億行以上のコードが入ったコードベースに向けて、約6万回のコード提出が毎日行われている」らしく、改めてとんでもない規模だなという感想。

「コードは書かれるより読まれることの方が圧倒的に多い」
これは何度も肝に銘じたい。

「ルールを決定するに背景の根拠をドキュメント化しておくと、変更するときにその根拠を前提に判断できる」
変更するときに効いてくるというのは新しい視点だった。

9章「コードレビュー」

「コードとは債務である」 変更が難しいクラスなど、いわゆる技術的負債のみを債務と考えがちだが、コードを書けば必ず保守が発生する。

コードレビューがもたらす影響についての記載も面白かった。 - エンジニアにコードが「自分個人のもの」ではなく、集合的な事業の一部であるという認識を強化させる - 批判にさらされることを受け入れやすくする - コード提出前の最後の一確認をするようになる

など、心理的な好影響については考えたことがなかった。

以下は、自分の組織だと行なっていないが、導入を検討・提案する余地がありそうと思った。 - レビューコメントに対する修正を行いさえすれば、再レビューを経なくて良いというもの(尊敬と信頼) - 大半のコードレビューはレビュアー1名。複数人に見てもらう場合でも別々の側面に専念するべき。

レビュー時には以下に気をつけることを改めて意識する(今でも自分としてはできているとは思うけど) - コード作成やのアプローチが間違っていると決めてかかる前に、なぜそのようなアプローチが取られたのかについて質問する

また、Googleでのコード変更の約35%というのは、想像よりもかなり高い割合で驚いた。
Webアプリケーションで全く同じようにはできないと思うが、このくらい変更が少ない単位で高速にレビューを回すというのは自分としては良い方法だと思う。

10章「ドキュメンテーション」

「ドキュメンテーションをコードのように扱う」は参考にしたい考え方だと思った。
やはり別物と考えてしまうために、保守のことを想定しなかったりして廃れていってしまう気がする。

ドキュメントを書く人と読む人が別になるのは結構罠で、意識しないとズレていく原因になるので意識したい。

Googleの取り組みで、自分の組織にも取り入れたいと思ったのは「鮮度日付」の仕組み。
今働いている会社にはせっかく頑張って作成したドキュメント集があるが、鮮度を保つ仕組みがないので、それこそリマインダーなど、何かしらやってみたい。

11章「テスト概観」

概ね、うんうんそうだよね、という話だった。(単体テストの比率の話など)

一方で、「テストは本番環境向けコード同様に扱うべき」というのはちょっとできていないところがあると思った。
レビューでも割と開発者任せにしてしまっているところがある。
ただ最近は、RSpecのうちsystem specには、重要かつsystem specでしか書けないテストだけ書く、くらいは意識しているつもり。

新人エンジニアにテストを書くのが当たり前という講習を受けさせて文化を変えていった話は、組織文化の変容のさせ方として非常に面白かった。
思想は強制するのではなく、「成功する思想は広まるものである」というのも納得の考え方。

12章「ユニットテスト」

こちらも今まで勉強してきたことや先輩に教えてもらった内容通りだなと思って読んでいた。

13章「テストダブル」

  • Googleではなるべく現実的なテストを書くことを優先してモッキングフレームワークの利用は避けている、とのこと
  • 先日も仕事でモックを使ったテストコードがあって、よく見たらテストしたいことがテストできていない、ということに気づいたことがあったので、モックは本当に必要な場面で最低限で使うのがいいのかなというのが自分の感想でもある
  • 依存関係が増えて機能修正などでテストが落ちる量が増えるかもしれないが、むしろそれは望ましく、バグ混入するリスクを減らすという大きなメリットを取った結果の仕方ないトレードオフなのかなと思っている
  • ただし、一方で、超大規模コードなどでそうも言ってられないこともあるのだろうなとも思ったりもした。
  • 「フェイキング」という言葉に馴染みがなかったが、外部APIとの接続機能を作る上で実際のAPIをローカルから叩けないときにダミーのリクエスト先を作っておくことをやるような話のテスト版だなという理解をした
  • 「フェイキング」「スタビング」「インタラクションテスト」など色々用語が出てきた。普段使い分けをあまり意識できていなかったが、それぞれ「あの時やったのは"インタラクションテスト"だったな」などという気持ちで読めて面白かった

15章「廃止」

  • 改めて、コードは債務であり資産ではない。価値があるのはコードが提供する機能。
  • ここでもHyrumの法則が関係してくる。ユーザーが提供側が思わぬところに依存していることがある。
    • 以前読んだ『Web API: The Good Parts』でもいかに破壊的変更が難しいか、という話があった
  • 以下は今まであまり考えたことがなかったが、なるほどと思った点
    • 最初から廃止することを念頭に設計すること。
      • 実際、先日外部APIの移行をした際、移行前のサービス名が入ったカラムやクラスがあって若干面倒な作業が増えたなと思ったので、実体験としても確かに大切だと感じた。
      • 若干話は違うけど、よく言う「失敗しても後戻りできるならどんどんしよう」みたいな話の具体的な方法論としても使える考え方だなと思った
    • 期限を定めない廃止勧告が意味はあまりない
    • 廃止作業をする際に専門で人員をつけないと、ユーザーからはケチだと思われる(自分達の要望が後回しにされていると感じる)
    • 廃止の警告をする際は、「行動可能性(エンジニアが何をしたら良いかわかること)」と「関連性があること(ユーザーが実際に使おうとした際に警告が出るなど)」が必要。
    • 廃止の計測的なマイルストーンは完全の撤去ではなく、インクリメンタル(段階的)なものにしておくべき

16章「バージョンコントロールとブランチ管理」

  • Googleのソースコードの大部分は単一のリポジトリーで管理されている(!)
  • Piperという社内用バージョンコントロールシステムを作って使っている

19章「GoogleのコードレビューツールCritique」

  • LGTMを与える場合は、指摘内容の対応は依頼するものの、再レビューは必要としないもので、タイムゾーンが異なる場合などの時間節約につながっている
    • 「信頼を公然と示すこともまた、信頼を築きチームを強化する優れた方法だ」の考え方は取り入れていきたいと思った。

20章「静的解析」

「ユーザーにコントリビュートする権限を与えよ」については、流石にGoogleのように独自の静的解析ツールを作成するのは大変すぎるが、Rubocopなら適用したいor適用したくないcopを開発メンバーが意見できる仕組みを作るのがこの考え方に倣うことになるかなと思った。

24章「継続的デリバリー」

  • 組織の長期的成功への鍵は常に速度
    • 「より速い方が安全」は良い標語だと思った
    • 1章の感想にも書いたが、この、失敗までを早くする、というのは最近読む本には本当にどこにも書かれているなという印象
  • 利用されるもののみをリリースすべき
    • Googleのように事前に大規模のA/Bテストはなかなかできなくても、リリース済みの機能を監視して使われていなければ削除していく、というのは「コードは資産ではなく負債で、資産は機能」という考え方に立つと非常に腹落ちする話だなと思った
  • 障害を恐れてリリース頻度が下がると逆にもっと開発者が辛いよね、という話は、基本的なことだがやはり大切

【Rails】デプロイのたびにseed処理でパスワードが更新され強制ログアウトされていた

問題の概要

先日、開発・検証環境でデプロイするたびにユーザーが強制的にログアウトする問題に当たりました。

関連して使っていたgemは以下です。

原因を調査すると、以下のことがわかりました。

  • この現象が起きるユーザーはseedで作成したユーザーであること
  • デプロイ時にdb:seed_doを実行している際に起きている現象であること

これを踏まえてseedファイルを確認すると、書き方に原因があったことがわかりました。

問題のあった書き方

User.seed(
  :email,
  { :email => "jon@example.com",   :password => "pass1234" },
  { :email => "emily@example.com", :password => "pass1234" }
)

seed-doのgemを素直に使うとこんなコードになりますが、素のパスワード情報は当然DB側で持っていないので、元のパスワードが何であれ(pass1234であっても)、毎回パスワードを変更する処理がかかります。
パスワード変更することでセッションが切れて、seed_do実行するごとにログアウトされていました。

パスワードも常にリセットしたい場合はこれでもいいのですが、私の場合はログアウトされないようにしたかったのでログアウトされないような対応をすることにしました。

対応策

1. find_or_create_byを使う

User.find_or_create_by!(email: "jon@example.com") do |user|
  user.password = "pass1234"
end

上記のようなコードであれば、同じemailのユーザーが存在すれば処理をスキップするので、passwordの更新はかかりません。

2. bulk_insertを使う

私が遭遇したプロダクトにはすでに activerecord-import gemがインストールされていたので、このgemを使うことにしました。

※ 前提として、Userモデルのテーブルのemailカラムにユニーク制約が設定されているとします。

users = [
  User.new(email: 'jon@example.com', password: 'pass1234'),
  User.new(email: 'emily@example.com',  password: 'pass1234'),
]

# on_duplicate_key_ignore: 一意制制約に違反する場合は処理をスキップする(パスワードを上書きしない)
User.import!(users, on_duplicate_key_ignore: true)

on_duplicate_key_ignore オプションを使えば、ユニーク制約をかけているemailを見て、重複レコードになりそうであればスキップしてパスワードを上書きしません。

注意点

もしseed実行時に間違いなく設定ファイル通りにデータをリセットしたい場合は、今回のような方法だとかえって混乱する場合がありますので、その辺りは理解した上で修正する必要があります。
(氏名レコードが別途ある場合、emailだけ見てスキップするコードだとパスワードの他にも氏名もリセットされないなど)

『ドメイン駆動設計入門』読書メモ・感想

www.shoeisha.co.jp

先日、上記の本を読んだので読書メモを残します。 (知識のまとめに若干AIの力を借りています)

書籍の感想

「ドメイン駆動設計」というものについて言葉だけ知っているが意味はよく知らない、という状態だったので読んでみました。
「ボドムアップでわかる!」あるように具体例から解説をされていて、なぜこれが必要なのか、という点が理解しやすく感じました。
普段Railsでアプケーション開発をしていると、Railsの「レール」で開発をしていると遭遇しない層の話などもあり、Railsの「設定より規約」の思想をより感じることになった一冊だったような気がします。

ドメイン駆動設計とは

ビジネスの問題を解決するためにビジネスの理解を進め、ビジネスの表現をする。ビジネスとコードを結びつけて継続的かつ反復的な改良を施せるように枠組みを作ることにより、ソフトウェアをより役立つものにしようというものです。

成瀬 允宣. ドメイン駆動設計入門 ボトムアップでわかる!ドメイン駆動設計の基本 (p. 4). (Function). Kindle Edition.

先日読んだ『ソフトウェアアーキテクチャの基礎』でもそうだったが、「継続的に反復する」というのは現代で良い設計とされるための重要な要素なんだろうという感想です。

Chapter1 ドメイン駆動設計とは

  • ドメインは「プログラムを適用する対象となる領域」
  • ドメインの概念をモデリングして得られたモデルをドメインモデルと呼ぶ
    • モデリング: ドメインの概念を、ソフトウェアで必要な情報に限定して抽象化する作業
  • ドメインモデルをソフトウェアで動作するモジュールとして表現したものがドメインオブジェクト
  • したがって、ドメインの変化があれば、ドメインモデルを通じてドメインオブジェクトに伝達される

Chapter2 値オブジェクト

  • 値オブジェクトとは、「氏名」などのようにシステム固有の値を表したオブジェクトのこと
  • 値オブジェクトの値は不変
  • 値オブジェクトにすべきかどうかの判断基準は、「そこにルールがあるか」「それ単体で扱いたいか」
    • 振る舞いをまとめてルールを語らせる(自己文書化)
  • 「表現力を増す」「不正な値を存在させない」「誤った代入を防ぐ」「ロジックの散在を防ぐ」を実現するための手段となる

Chapter3 エンティティ

  • 値オブジェクトとの主な違いは「同一性によって区別される」「可変」「同じ属性でも区別される」「ライフサイクルがある」 -「ユーザー」のイメージ
    • idが同じであれば同じ、同じ生年月日でも別々のユーザーと認識される、退会でユーザーが削除される、など
値オブジェクト エンティティ
同一性の基準値 (中身) が同じなら同じもの ID が同じなら同じもの
不変性 不変 変化してよい
金額、住所、色、座標 ユーザー、注文、商品

Chapter4 ドメインサービス

  • ドメインオブジェクト(値オブジェクトやエンティティ)に記述すると不自然になる振る舞いを記述する場所
  • 例えばユーザーの重複チェックをユーザーオブジェクトにさせると、ユーザーオブジェクト自身に問い合わせることになり不自然
  • ただし、ドメインオブジェクトに記載できることまでドメインサービスに書くと、ドメイン貧血症(ドメインが何も語らなくなる)になるので注意

Chapter5 リポジトリ

  • データの永続化にまつわる処理を抽象化するための層(DB操作など)
  • ドメインオブジェクトがDBの変更等の影響を受けない

感想

RailsだとActiveRecordでいわゆる「ドメインオブジェクト」と「リポジトリ」の役割が両方入っており明確に違うなーと勉強になった。

Chapter6 アプリケーションサービス

  • アプリケーション向けのユースケースを実現するオブジェクト
    • ユーザー登録など

感想

RailsのServiceクラスの役割に一番近いのはこれかなーと思ったりした。

Chapter7 柔軟性をもたらす依存関係のコントロール

  • 依存関係逆転の法則
      1. 上位レベルのモジュールは下位レベルのモジュールに依存してはならない、どちらのモジュールも抽象に依存すべきである。
      1. 抽象は、実装の詳細に依存してはならない。実装の詳細が抽象に依存すべきである。
  • とあるアプリケーションサービスがリポジトリクラスに依存してしまっている場合(上位が下位に依存し、抽象が詳細に依存してしまっている状態)、抽象層を作成することでアプリケーションサービスとリポジトリクラスが共に抽象に依存できるようになり、リポジトリの変更がアプリケーションサービスに影響しないようにできる
    • データベースの操作のコードを変えたらより高レベルなアプリケーションサービスのコードを変えないといけない、というのは避けたい

Chapter9 複雑な生成処理を行う「ファクトリ」

  • 複雑なオブジェクトの生成処理をオブジェクトとして定義すること
  • 「コンストラクタ内で他のオブジェクトを生成するかどうか」はファクトリを作る際の動機づけに良い指標

Chapter10 データの整合性を保つ

  • ユニーク制約やトランザクションを使って整合性を保つ
  • ただ、整合性は高次元な概念
    • ビジネスロジックには整合性を保つための詳細ではなく、整合性が必要な処理であることの明確な主張が書かれるべき

Chapter12 ドメインのルールを守る「集約」

  • 集約ルートを通ってのみオブジェクトを操作させるようにする
  • 関連するものとして、オブジェクト指向では「デルメルの法則」がある
    • userクラスの中で、address.location.cityとせず、userがcityメソッドを呼べるようにして、内部構造(addressやlocation)を知る必要をなくすようなこと

Chapter13 複雑な条件を表現する「仕様」

  • あるオブジェクトがある評価基準に達しているかを判定するオブジェクト
  • ドメインオブジェクトが必要以上に他のオブジェクトの情報を知らないといけなかったりする際に導入し、複雑な評価処理をカプセル化する

『ソフトウェアアーキテクチャの基礎』読書メモ・感想

年末年始に、そのボリュームの多さになかなか手がつけられていなかった『ソフトウェアアーキテクチャの基礎 -エンジニアリングに基づく体系的アプローチ』を読みましたので、自分なりのまとめと感想を記事にしました。

www.oreilly.co.jp

どんな本?

  • 現代のソフトウェアアーキテクチャのバリエーションやトレンド、それぞれの特徴について解説しつつ、ソフトウェアアーキテクトという職業に求められるスキルなどについても解説している本
  • いわゆるコードレベルの設計ではなく、例えばマイクロサービスとか、モノリシックなアーキテクチャとか、そういう次元の話
  • 自分は本書でいう「アーキテクト」の仕事はしたことはなく、「開発者」の立場として読んだが、興味深かった
  • あまり知らない領域の話だったものの、丁寧に解説されていて、大事なことは何度も繰り返し主張されるので、意外と理解に困ることはなかった

各章の自分的まとめ・感想

2章「アーキテクチャ思考」

  • アーキテクトは、技術的な深さよりも幅が求められ、そのための努力が必要
  • アーキテクチャ決定は必ずトレードオフであることを意識する必要がある

3章「モジュール性」

  • 計算式は理解が難しくて完全にはわからなかった
  • 適切な抽象化やコナーセンスの度合いを抑えることで変更につよいシステムを構築することが必要

qiita.com

4章「アーキテクチャ特性」

  • いわゆる「非機能要件」の言い換え的な話
  • 色々な特性(信頼性、拡張性、etc)があるが、全てを取るのは不可能であり、どの特性が必要かを選択する必要がある
  • 詰まるところ、これもトレードオフの関係になるし、イテレーティブな設計になっていればやり直しがしやすくなる

5章「アーキテクチャ特性を明らかにする」

  • ドメインの関心事をアーキテクチャ特性として翻訳する
    • この際に、短絡的にならないように注意。
    • 例えば、市場投入までの時間というドメインの関心事に対して、「アジリティ」というアーキテクチャ特性だけに翻訳してしまうと、テスト容易性やデプロイ容易性の観点を損ない、目的が達成できない可能性がある
  • アーキテクチャ特性の優先順位を決めすぎず、上位3を決める、というような方法にする
    • どのアーキテクチャ特性が重要ではないかを決めるのは必要だが、一番必要なのはどれか、というところは不毛な議論になりやすい
  • アーキテクチャアーキテクチャ特性を満たさなくても、アプリケーションの設計で満たすことができる可能性もある

6章「アーキテクチャ特性の計測と統制」

  • アーキテクチャ特性を計測する仕組みを作ることが必要
  • また、必要と決めたアーキテクチャ特性については、適応度関数の仕組みなどを入れて統制させる仕組みづくりが必要

7章「アーキテクチャ特性のスコープ」

  • アーキテクチャ特性について、以前はシステム全体に対する特性、だったが、現代の開発では、マイクロサービスの登場などにより、もっと細かい単位でスコープの設定をするべき状況になっている
  • その単位を著者らは「アーキテクチャ量子」と定義している
  • 本章では、オークションサイトのアーキテクチャ例を紹介しながら、例えば入札者と競売人で必要なアーキテクチャ特性が異なることなどを説明している

8章「コンポーネントベース思考」

  • モジュールをパッケージ化した「コンポーネント」を発見することが必要
  • そのためには、アーキテクチャをどのように分割するか(最上位の分割)を決める。
    • これには、ドメインによる分割や、技術による分割などの手法がある

10章「レイヤードアーキテクチャー」

  • シンプルで親しみやすく、コストが低い
  • アーキテクチャが固まっていない時点の出発点としても適切になることが多い
  • 一方で、システム単位が大きくなるので、デプロイ容易性や、弾力性、耐障害性(どこかでメモリ不足になると全体がクラッシュ)が低いなどのデメリットもある

11章「パイプラインアーキテクチャ

  • bashzshの基本原則になっているようなアーキテクチャ
  • 特にシンプルな一方通行の処理を数sめるタスクで用いられる
  • シンプルなのは強みである一方で、基本的にはモノリシックなので、弾力性などは低い

12章「マイクロカーネルアーキテクチャ

13章「サービスベースアーキテクチャ

バランス型、という感じで、書籍の中でも割と推されているような記述ぶりに感じた。

14章「イベント駆動アーキテクチャ

  • 非同期的にイベントを送受信して処理するアーキテクチャ
  • リクエストベースモデルとイベントベースモデルという分類がある
  • ブローカートポロジーは、イベントの処理が終わったのち、受信する人がいるかどうかを気にせずイベントを発生するもの。拡張性に優れるが、ワークフローを管理する役割を持つ機能が不在
  • メディエータートポロジーは、イベントの受信を請け負って、ワークフロー全体の動向を管理するが、その分拡張性はブローカーほどではない
  • 他のアーキテクチャとハイブリッド形式で採用されることも多い

15章「スペースベースアーキテクチャ

  • キャッシュを使った(DBへの接続回数を減らす)アーキテクチャ
    • 複数の「処理ユニット」間で、データのキャッシュをレプリケーションして同期的に持つ(ただし、レプリケーションではなく、一元的にキャッシュを管理して各処理ユニットがリモートアクセスする分散キャッシュを利用することもある)
    • キャッシュを更新してからDBを非同期で更新する
  • それゆえ、更新のタイミングによってはキャッシュのデータに不整合が生じることもある
  • クラウドとオンプレミス環境を跨いでデプロイする(例: DBと読み書きをするデータリーダー/ライターだけオンプレにする)こともできる
  • これらの特徴から、コンサートチケットのサイトなどに適している(DBへのアクセスが少なく、弾力性が高い)
  • 一方で、非常に複雑なので、テストが難しく、コストも高くなる

16章「オーケストレーション駆動サービス指向アーキテクチャ

  • 過去に分散アーキテクチャとして考案されたものの、あまりうまくいかなかったもの
  • 再利用することに対するトレードオフは結合が高まること

この章の話は、全体的にあまりピンと来なかった

17章「マイクロサービスアーキテクチャ

  • 高度な分離をし、「境界づけられたコンテキスト」の論理的概念を物理的にモデル化することを目的としたアーキテクチャ
  • 「マイクロ」という名前は最小のという意味より、単なるラベル名と思うべき。トランザクションがサービス間を跨ぐことがないようなものであることは必要。
  • データベース含めて高度に分離されるので、各サービスで言語環境を変えるなどの組織計画も可能になる
  • サービス間で連携が必要な場合は、イベント駆動アーキテクチャでいう「ブローカー」や「メディエーター」のパターンを採用する
    • それぞれのメリット・デメリットはブローカー/メディエーターと同じ
  • どうしてもサービスを跨いでトランザクションを構築したい場合は、サーガという「メディエーター」の役割をするサービスを別途構築してそのサービスに管理させるという方法があるものの、結合してしまうトレードオフがあるので、基本的にはサービスを跨いでトランザクションを構築するのは推奨されず、サービスの粒度の見直しを優先的に検討するべき
  • コストや複雑性は課題だが、現在のエンジニアリングプラクティスで重視されるデプロイやテストの容易性に強みを持つ

19章「アーキテクチャ決定」

20章「アーキテクチャ上のリスクを分析する」

  • リスクマトリックスを利用し、アーキテクチャ内のどの部分にどのようなリスクがどのくらいあるかを分析する
  • リスクアセスメントとしてドメイン領域などで区切ってレポートにまとめる
    • この際、リスクの方向(改善か、悪化か)も重要な情報となる。リスクの方向の伝え方は工夫が必要(矢印などだと誤解を生みやすい)
  • 開発者(テックリード)と一緒にリスクストリーミングを行い、
    • リスクの特定(これは個人作業)
    • リスクの合意(どのアーキテクチャにどのレベルのリスクがあるかの合意を取る)
    • リスクの軽減(最も重要。合意したリスクに対して、それを軽減する方法を検討する。これを丁寧に行うことで、コスト増とのトレードオフ調整などがしやすくなる側面もある)

21章「アーキテクチャの図解やプレゼンテーション」

  • アーキテクトは良いアーキテクチャを考えても、きちんと伝えなければ意味がない
  • 図解を作成する上では、時間をかけすぎると作成した資料に執着が生まれてしまい、検討の邪魔になるので、最初は付箋などアナログな方法を活用してイテレーティブにできると良い
  • 図解を示したり、プレゼンテーションを工夫しないといけない

具体的な方法がいくつか述べられていた。今は生成AIを活用するのもよさそうと思った。

22章「効果的なチームにする」

  • アーキテクトが作成する制約はキツすぎても(コントロールフリーク)、緩すぎても(アームチェアアーキテクト)いけない
    • 開発者からアーキテクトになりたてだと、コントロールフリークに陥りやすい
    • 逆に、開発から離れていると、アームチェアアーキテクトに陥りやすい
  • どのように管理するのが良いかは、チームやプロジェクトの性質によるので、都度分析が必要
  • プロジェクトメンバーが多すぎると、多元的無知(自分の意見が言えなくなる)、責任の分散(自分で何かしようと思わなくなる)など、不具合が発生しやすくなる
  • チェックリストを活用する。静的解析など自動化できるものは自動化する
  • 開発者が技術スタックを使用したいときには、「ビジネス上の根拠」も確認するようにする
    • 開発者は、これが達成できないのに使用をしたいということがある

マネージャーとアーキテクトの棲み分けもなかなか難しそうと思った。
また、これを見るとアーキテクトは設計して終わりではなくて、開発・運用する間も一緒にプロジェクトを走らないと意味ないんだなーという感想を持った。

23章「交渉とリーダーシップのスキル」

  • 交渉
    • アーキテクトが下すほぼ全ての決定には異議が唱えられるため、交渉の力が非常に重要
    • ステークホルダーとの普段の会話から重要なアーキテクチャ特性を見極め、交渉の要素にすると良い
    • 交渉の際には、費用は時間は非常に重要であるものの、交渉の材料としては最後の手段にするべき
    • ステークホルダーが譲れないと言っている条件が、実はシステム全体ではなくシステムの一部が対象であり、その結果コストを抑えられることもある
    • 他のアーキテクトと衝突した場合は、議論しすぎるのではなく、冷静に実証することに努める
    • 相手が聞くのをやめてしまわないよう、根拠や理由を最初に説明する
  • リーダシップ
    • アーキテクトという肩書きで仕事をするのではなく、手本を示して周りを動かす
    • 開発者が質問や問題を相談できる相手になることを心がける
    • 開発チームに溶け込む時間を確保するため、アーキテクトは招待されたMTGについては自分の必要性を確認しつつ、開発者がMTGに巻き込まれているのを見たら、自分が代わりに参加して開発者の時間を確保することを検討する

交渉の方は、本で読むだけでしんどい仕事だなー、、、と感じた。
バズワードを利用する」というのは一般的には反感を買いそうな言葉だなーと思ったが、交渉のテクニックとしては必要か、、という気持ち。
リーダシップの方は、アーキテクトに関わらずリーダの役割として仕事をする上で大切なことが書かれている気がした。
「肩書きで仕事しない」の箇所は、「役職で仕事をしないこと」と公務員をしていた頃に散々聞いてきた話を久々に聞いた感じがして懐かしい気持ちになった。

第24章「キャリアパスを開く」

  • 技術は目まぐるしく変化するので、常に学び続ける必要がある
  • 朝一、20分の時間を使って技術的な幅を広げるための時間を作る(「未知の未知」を「既知の未知」にする)
  • テクノロジーレーダーを活用して、自分が次に何を追求するかのビジョンを描く
  • ソーシャルメディアを使用して、「弱いつながり」(新しい仕事のオファーや普段の経験の外側からのアドバイスが期待できる)を広げる

みんな分かっているけどなかなかできないよね、、という章。
ただ、20分を使って、「未知の未知」を「既知の未知」にする、というのはハードルが下がってよさそうな気がした。
あとSNSは、どうしても自分には向いていないと思ってしまうので、どうしたもんかな、、、という感想でした。

『リファクタリング 第2版』読書メモ

積読していた『リファクタリング 第2版』を読みました。

www.ohmsha.co.jp

どんな本?

リファクタリングとは、リファクタリングの意義を前半で解説した上で、後半(6章〜)はリファクタリングのサンプルカタログのようになっています。

私は、1-5章は丁寧に読み、6章以降は、具体的な手順はそこそこに、どんなケースでどんなリファクタリングをするべきなのか、という点を確認していく読み方をしました。

感想

自分が考えていたリファクタリングは、本書で解説されているリファクタリングより少し狭かったなーと感じました。

というのも、自分の考えていたリファクタリングは、

  • 従来よりもコード数が少なくなる(よりスッキリ書く)
  • 適切なクラスに責務を負わせる
  • 実装効率が改善する

などで、「可読性」についてはちょっと意識が少なかったかも、、という感想を持ちました。

以下、「印象に残った箇所」でも紹介していますが、 例えば「せっかく一度読んだコードの意味を覚えておくために関数化する」「可読性を採った結果、多少非効率に見える実装になっても、パフォーマンス上は実は無視できるレベルであることも多い」というのは、あまり考えていなかったポイントでした。

本書を読み進めている最中から、早速実務の方でも意識的に取り組んでみましたが、「可読性」を重視したリファクタリングは、結果スッキリ見えるし、メンテナンス性も上がるよなーという印象です。

印象に残った箇所

第1章

  • P7

理解したコードを埋め込んでおく方法とは、意味のあるコードの塊を関数に

処理をまとめて関数にすることの理由を、再利用とか見やすくするとかぼんやり理解していたが、「せっかく理解したコードの内容を忘れないようにする」ということをしていたんだなと気づきがあった。

  • P14

ローカル変数を削除することによる大きな利点は、扱うべきローカルスコープが減ることにより、メソッドの抽出がずっと楽になること

この箇所では、ローカル変数を削除する代わりにメソッドを何度も呼ぶことになるコードが紹介されていた。 自分だったら、何度も計算するのが嫌でローカル変数にすぐ入れてしまうなーと思ったので、パフォーマンス上問題ない限りは、リファクタリングする上ではローカル変数は減らすべきというのは頭に入れていきたい。 筆者は、別の箇所で、パフォーマンス改善もリファクタリングしてからの方がしやすい、とも言っている。一度パフォーマンスのことは気にせず、整理してからパフォーマンスに向き合うというのもアリなんだなーという気づきを得た。

  • P27あたり

中間データ構造を設けて、計算を担当する部分と表示を担当する部分を切り離すというもの。 中間データ構造を設けることで、関連する関数同士を切り離せるようにするという発想は色々な場面で使えそう。

  • P33

「簡潔さは知恵の要」と言われますが、明確さは進化するソフトウェアの要

コード量が長くなるのは良くないと思いがちだが、それによって明確さが増すのであればむしろ良いコードになるということを意識したい。

第2章

  • P46

ここのリファクタリングは非常に小さいステップ、またはそれらの組み合わせてでできています。その結果、リファクタリングではコードが壊れた状態になっている期間は非常に短く、たとえ未完成であっても、いつでも中断が可能です。

大規模なリファクタリング計画があったとしても、小さいステップで手順を踏み、その都度テストが通るように行なうリファクタリングを積み重ねるというプロセスを取るべきというもの。 実際に私も、リファクタリングするぞ、となった時、ついついいきなり大きく変更を加えてしまうことがあるが、これではリファクタリングになっていないということを意識しないといけないと感じた。

リファクタリングの途中で気がついたバグについては、リファクタリングも残っているべき

リファクタリング中にバグに気づくのはあるあるで、つい直してしまうが、勇気を持って見逃す(メモはしておく)というのも時には必要。 今までは、気付いたからには直さないといけないのでは?という気持ちで、リファクタリングを中断してバグ修正を先に行うなどしていたが、この書籍を以て、バグの程度によってはバグを保ったままリファクタリングを続けるという選択肢も持っておきたい。

  • P57

リファクタリングは、コードベースがどれだけ美しいかではなく、純粋に経済的な基準で測られるもの

あくまでリファクタリングは機能追加等をしやすくするためのもので、3行で書いていたコードをトリッキーなことをして1行にした!(ただし、読みづらくなっている)というような類ではないよ、という話。 これは結構勘違いしやすいところがある気がする。

  • P74, P75

良い名前が思いつかないということは、設計がまだ固まっていないことの兆候でもあります


コメントが必要だと感じたとき、代わりにわかりやすい名前を付けるようにする

今まで何度か聞いた言葉だが、改めて意識したい。

  • P236「ループの分離」

1つのループで複数のことをやらず、ループを分離しよう、というもの。 書籍の例に漏れず無駄に2回ループ回すのを避けてしまうが、パフォーマンス上問題ないケースも確かに多くありそう。

自分はポリモーフィズムを使うというリファクタリングをする勇気が持てないことが多いので、ここにあるガイドブックをもとに少しずつ挑戦してみようかな、、という気持ちになった。

  • p 332, p335 「問い合わせによるパラメータの置き換え」「パラメータによる問い合わせの置き換え」

どちらを取るかという1つの基準は「参照透過性」というのはあまり考えたことがなかったので、参考になった。

CSVを使った一括インポート機能を作成する際に確認するポイント

先日、CSVファイルを使ったデータの一括インポート機能を作成する機会がありました。 一連の実装を振り返ると、気にすることや決めるべきことが色々あるなーと思ったので、メモしておきます。

異常系への対応

✅ ファイルの形式が間違っているケースの対応をする

  • HTMLのaccept属性で制限するのとは別に、csvファイル以外がアップロードされた場合のサーバーサイドでの対策が必要です
  • 例えばrubycsvライブラリでは、CSV::MalformedCSVErrorが発生するので、それをrescueする処理を書いておくのが1つの方法です
begin 
  CSV.parse(...)
rescue CSV::MalformedCSVError
  flash.now[:alert] = 'CSVファイルをアップロードしてください'
end

✅ インポートできるデータの上限数を決めておく

  • 無制限にしてしまうと、サーバーの圧迫などを招く可能性があります
  • したがって、例えば以下の要素を検討し、適切な上限を設定する必要があります
    • ユーザーが不便に感じない上限数
    • パフォーマンス上問題ない上限数
    • プレビュー画面で表示した際にユーザーが見やすい上限数

✅ インポートされたデータが0件のケースの対応をする

  • 意外と忘れがちなのが、ユーザーがテンプレートをそのままアップロードしてしまった場合など、登録するべきデータが0件の場合です。
  • 何も対策していないと、バリデーションなどを通過してしまい、保存処理時に500エラーが起きる、といったこともあり得るので、0件の場合はきちんとエラーを返して保存処理に進ませない対策が必要です

ユーザービリティ関係の対応

✅ 何を記入したらいいかわからないようになっていないか

  • 電話やメールアドレスなどは明らかですが、DBの制約上、複数の選択肢から選んで欲しい場合(例えば「職種」など)などもあります
  • 何も説明がないと、自由記述とユーザーは思ってしまうので、何かしらの対策が必要です
  • 選択肢が少ない場合は、行のところに「職種(エンジニア or その他 で入力)」など書いておけばいいですが、選択肢が多い場合は非常に悩ましいです

✅ (記入例行がある場合)記入例行を削除するユーザーに対応できているか

  • 「何を記入したらいいかわからないようになっていないか」とも関連しますが、例えばテンプレートに記入例行を設けておく場合などは取り扱いに注意が必要です
  • 機械的に「上から1行は記入例だから無視」としてしまうと、記入例に上書き・削除して記載するユーザがいた時に、最悪保存できたつもりが保存できていない、という状況に陥り、不具合報告につながる可能性があります
  • 記入例行が記入例と違えば登録処理の対象に入れる、という仕様もありますが、仕様が複雑になったり、ユーザーのわかりづらさにつながるので、個人的には、可能な限り記入例行を作らずにテンプレートは作成できると良いのかなーと思っています

✅ バックグラウンド処理を使わずに実装できないか

  • 一括インポートは当然処理時間が長くなりがちのため、バックグラウンド処理が1つの選択肢となります
  • ただし、バックグラウンド処理をすると、考えるとが増えるので、できればbulk insertなどを有効活用しながら、同期処理で行うのが良いと思います
    • この点については、morihirokさんのKaigi on Rails 2025での発表が非常に参考になります

speakerdeck.com