Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
履歴 on Rails: Bitemporal Data Modelで実現する履歴管理/hi...
Search
hypermkt
September 27, 2025
Technology
0
120
履歴 on Rails: Bitemporal Data Modelで実現する履歴管理/history-on-rails-with-bitemporal-data-model
2025.09.27 Sat, Kaigi on Rails 2025@JP TOWER Hall & Conference
hypermkt
September 27, 2025
Tweet
Share
More Decks by hypermkt
See All by hypermkt
Sidekiqで実現する 長時間非同期処理の中断と再開 / Pausing and Resuming Long-Running Asynchronous Jobs with Sidekiq
hypermkt
7
5.5k
脆弱性から学ぶ Webセキュリティ Part2/study-web-security-from-vulnerability2
hypermkt
5
3.3k
脆弱性から学ぶ Webセキュリティ/study-web-security-from-vulnerability1
hypermkt
5
2.4k
モバイルアプリ向けAPI 開発を通じて学んだこと / learned-from-developing-mobile-app-api
hypermkt
3
4.4k
Passportのパスワードグラントで独自の認証を実装する方法 / how-to-implement-original-authentication-for-passport-password-grant
hypermkt
1
810
Webpackで作る Vueコンポーネント開発環境 / Creating the Vue component development with Webpack.
hypermkt
3
4.1k
あの問題解きました! / solved the code
hypermkt
0
340
Vue.js で作る日報アプリケーション ハンズオン / vue-js-handson-by-nippo
hypermkt
0
450
できるPHP7アップグレード / php7 upgrade
hypermkt
5
7.3k
Other Decks in Technology
See All in Technology
使いやすいプラットフォームの作り方 ー LINEヤフーのKubernetes基盤に学ぶ理論と実践
lycorptech_jp
PRO
2
270
そのJavaScript、V8が泣いてます。V8の気持ちを理解して書くパフォーマンス最適化
riyaamemiya
16
6.2k
ZennとCloud Runの歩み - プロダクト開発に全集中できる相棒になるまで
wadayusuke
4
510
あなたのWebサービスはAIに自動テストしてもらえる?アクセシビリティツリーで読み解く、AIの『視点』
yusukeiwaki
0
150
High performance GIF playback/iOSDC25
noppefoxwolf
1
120
AIで 浮いた時間で 何をする? #プロヒス2025
konifar
26
12k
全身画像からコーデアイテムを抽出し毎日にIRODORIを!デバイス完結型アプリを作る
zozotech
PRO
0
110
新卒QAエンジニアの成長戦略
qatonchan
0
190
測りにくい成果を測る — BtoB SaaSにおける効果検証への挑戦 / Shirokane Kougyou vol 20
sansan_randd
3
540
Geospatialの世界最前線を探る [2025年版]
dayjournal
0
110
「非更新サブスクリプション」って何者?
haseken_dev
0
200
5年間のFintech × Rails実践に学ぶ - 基本に忠実な運用で築く高信頼性システム / 5 Years Fintech Rails Retrospective
ohbarye
4
1.2k
Featured
See All Featured
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.1k
Building an army of robots
kneath
306
46k
Build The Right Thing And Hit Your Dates
maggiecrowley
37
2.9k
Become a Pro
speakerdeck
PRO
29
5.5k
Rails Girls Zürich Keynote
gr2m
95
14k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
15k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
657
61k
CSS Pre-Processors: Stylus, Less & Sass
bermonpainter
358
30k
Imperfection Machines: The Place of Print at Facebook
scottboms
269
13k
Raft: Consensus for Rubyists
vanstee
139
7.1k
Git: the NoSQL Database
bkeepers
PRO
431
66k
GraphQLとの向き合い方2022年版
quramy
49
14k
Transcript
履歴 on Rails: Bitemporal Data Modelで実現する履歴管理 2025.09.27 Sat, Kaigi on
Rails 2025@JP TOWER Hall & Conference @hypermkt / ばーちー SmartHR プロダクトエンジニア 1
ばーちー 自己紹介 株式会社SmartHR バックエンドエンジニア @hypermkt 2
ばーちー 自己紹介 株式会社SmartHR バックエンドエンジニア @hypermkt 3
なぜ私は今日発表するのか • SmartHRで履歴に関わる機能の開発・運用を担当 • 日々の現場で「履歴」の難しさ・面白さ・課題に直面 • その経験から得られた知見をRailsエンジニアの 皆さんに共有したい 4
1. 履歴管理の基本 2. Bitemporal Data Modelとは 3. ActiveRecord::Bitemporal 4. 履歴運用の課題と向き合い方
アジェンダ 5
履歴管理の基本 6
履歴管理とは • 「いつ・何を・どのように」 変えたかを 後から追える仕組み が履歴管理 7
システムでどうやって履歴を管 理するのか 🤔 8
履歴管理の代表的な手法 9 • バージョン型 • 有効期間型
バージョン型 10 • 概要: バージョンごとに全レコードを保持する方式 • ✅ メリット: ◦ 最新も過去も同じテーブルで取得できる
• ⚠ デメリット : ◦ 常に最新バージョンを取得する条件が必要 ID 投稿ID バージョン タイトル 本文 1 1 1 新製品のお知らせ(初稿) 新製品をリリースします。 2 1 2 新製品のお知らせ(修正版) 新製品を◦月◦日に発売しま す。
有効期間型 ( Uni-Temporal ) 11 • 概要:各レコードに「いつの情報か」を示す期間を持たせて管理する方式 • ✅ メリット:
◦ ある日付時点での状態を再現しやすい • ⚠ デメリット : ◦ 「いつ登録されたか」が記録されないため、あとから登録した履歴かどうかがわ からない ID employee_id department valid_from valid_to 1 1 総務部 2022-04-01 2023-03-31 2 1 システム部 2023-04-01 9999-12-31
人事労務ソフトとして 欲しいものはなんだろうか 🤔 12
• 「従業員」 を起点に情報が変わった推移が見たい ◦ 情報が変化するきっかけ ▪ 異動による部署や上長の変更 ▪ 引っ越しによる住所変更 人事労務ソフトとして欲しいもの
13
• 🕒 ある時点での状態を知りたい: ◦ 「あの時、この人はどこの部署にいた?」 • 📝 あとから分かった出来事も正しい日付で反映したい: ◦ 「1/10に引っ越したけど、住所変更を申請したのは1/20だった」
• 🔍 過去にどう変わってきたかを振り返りたい: ◦ 「いつ、どんな変更があったか」 「従業員を起点とした変化」をどう扱いたいか 14
履歴管理パターンの比較 課題 バージョン型 有効期間型 ある時点での状態を知りたい △ ◯ あとから分かった出来事も正しい日付で反映したい ✕ △
過去にどう変わってきたかを振り返りたい ◯ ◯ 15
履歴管理パターンの比較 課題 バージョン型 有効期間型 ほしいもの ある時点での状態を知りたい △ ◯ ◯ あとから分かった出来事も正しい日付で反映したい
✕ △ ◯ 過去にどう変わってきたかを振り返りたい ◯ ◯ ◯ 16
そこで Bitemporal Data Model!! 17
Bitemporal Data Modelとは 18
• 2つの時間軸 を持つ履歴管理モデル ◦ 有効期間 ▪ 現実世界でその事実が有効だった期間 ◦ システム期間 ▪
システム上でデータとして有効だった期間 Bitemporal Data Modelとは 19
Bitemporal Data Modelのテーブル例 ID 履歴ID 有効開始日 有効終了日 システム登録 開始日 システム登録
終了日 • 履歴ID: 同じ事実の履歴をまとめるためのID • 有効開始日 : その出来事が現実に始まった日 • 有効終了日 : その出来事が現実に終わった日 • システム登録開始日 : システムにこの情報が登録された日 • システム登録終了日 : システムにこの情報が無効となった日 20
Bitemporal Data Modelでの レコードの挙動 21
4/1 山田太郎さん システム部に 入社 履歴ID 名前 部署 有効開始日 有効終了日 システム開始日
システム終了日 1 山田太郎 システム部 2025-04-01 9999-12-31 2025-04-01 9999-12-31 UPDATE 4/1 山田太郎 システム部 INSERT SELECT ※ 時刻は省略しています 22
6/1 営業部に異動 履歴ID 名前 部署 有効開始日 有効終了日 システム開始日 システム終了日 1
山田太郎 システム部 2025-04-01 9999-12-31 2025-04-01 2025-06-01 1 山田太郎 システム部 2025-04-01 2025-06-01 2025-06-01 9999-12-31 1 山田太郎 営業部 2025-06-01 9999-12-31 2025-06-01 9999-12-31 4/1 山田太郎 システム部 6/1 山田太郎 営業部 UPDATE INSERT SELECT 23
今現在の情報を取得する 履歴ID 名前 部署 有効開始日 有効終了日 システム開始日 システム終了日 1 山田太郎
システム部 2025-04-01 9999-12-31 2025-04-01 2025-06-01 1 山田太郎 システム部 2025-04-01 2025-06-01 2025-06-01 9999-12-31 1 山田太郎 営業部 2025-06-01 9999-12-31 2025-06-01 9999-12-31 4/1 山田太郎 システム部 6/1 山田太郎 営業部 UPDATE INSERT SELECT 24
SELECT * FROM employees WHERE "有効開始日" <= '2025-09-27' AND "有効終了日"
> '2025-09-27' AND "システム開始日" <= '2025-09-27' AND "システム終了日" > '2025-09-27' 今現在の情報を SQLで取得する UPDATE INSERT SELECT 履歴ID 名前 部署 有効開始日 有効終了日 システム開始日 システム終了日 1 山田太郎 システム部 2025-04-01 9999-12-31 2025-04-01 2025-06-01 1 山田太郎 システム部 2025-04-01 2025-06-01 2025-06-01 9999-12-31 1 山田太郎 営業部 2025-06-01 9999-12-31 2025-05-01 9999-12-31 25
✅ メリット • 正確な時点再現ができる • あとから履歴データの登録・変更ができる • 監査・調査に強い ⚠ デメリット
• SQLが複雑になる • 概念(2つの時間軸)が複雑で学習コストがある • 履歴が積み重なり、レコード数が増える メリット・デメリット 26
Railsでどうやって Bitemporal Data Modelを 扱うのか...🤔 27
そこで ActiveRecord::Bitemporal! 28
ActiveRecord::Bitemporal 29
• ActiveRecordでBitemporal Data Modelを自然に 扱えるようにするためのGem • https://github.com/kufu/activerecord-bitemporal ActiveRecord::Bitemporalとは 30
• 導入が簡単 • ActiveRecord互換 • 履歴を操作・アクセスする様々なメソッドが提供されている • 複雑なSQLを意識せずにBitemporal Data Modelを扱える
ActiveRecord::Bitemporalのメリット 31
従業員モデルを例にする Employee( id: integer, name: string, # 名前 department: string,
# 部署 bitemporal_id: integer, # 履歴ID valid_from: date, # 有効開始日 valid_to: date, # 有効終了日 transaction_from: datetime, # システム登録日時 transaction_to: datetime, # システム終了日時 ) 32
4/1 山田太郎さん 入社 id bitemporal_id name department valid_from valid_to transaction_from
transaction_to 1 1 山田太郎 システム部 2025-04-01 9999-12:31 2025-04-01 10:00:00 9999-12:31 09:00:00 INSERT UPDATE SELECT employee = Employee.create( name: "山田太郎", department: "システム部" ) INSERT INTO "employees" ("name", "department", "bitemporal_id", "valid_from", "valid_to"..... UPDATE "employees" SET "bitemporal_id" = 1 WHERE "employees"."id" = 1; 33
6/1 営業部に異動 id bitemporal_i d name department valid_from valid_to transaction_from
transaction_to 1 1 山田太郎 システム部 2025-04-01 9999-12:31 2025-04-01 10:00:00 2025-06-01 10:00:00 2 1 山田太郎 システム部 2025-04-01 2025-06-01 2025-06-01 10:00:00 9999-12:31 09:00:00 3 1 山田太郎 営業部 2025-06-01 9999-12:31 2025-06-01 10:00:00 9999-12:31 09:00:00 INSERT UPDATE SELECT employee.update(department: "営業部") UPDATE "employees" SET "transaction_to" = $1 WHERE "employees"."id" = $2 INSERT INTO "employees" ("name", "department", "bitemporal_id", "valid_from", "valid_to", …. INSERT INTO "employees" ("name", "department", "bitemporal_id", "valid_from", "valid_to", …. 34
id bitemporal_i d name department valid_from valid_to transaction_from transaction_to 1
1 山田太郎 システム部 2025-04-01 9999-12:31 2025-04-01 10:00:00 2025-06-01 10:00:00 2 1 山田太郎 システム部 2025-04-01 2025-06-01 2025-06-01 10:00:00 9999-12:31 09:00:00 3 1 山田太郎 営業部 2025-06-01 9999-12:31 2025-06-01 10:00:00 9999-12:31 09:00:00 現時点で従業員情報を取得する INSERT UPDATE SELECT Employee.all 35 SELECT "employees".* FROM "employees" WHERE "employees"."transaction_from" <= '2025-09-27 10:00:00' AND "employees"."transaction_to" > '2025-09-27 10:00:00' AND "employees"."valid_from" <= '2025-09-27' AND "employees"."valid_to" > '2025-09-27'
履歴を扱うための様々なメソッドを提供 • valid_at(datetime): ◦ 指定した時間で有効なレコードを検索 • ignore_valid_datetime: ◦ 有効時間の制約を無視してすべての履歴を検索 •
ignore_transaction_datetime: ◦ システム時間の制約を無視して論理削除されたレコードも含めて検索 • ignore_bitemporal_datetime: ◦ すべての時間制約を無視してすべてのレコードを検索 • など 36
id bitemporal_i d name department valid_from valid_to transaction_from transaction_to 1
1 山田太郎 システム部 2025-04-01 9999-12:31 2025-04-01 10:00:00 2025-06-01 10:00:00 2 1 山田太郎 システム部 2025-04-01 2025-06-01 2025-06-01 10:00:00 9999-12:31 09:00:00 3 1 山田太郎 営業部 2025-06-01 9999-12:31 2025-06-01 10:00:00 9999-12:31 09:00:00 時間を指定して検索をする INSERT UPDATE SELECT Employee.valid_at("2025-04-10").all 37 SELECT "employees".* FROM "employees" WHERE "employees"."transaction_from" <= '2025-09-27 10:00:00' AND "employees"."transaction_to" > '2025-09-27 10:00:00' AND "employees"."valid_from" <= '2025-04-10' AND "employees"."valid_to" > '2025-04-10'
履歴運用の課題と向き合い方 38
履歴運用の課題 • 履歴に関するデータの調査が困難 • 履歴に関わるシステムの複雑化 • 履歴が不要なモデルもBitemporal Data Model化 39
課題1: 履歴に関するデータの調査が困難 • お客様から期待した挙動をしないというお問い合わせ • 原因: ◦ 不具合が原因で想定外の履歴データが作られていること • 難しいポイント
: ◦ 履歴の実データはとても複雑 ◦ 問題となっているデータの特定に時間を要する ◦ 調査に丸1日かかることもある... 40
Employee.ignore_bitemporal_datetime.bitemporal_for(Employee.first).pluck(:name, :department, :valid_from, :valid_to, :transaction_from, :transaction_to) => [["山田ハナコ", "システム部", Tue,
01 Apr 2025 10:00:00.000000000 JST +09:00, Fri, 31 Dec 9999 09:00:00.000000000 JST +09:00, Tue, 01 Apr 2025 10:00:00.000000000 JST +09:00, Thu, 01 May 2025 10:00:00.000000000 JST +09:00], ["佐藤ハナコ", "システム部", Tue, 01 Apr 2025 10:00:00.000000000 JST +09:00, Thu, 01 May 2025 10:00:00.000000000 JST +09:00, Thu, 01 May 2025 10:00:00.000000000 JST +09:00, Fri, 31 Dec 9999 09:00:00.000000000 JST +09:00], ["佐藤ハナコ", "営業部", Thu, 01 May 2025 10:00:00.000000000 JST +09:00, Fri, 31 Dec 9999 09:00:00.000000000 JST +09:00, Thu, 01 May 2025 10:00:00.000000000 JST +09:00, Fri, 31 Dec 9999 09:00:00.000000000 JST +09:00]] 41
履歴を可視化して理解する 42
ActiveRecord::Bitemporal::Visualizer.visualize(record, height:, width:, highlight: true) • レコードの履歴を視覚化するためのメソッド 履歴を可視化して理解する : その1
Sample 43
Googleスプレッドシートで履歴状態を描く 履歴を可視化して理解する : その2 Sample 44
課題2: 履歴に関わるシステムの複雑化 • 従業員情報の変更は日単位が基本 • しかし内部では「時分秒」まで保持していた ◦ 時分秒の違いによって、実装が複雑化 ◦ 利用者・開発者の双方にとって使いづらい
従業員がアルバイト (システム部 )→正社員(営業部)になった例 アルバイト 雇用形態 部署 システム部 営業部 2025/01/01 00:00:00 2025/04/01 10:11:23 2025/04/01 15:24:33 正社員 同日の別時刻の履 歴 45
有効期間の日付管理を timestamp型からdate型に変更 アルバイト 雇用形態 部署 システム部 営業部 2025/01/01 00:00:00 2025/04/01
10:11:23 2025/04/01 15:24:33 正社員 Before After アルバイト 雇用形態 部署 システム部 営業部 2025/01/01 2025/04/01 正社員 46
詳しくはSmartHR Tech Blogをご参考ください !! https://tech.smarthr.jp/entry/2025/09/12/115617 47
課題3: 履歴が不要なモデルも Bitemporal Data Model化 • いくつかのモデルではBitemporal Data Model化が不要 なものがある
• 経緯までは不明 48
非Bitemporal Data Model化をしたいが .... • データ移行が大変そう... • SmartHRでは実例なし 49
Bitemporal Data Modelを扱うときの気をつけること • ❗課題 ◦ データの肥大化 ◦ 実装が複雑化しやすい ◦
導入時に学習コストもかかる ◦ やめるのも大変そう...(社内事例無し) • 🛠 対策 ◦ 🧐本当に必要かどうか 、どこに適用するのか検討する 50
まとめ 51
• Bitemporal Data Modelを導入することで、時点の再現や履歴管 理が実現できる • 一方で運用上の様々な課題があるため、導入前に「本当にその 履歴が必要か」「どこまで適用するか」は検討が必要 まとめ 52
We Are Hiring! 53