調査日: 2026-03-04
本レポートは、以下を整理することを目的とする。
- T字ER(TM思考法)でリソース系とイベント系を分離して設計した後、どのような運用が現実的か。
- カレントディレクトリ内の5アプリ(
saleor/spree/erpnext/redmine/discourse)が、どの設計パターンに該当するか。 - T字ERに関する学習コンテンツ・実装/導入プロジェクト情報(公開情報ベース)を整理すること。
公式説明では、TM(T字形ER図)は「企業オブジェクト(モノ・コト)とイベント(コト)」を中心に業務をモデル化する考え方として説明されている。
- 公式理論ページでは、業務イベント連鎖、企業オブジェクトのライフサイクル、計画/予定/実績などを一体で扱う視点が示されている。
参照: https://www.its-mine.co.jp/service/theory/
また、実務解説(非公式だが実装向けに詳細)では、関係の作り方として以下が整理されている。
R:R(リソース:リソース)E:E(イベント:イベント)R:E(リソース:イベント)- 再帰関係
参照(実務解説):
リソース/イベント分離後は、運用が以下の責務に分かれる。
- 書き込み運用
- イベント発生時にイベント表へ追記する。
- 必要に応じてリソース表(現在値)を更新する。
- 読み取り運用
- 参照性能のため、リソース表や集約値を主に参照する。
- 監査・追跡・再計算はイベント表を参照する。
- 状態遷移運用
- 「状態そのもの」と「状態遷移履歴」を分離し、遷移はイベントとして記録する。
- 外部連携運用
- イベントをフックしてWebhook/通知/非同期連携を実行する。
- 保守運用
- イベント定義変更時のデータ移行(type名変更、payload再構成)
- 将来再計算のための台帳整合性維持(キャンセル時の反転仕訳・逆仕訳)
- パターンA:
リソース本体 + 監査/行動イベント履歴 - パターンB:
リソース本体 + 状態遷移イベント - パターンC:
取引伝票イベント + 会計/在庫台帳イベント + マスタ - パターンD:
リソース本体 + ドメインイベントログ + 外部イベント連携
| アプリ | 該当パターン | 判断要点 |
|---|---|---|
| saleor | パターンD | OrderとOrderEventを分離し、イベント作成とWebhook発火を標準化 |
| spree | パターンB(+A) | spree_ordersとspree_state_changesで状態遷移履歴を分離 |
| erpnext | パターンC | Item/Customer(Setup)とSales Order/Invoice(Document)とGL/SLE台帳を分離 |
| redmine | パターンA | issues本体とjournals/journal_details/time_entries履歴を分離 |
| discourse | パターンA | topics/posts本体とpost_actions/user_histories/email_logs行動履歴を分離 |
- リソース本体:
Order
saleor/saleor/order/models.py:109 - イベント本体:
OrderEvent(orderFK,type,parameters)
saleor/saleor/order/models.py:868-909 - イベント追加運用:
OrderEvent.objects.create(...)をイベント関数群で実施
saleor/saleor/order/events.py:37-86 - 外部連携運用:
call_order_event(s)でWebhook対象イベントを解決し発火
saleor/saleor/order/actions.py:246-325 - 保守運用: 既存イベントtypeやpayloadをマイグレーションでバッチ更新
saleor/saleor/order/migrations/0163_order_events_rename_transaction_events.py:35-75
saleor/saleor/order/migrations/0145_rewrite_order_events.py:9-63
- リソース本体:
spree_orders
spree/server/db/schema.rb:709-742 - 状態遷移イベント:
spree_state_changes
spree/server/db/schema.rb:1425-1435 - 在庫/クレジットイベント:
spree_stock_movements,spree_store_credit_events
spree/server/db/schema.rb:1492-1502, 1525-1537 - モデル運用:
Orderがhas_many :state_changesを持ち、状態変更時にstate_changes.create
spree/spree/core/app/models/spree/order.rb:120-126, 622-639 - イベントモデル:
StateChangeはstatefulポリモーフィック関連
spree/spree/core/app/models/spree/state_change.rb:2-6
- マスタ(リソース):
Item,Customerはdocument_type: "Setup"
erpnext/erpnext/stock/doctype/item/item.json:9
erpnext/erpnext/selling/doctype/customer/customer.json:10 - 取引伝票(イベント):
Sales Order,Sales Invoiceはis_submittable: 1
erpnext/erpnext/selling/doctype/sales_order/sales_order.json:1741
erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.json:2325 - 台帳(イベント):
GL Entry,Stock Ledger Entryは voucher参照を保持
erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py:68-71, 132-134
erpnext/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py:71-74, 194-196 - 運用: Sales Invoice submit/cancel時に在庫台帳→会計台帳を更新/反転
erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py:449-494, 585-616, 1541-1567 - 運用: Sales Order submit/cancelで予約在庫や後続ドキュメント整合を更新
erpnext/erpnext/selling/doctype/sales_order/sales_order.py:495-517, 525-546, 625-646
- リソース本体:
issues,projects,users
redmine/db/migrate/001_setup.rb:122-137, 176-185, 203-210 - 変更履歴イベント:
journals,journal_details
redmine/db/migrate/007_create_journals.rb:8-25 - 工数イベント:
time_entries
redmine/db/migrate/032_create_time_entries.rb:3-18 - モデル運用:
Issueがhas_many :journals,has_many :time_entries
redmine/app/models/issue.rb:32-43 - モデル運用:
Journalはjournalizedポリモーフィック、details保持
redmine/app/models/journal.rb:24-31
- リソース本体:
topics,posts
discourse/app/models/topic.rb:252, 2262-2271
discourse/app/models/post.rb:32, 1235-1244 - 行動イベント:
post_actions
discourse/app/models/post.rb:38
discourse/app/models/post_action.rb:3-11, 253-261 - 監査イベント:
user_histories(コメントで用途明示)
discourse/app/models/user_history.rb:3-6, 468-476 - 通知配送イベント:
email_logs
discourse/app/models/email_log.rb:3, 123-131
判定基準は次の3類型で整理した。
Resource SSoT: 現在値はリソース表が正とされ、イベントは監査/通知/分析を担う。Event SSoT: イベント列から現在値・過去時点値(as-of)を再計算できる。Hybrid: 領域ごとに SSoT が分かれる(例: 台帳は Event SSoT、マスタは Resource SSoT)。
| アプリ | SSoT判定 | イベントの主用途 | イベントから as-of 復元できるか | ドメインイベント情報量の評価 |
|---|---|---|---|---|
| saleor | Resource SSoT | 監査・顧客表示・Webhookトリガ | 限定的(完全復元は困難) | 中(イベント種別は多いが payload は表示/通知向け) |
| spree | Resource SSoT(部分的に Hybrid) | 状態遷移監査・在庫/クレジット操作履歴 | 状態履歴は可、注文全体は困難 | 中(状態差分は十分、注文全体スナップショット不足) |
| erpnext | Hybrid(会計/在庫は Event SSoT 寄り) | 台帳記録・取消反転・監査 | 会計/在庫は可、業務オブジェクト全体は不可 | 高(debit/credit・qty差分・時点情報が揃う) |
| redmine | Resource SSoT | 変更履歴・監査・通知 | 一部可(Issue属性差分の再生)、全体は困難 | 中(old/new差分はあるが、汎用リプレイ設計ではない) |
| discourse | Resource SSoT | 行動ログ・モデレーション監査・配送ログ | 一部可(like等の限定領域)、全体は困難 | 低〜中(用途特化ログが中心) |
OrderEvent.parametersは「storefront 表示のための値」と明記されており、ドメイン完全状態の保存を目的としていない。
saleor/saleor/order/models.py:868-885- イベントは
OrderEvent.objects.create(...)で追記されるが、order_confirmed_eventのように軽量な type 中心レコードが多い。
saleor/saleor/order/events.py:345-361 - 一方で注文状態は
update_order_status/order.save(update_fields=["status", ...])によりリソース本体へ確定される。
saleor/saleor/order/utils.py:214-230
saleor/saleor/order/actions.py:438-442 - さらにイベント type/payload を後から更新するマイグレーションが存在するため、厳密な「不変イベント列」前提には立っていない。
saleor/saleor/order/migrations/0145_rewrite_order_events.py:9-31
saleor/saleor/order/migrations/0163_order_events_rename_transaction_events.py:28-65
評価:
- as-of 復元は「イベントタイムライン表示」には使えるが、注文の完全状態(行・金額・配送・決済の一貫状態)をイベントのみで復元する設計にはなっていない。
- 注文の現在値(合計・税・支払/配送状態)は
spree_ordersに保持され、OrderUpdater#persist_totalsが直接更新する。
spree/server/db/schema.rb:709-747
spree/spree/core/app/models/spree/order_updater.rb:123-137 - 状態遷移は
spree_state_changesにprevious_state/next_stateで記録される。
spree/server/db/schema.rb:1425-1434
spree/spree/core/app/models/spree/order.rb:622-639
spree/spree/core/app/models/spree/order/checkout.rb:45-53 - よって
stateの時系列追跡は可能だが、注文全体 as-of(金額内訳・明細全体)を state change だけで復元はできない。 - ただし
StoreCreditはstore_credit_eventsを持ち、金額変更時にイベントを保存しており、領域限定ではイベント再計算余地がある。
spree/spree/core/app/models/spree/store_credit.rb:28-49
spree/spree/core/app/models/spree/store_credit.rb:233-250
spree/spree/core/app/models/spree/store_credit_event.rb:9-23
評価:
- 注文ドメイン全体では Resource SSoT。クレジット等の一部サブドメインは Hybrid 寄り。
GL EntryとStock Ledger Entryは voucher 紐付けと時点情報を保持し、取消フラグも持つ。
erpnext/erpnext/accounts/doctype/gl_entry/gl_entry.py:68-71
erpnext/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py:71-74, 101-105Sales Invoiceの submit/cancel で在庫台帳・会計台帳を作成/反転しており、台帳の完全性を運用で担保している。
erpnext/erpnext/accounts/doctype/sales_invoice/sales_invoice.py:449-494, 585-616- as-of 計算は
posting_date <= .../posting_datetime <= ...を使って実装されている。
erpnext/erpnext/accounts/utils.py:227-352
erpnext/erpnext/stock/utils.py:60-94, 97-134
erpnext/erpnext/stock/stock_ledger.py:1779-1880
評価:
- 会計残高・在庫残高は、イベント(台帳)から as-of 復元できる設計。
- ただし
Item/Customerなどマスタや伝票ヘッダ自体は別テーブルの現在値管理なので、全ドメイン一律の Event SSoT ではない。
Issueが本体で、journals/time_entriesは関連履歴としてぶら下がる。
redmine/app/models/issue.rb:32-43- 更新後に
create_journalが呼ばれ、journalize_changesで属性のold_value/value差分を記録する。
redmine/app/models/issue.rb:2039-2044
redmine/app/models/journal.rb:267-333
redmine/db/migrate/007_create_journals.rb:8-21 journalized_attribute_namesは Issue列の広い範囲を対象にするため、Issue 属性単位では as-of 再生余地がある。
redmine/app/models/issue.rb:891-898
評価:
- Issue属性の差分追跡は十分だが、履歴は監査/通知用途が中心で、システム全体をイベントリプレイで復元する設計ではない。
- 本体は
Topic/Postで、PostAction/UserHistory/EmailLogは行動・監査・配送ログとして別管理。
discourse/app/models/topic.rb:250-253
discourse/app/models/post.rb:31-39
discourse/app/models/post_action.rb:3-21
discourse/app/models/user_history.rb:3-12
discourse/app/models/email_log.rb:3-25 PostActionは保存時に集計カウンタ(like_countなど)を更新するため、特定指標ではイベント→現在値集約がある。
discourse/app/models/post_action.rb:209-247- ただし、これは「全ドメイン状態の復元」ではなく、用途特化した集計更新。
- 投稿本文の改訂履歴は
PostRevision.modifications側で扱っており、今回の行動イベント群とは役割が分かれている。
discourse/app/models/post_revision.rb:3-11
評価:
- as-of 復元は領域限定(例: アクション数、投稿改訂履歴)で、プロダクト全体をイベントのみで復元する設計ではない。
判定はコード/スキーマからの推定。
| アプリ | R:R | R:E | E:E | 再帰 |
|---|---|---|---|---|
| saleor | あり(Order-User等) | あり(Order-OrderEvent) | 強い(OrderEventの関連イベント、イベントtype移行) | あり(OrderEvent.related) |
| spree | あり(Order-Payment-Shipment) | あり(Order-StateChange) | 中(在庫/クレジットイベント鎖) | 弱 |
| erpnext | あり(Item-Customer等) | あり(伝票->台帳) | 強い(voucher経由で伝票/台帳イベント連鎖) | 弱 |
| redmine | あり(Issue-Project-User) | あり(Issue-Journal/TimeEntry) | 中(Journal-JournalDetail) | あり(Issue relation系) |
| discourse | あり(Topic-Post-User) | あり(Post-PostAction、Topic/Post-UserHistory) | 中(行動ログ連鎖) | あり(Topic/Postの自己参照構造) |
- TM思考法® 理論ページ(公式)
- URL: https://www.its-mine.co.jp/service/theory/
- 内容: TMの基本思想、7つの視点、モデリングの考え方。
- TM思考法® 入門セミナー(公式)
- URL: https://www.its-mine.co.jp/tmer/2026/02/12/beginner-seminar-2025-no-01/
- 確認できた開催日(記事記載): 2026-05-29, 2026-06-26
- TER-MINE入門編Webinar(公式)
- URL: https://www.its-mine.co.jp/tmer/2026/02/03/ter-mine-intro-webinar-2502/
- 内容: TER-MINEの使い方入門。
- TMD-Maker(オープンソース)
- ドキュメント: https://docs.tmdmaker.org/
- テンプレート: https://docs.tmdmaker.org/templates/
- デモ: https://docs.tmdmaker.org/demos/
- 内容: TM/TMDを作図するためのOSSツール。学習用テンプレートとサンプル図がある。
- ITS社の導入実績(公式)
- URL: https://its-inc.co.jp/
- 公開情報上、物流/製造/病院/出版/損保などでの開発実績を記載。
- 30年以上のTM開発・教育・ツール提供を継続と記載。
- TER-MINE(モデリング/実装支援ツール)
- サービスページ: https://www.its-mine.co.jp/service/ter-mine/
- 公開情報上、エディタ・作図・入出力・クラウド保存等の機能を提供。
補足:
- 「TMを厳密に採用したアプリケーション本体のOSS実装」は、公開情報では限定的だった。
- 公開されているのは、方法論資料・セミナー・モデリングツール・導入会社の実績情報が中心。
- 分離後の運用は「イベント追記」「現在値参照」「外部通知」「履歴保守(移行/反転仕訳)」の4系統で回す設計が現実的。
- ローカル5アプリはすべて
R:Eを実装しており、用途に応じて次の型に分かれる。
- 監査ログ型:
redmine,discourse - 状態遷移型:
spree - 台帳二層型:
erpnext - イベント駆動統合型:
saleor
- SSoT観点では、5アプリ中4アプリ(
saleor,spree,redmine,discourse)は Resource SSoT が中心で、erpnextのみ「会計/在庫台帳は Event SSoT 寄り」という Hybrid 構造が明確。 - as-of 復元は、
erpnextの台帳領域では実装済み(posting_date/posting_datetime条件集計)だが、他4アプリは監査・通知・分析中心で、イベントのみの完全復元は前提にしていない。 - T字ER学習は、現状は「公式理論ページ + セミナー + TMD-Makerテンプレート/デモ」が最短。
- 実案件情報は企業導入実績としては確認できるが、OSSの「TM準拠実装」公開例は少ない。
- TM思考法® 理論: https://www.its-mine.co.jp/service/theory/
- TM思考法® 入門セミナー: https://www.its-mine.co.jp/tmer/2026/02/12/beginner-seminar-2025-no-01/
- TER-MINE入門編Webinar: https://www.its-mine.co.jp/tmer/2026/02/03/ter-mine-intro-webinar-2502/
- TER-MINEサービス: https://www.its-mine.co.jp/service/ter-mine/
- ITS株式会社(導入実績/開発領域): https://its-inc.co.jp/
- TMD-Maker docs: https://docs.tmdmaker.org/
- TMD-Maker templates: https://docs.tmdmaker.org/templates/
- TMD-Maker demos: https://docs.tmdmaker.org/demos/
- 実務解説(関係パターン): https://www.rainbow.se/opensource/tmer/
- 実務解説(関係パターン): https://sehippocampus.com/memo/28/