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
1
2.9k
履歴 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
プロポーザルを書くときに 私が考えていること/what-i-think-about-when-writing-a-proposal
hypermkt
0
390
Sidekiqで実現する 長時間非同期処理の中断と再開 / Pausing and Resuming Long-Running Asynchronous Jobs with Sidekiq
hypermkt
8
5.9k
脆弱性から学ぶ Webセキュリティ Part2/study-web-security-from-vulnerability2
hypermkt
5
3.3k
脆弱性から学ぶ Webセキュリティ/study-web-security-from-vulnerability1
hypermkt
5
2.5k
モバイルアプリ向けAPI 開発を通じて学んだこと / learned-from-developing-mobile-app-api
hypermkt
3
4.5k
Passportのパスワードグラントで独自の認証を実装する方法 / how-to-implement-original-authentication-for-passport-password-grant
hypermkt
1
820
Webpackで作る Vueコンポーネント開発環境 / Creating the Vue component development with Webpack.
hypermkt
3
4.2k
あの問題解きました! / solved the code
hypermkt
0
350
Vue.js で作る日報アプリケーション ハンズオン / vue-js-handson-by-nippo
hypermkt
0
460
Other Decks in Technology
See All in Technology
機械学習を「社会実装」するということ 2025年冬版 / Social Implementation of Machine Learning November 2025 Version
moepy_stats
4
670
type-challenges を全問解いたのでエッセンスと推し問題を紹介してみる
kworkdev
PRO
0
110
【保存版】「ガチャ」からの脱却:Gemini × Veoで作る、意図を反映するAI動画制作ワークフロー
nekoailab
0
110
AI エージェント活用のベストプラクティスと今後の課題
asei
2
390
命名から始めるSpec Driven
kuruwic
1
550
社内外から"使ってもらえる"データ基盤を支えるアーキテクチャの秘訣/登壇資料(飯塚 大地・高橋 一貴)
hacobu
PRO
0
8.3k
事業状況で変化する最適解。進化し続ける開発組織とアーキテクチャ
caddi_eng
1
8.7k
Introduction to Sansan, inc / Sansan Global Development Center, Inc.
sansan33
PRO
0
2.9k
20251127 BigQueryリモート関数で作る、お手軽AIバッチ実行環境
daimatz
0
250
Claude Code はじめてガイド -1時間で学べるAI駆動開発の基本と実践-
oikon48
7
3.5k
Android Studio Otter の最新 Gemini 機能 / Latest Gemini features in Android Studio Otter
yanzm
0
470
生成AIシステムとAIエージェントに関する性能や安全性の評価
shibuiwilliam
2
250
Featured
See All Featured
How to Create Impact in a Changing Tech Landscape [PerfNow 2023]
tammyeverts
55
3.1k
Java REST API Framework Comparison - PWX 2021
mraible
34
9k
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
140
34k
The Language of Interfaces
destraynor
162
25k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
350
KATA
mclloyd
PRO
32
15k
10 Git Anti Patterns You Should be Aware of
lemiorhan
PRO
659
61k
Agile that works and the tools we love
rasmusluckow
331
21k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Code Review Best Practice
trishagee
72
19k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
I Don’t Have Time: Getting Over the Fear to Launch Your Podcast
jcasabona
34
2.5k
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