時間模式

彙整了各種模式,可協助回答有關資訊過去狀態的問題。這些問題包括「1999 年 7 月 1 日 Martin 的地址為何」以及「1999 年 8 月 12 日寄送帳單給 Martin 時,我們認為 Martin 的地址為何」。

2005 年 2 月 16 日



這是 進階企業應用程式架構開發 的一部分,我於 2000 年代中期撰寫。很遺憾,自此之後有太多其他事情吸引了我的注意力,因此我沒有時間進一步研究,而且在可預見的未來也看不到太多時間。因此,這份資料仍處於草稿階段,直到我找到時間再次研究它為止,我不會進行任何更正或更新。

萬物變遷。如果我們儲存有關世界資訊,這可能不是問題。畢竟,當某件事發生變化時,電腦化記錄系統的其中一個重要價值在於它允許我們輕鬆更新記錄,而無需使用修正液或重新輸入整頁資訊。

然而,當我們需要記錄變更歷程時,事情就變得有趣了。我們不僅想知道世界的狀態,我們還想知道六個月前的世界狀態。更糟糕的是,我們可能想知道兩個月前我們認為六個月前的世界狀態為何。這些問題引領我們進入一個迷人的時間模式領域,這一切都與組織物件有關,這些物件讓我們能夠輕鬆找到這些問題的答案,而不會完全混淆我們的領域模型。在所有物件建模的挑戰中,這既是最常見的挑戰之一,也是最複雜的挑戰之一。

解決此問題最簡單的方法是使用稽核記錄。您關注的是保留變更記錄,但您不希望經常回頭使用它。因此,您希望它易於建立,並且對您的工作影響最小。當有人需要查看它時,您預期他們必須做很多工作才能找出資訊。如果他們不需要經常執行此操作,也不需要快速取得結果資訊,那就沒問題。事實上,如果您使用資料庫,它是免費的。

當資訊必須更容易存取時,您需要多做一點工作。人們在這種情況下使用最常見的模式是效期。這僅僅標示一個物件為它被視為有效的時間段(圖 1)。然後,當您操作物件時,您使用這個時間段在正確的時間取得正確的物件。

圖 1:使用明確的日期範圍顯示效期。

效期的問題在於它很明顯且明確。在所有內容中使用物件的每個人都必須了解時間面向。這可能會大幅複雜化模型。因此,如果時間問題很普遍,那麼在不需要它們時隱藏它們,並在需要它們時讓它們更容易使用通常是有意義的。

如果您只有幾個需要容易存取時間資訊的物件屬性,那麼您可以在這些物件上使用時間屬性時間屬性的基本重點是,您移除明顯的效期日期,而是使用看起來像一般屬性的內容,但使用將日期作為引數的存取器。這讓您可以詢問「此屬性在 2000 年 4 月 1 日的值為何」。

圖 2:使用時間屬性作為地址。

時間屬性的精髓是這個概念,即具有由日期參數化的存取器,這讓這個屬性的使用者免於必須瀏覽一堆具有效期的物件。因此,時間屬性效期並非互斥的模式。您可以使用地址使用物件(使用效期)來實作時間屬性介面。如果地址使用物件承擔其他責任,您可以提供方法存取那些需要這些其他責任的客戶的地址使用,以及為那些發現更方便的人提供時間屬性介面。

時間屬性引入了使用時間為基礎的索引來參照事物的概念,但只將它作為單一屬性。如果您在一個物件上有許多時間屬性,事情可能會變得笨拙。有兩種方法可以處理這個問題。第一個是使用快照,它會給您一個物件,用來參照在時間點上真實物件的狀態。因此,如果您想要在 2000 年 4 月 1 日詢問客戶很多問題,您可以要求該客戶在 2000 年 4 月 1 日的快照,然後詢問屬性的值,而不需要不斷重複日期。

圖 3:快照顯示某個物件在某個日期的檢視。

快照 讓您檢視某個物件在某個日期的狀態,但有時您真的想要思考一個物件在時間推移中經歷明確版本時所發生的變更。這種需求導致了 時態物件 的產生。 時態物件 有許多形式,但最直接的形式有兩個類別,如 圖 4 所示。合約類別通常由其他物件參考,因為它表示時間推移中的合約。它包含一系列版本,記錄合約在每次變更時的狀態。這讓人們可以參考不變的合約概念,或特定時間點的特定版本。

圖 4:時態物件有明確的版本歷程,因此每次變更都會產生一個新版本。

實際上,我看到 效期 被廣泛使用,但通常是因為人們不太熟悉 時態屬性 和其他更精密的模式。 時態屬性時態物件 等模式之所以有效,是因為它們隱藏了時態行為的大部分機制。然而,效期 仍然很重要:當人們想要一個僅在特定時間段內有效的明確物件時,它是正確的選擇。

時間維度

正如我上面所描述的,時間在建模中是一個相當具有挑戰性的概念。然而,我跳過了時態模型中最尷尬的方面。我們都學過,即使只是從糟糕的科幻小說中,時間是第四個維度。問題在於這是不正確的。

我發現描述這個問題的最佳方法是舉例說明。假設我們有一個薪資系統,該系統知道員工從 1 月 1 日開始的費率為 100 美元/天。2 月 25 日,我們以這個費率執行薪資。3 月 15 日,我們得知員工的費率自 2 月 15 日起變更為 211 美元/天。當我們被問及 2 月 25 日的費率時,我們應該如何回答?

從某種意義上來說,我們應該回答 211 美元,因為現在我們知道那是費率。但我們通常不能忽視在 2 月 25 日我們認為費率是 100 美元,畢竟那是我們執行薪資的時候。我們列印了一張支票,寄給他,他兌現了它。這些都發生在他的費率金額的基礎上。如果稅務機關詢問我們他在 2 月 25 日的費率,這就變得重要了。

事實上,我們可以認為,的確有兩段對我們而言很重要的 Dinsdale 薪資記錄。我們現在知道的記錄,以及我們在 2 月 25 日知道的記錄。的確,一般來說,我們可以說,不僅每一天的 Dinsdale 薪資記錄都有記錄,Dinsdale 薪資記錄的記錄也有記錄。時間不是第四維度,而是第四和第五維度!

我將第一個維度視為實際時間:某件事發生的時間。第二個維度是記錄時間,我們知道它的時間。每當某件事發生時,總會有這兩個時間隨之而來。Dinsdale 的加薪在實際時間是 2 月 15 日,在記錄時間是 3 月 15 日。同樣地,當我們詢問 Dinsdale 的薪資時,我們真的需要提供兩個日期:記錄日期和實際日期。

記錄日期實際日期Dinsdale 的薪資
1 月 1 日1 月 1 日100 美元/天
2 月 25 日2 月 25 日100 美元/天
3 月 14 日2 月 25 日100 美元/天
3 月 15 日1 月 1 日100 美元/天
3 月 15 日2 月 25 日211 美元/天

我們可以這樣思考這兩個維度。實際記錄是回顧實際時間。如果我查看我目前的實際記錄,那麼我看到 Dinsdale 的薪資在 2 月 15 日之前是 100 美元,在那時增加到 211 美元。然而,那是今天在記錄時間的實際記錄。如果我查看 2 月 25 日的實際記錄,那麼 Dinsdale 從 1 月 1 日起以 100 美元支付,而 211 美元從未包含在內。記錄時間中的每一天(嚴格來說,每一個時間點)都有實際記錄。這些記錄是不同的,因為我們發現我們過去認為是真實的事情不再是真實的。

從另一個角度來說,我們可以說實際記錄中的每一天都有記錄記錄。記錄記錄告訴我們我們對那一天的了解如何隨著時間而改變。因此,實際時間中的 2 月 25 日有一個記錄記錄,其中寫道,截至 3 月 15 日,Dinsdale 的薪資為 100 美元,在那時達到 211 美元。

讓我們進一步舉這個例子。假設我們在 3 月 26 日的薪資運行中進行了相應的調整。4 月 4 日,我們被告知員工的先前資訊錯誤,實際上費率在 2 月 15 日變更為 255 美元。現在,我們如何回答「員工在 2 月 25 日的費率是多少?」這個問題。

我看到成長中的開發人員在面對這種事情時會咬掉自己的頭。但是,一旦你意識到一切都歸結為這兩個維度的概念,事情就會變得簡單很多。將這個較早的表格擴充套件,可以視覺化這一點的方法之一

記錄日期實際日期員工費率
1 月 1 日1 月 1 日100 美元/天
2 月 25 日2 月 25 日100 美元/天
3 月 14 日2 月 25 日100 美元/天
3 月 15 日1 月 1 日100 美元/天
3 月 15 日2 月 25 日211 美元/天
3 月 26 日2 月 25 日211 美元/天
4 月 4 日1 月 1 日100 美元/天
4 月 4 日2 月 25 日$255/天

如果我們檢視目前的實際記錄(也就是記錄日期為今天的實際記錄),我們會說 Dinsdale 的薪資從 1 月 1 日開始為 $100,並於 2 月 15 日調漲至 $255。對於目前的實際記錄,$211 的費率完全不會出現,因為這從未發生過。如果我們檢視 3 月 26 日的實際記錄,我們會看到 Dinsdale 的薪資在 2 月 15 日之前為 $100,然後調漲至 $211。在 3 月 26 日的實際記錄中,$255 的費率從未發生過,因為我們還不知道這件事。

我們也可以考慮 2 月 25 日的記錄記錄。現在,這筆記錄記錄表示費率為 $100(在當天),直到 3 月 15 日變更為 $211。然後在 4 月 4 日再次變更為 $255。

一旦你了解這兩個面向,思考問題就會變得容易許多,但想到你必須實作這類事項,就會令人害怕。幸運的是,在實作時,你可以採取許多方法來簡化問題。

第一個簡化方式是,使用 稽核記錄 來因應這些變更並不困難。你所需要做的,就是記錄每個條目的記錄日期和實際日期。這個簡單的練習就足以讓任何記錄在兩個面向都保持有效,而且我相信即使你只煩惱其中一個面向,這也是值得執行的。

第二個簡化方式是,你通常不希望你的模型處理兩個面向。這裡的重要事項是知道你的模型中有哪些,以及你將哪些留給 稽核記錄

如果我們想要保留事物的記錄,了解事物隨著時間如何變更,但不在乎我們何時得知變更,我們會說這是實際時間。因此,如果我保留員工地址的記錄,我可能會選擇將其保留為實際時間屬性。對於協助線上查詢的資訊系統,這很有效,因為當你存取資料庫時,你通常會想要了解實際記錄。

當您有一個系統會根據物件狀態產生帳單等動作時,就會出現記錄時間事實。這些事情會引發有關帳單如何計算的問題,這會讓您需要知道計算帳單時物件的狀態為何。記錄時間事實通常可以比喻成軟體中的版本控制系統,您可以在其中返回並說「4 月 1 日這個檔案是什麼樣子?」

當然,有時您需要兩個維度同時存在,這稱為雙時間事實。基本上,雙時間資訊總是需要兩個日期。

雙時間性是完整的解決方案,但總是值得思考如何解決它。舉例來說,從帳單計算中得知。如果您想找出帳單為何會變成這樣,其中一種可能性是擁有完全雙時間的資料庫。但是,在計算帳單時儲存計算的詳細追蹤記錄通常會更好。這以比雙時間物件模型更簡單的方式滿足需求。

更新時間記錄

到目前為止,我只在存取資訊的方面討論時間資訊,而不是在更新資訊的方面。您允許更新的方式會導致更多決策,但其中許多涉及有用的簡化。

一般來說,變更時間記錄相當混亂。如果我們有一位員工的費率從 2 月 15 日到 4 月 15 日為 211 美元,完全通用的更新將允許變更開始日期、結束日期和值的任何組合。提供一個介面來執行這項操作很尷尬,因為它要求客戶深入了解時間資訊的運作方式。

第一個簡化是您可能只有附加變更。附加變更總是會加到記錄的結尾。附加變更的範例是「讓員工的費率從 2 月 15 日起為 211 美元」。雖然乍看之下這看起來像是您只從組合中移除一筆資料,但後果實際上會讓更新大幅簡化。實際發生的更新大多是附加的,因此可以大幅簡化客戶端。此外,您可以使用一些附加更新的組合進行任何變更。雖然這會讓具有複雜歷程的物件變得非常混亂,但此屬性可以透過只支援附加介面來簡化具有簡單歷程的物件。

第二個簡化只允許目前的更新。目前的更新只允許記錄在今天生效日期的變更。一般來說,即使是附加變更也可能發生在過去或未來的任何日期。目前的變更完全不需要日期資訊,讓您可以擁有完全非時間性的更新介面。

目前的更新看起來好得令人難以置信,如果只用目前的更新更新,為什麼還要時間資訊?這裏的好消息是,紀錄時間資訊只能用目前的更新更新。不僅追溯變更紀錄時間資訊會破壞紀錄的完整性,而且沒有任何情況需要追溯變更,而無法由實際時間維度處理(除非欺詐是您的需求之一)。這是一大步,因為這表示整個維度都有隱形的更新 - 查詢時您只需要擔心紀錄時間,更新時則不用。

其他讀物

由於這是一個複雜的領域,因此隨著人們探索這些問題,它產生了一些其他著作。這個領域最全面的處理方式是 Snodgrass。他的著作基於關聯式資料庫,書中大部分內容都是關於如何在 SQL 中處理這些問題。然而,問題是一樣的。此外,他提出的許多評論和使用的術語與即將推出的 SQL 標準中關於時間資料庫的內容相同。這本書現在已經絕版,但您可以從 Richard Snodgrass 的出版物頁面 取得 PDF 檔。

術語問題是我仍然不太滿意的地方。他使用與我相同的兩個維度,但他的術語不同。我稱之為實際時間,他稱之為有效時間,我稱之為紀錄時間,他稱之為交易時間。在這些模式的早期版本中,我遵循他的術語,但許多人表示他們發現這些術語很難理解。因此,我決定使用不同的術語。另一個不同之處是他將隨著時間而變化的表格稱為順序。當然,物件一直使用順序,不僅用於時間目的,所以我將隨著時間而變化的東西稱為時間。

[Anderson] 是時間模式的最佳集合之一。我再次掠奪想法,但改變了一些術語。我使用術語版本而不是版本,表示在某個時間段內屬性或物件的值。他的變更記錄關聯歷史模式與 時間屬性 相同。我認為兩個 Anderson 模式之間的差異更像是實作問題。他的自我的歷史模式與 時間物件 相同。

在同一本 PLoPD 4 書中,您會找到一篇由 Andy Carlson 與我本人以及 Sharon Estepp 合作撰寫的論文 [Carlson et al]。這篇論文提供了 時間屬性快照 的早期描述。它還提到了一個模式時間關聯,我現在將其視為 效力 的一種用法。

也提交給 PLoP,但目前尚未公開的是 [Arnoldi et al]。這引入了許多有趣的點子,我尚未在這裡的模式中完全探討(至少目前還沒有)。

[Anderson] 和 [Arnoldi et al] 都考慮使用 時間點 以外的物件作為時間記錄的索引。[Anderson] 使用(單一時間)事件,而 [Arnoldi et al] 使用觀點:實質上是兩個時間點組合成一個單一物件。雖然這兩種方法都有其可取之處,但我沒有在這裡使用它們,因為我覺得使用時間點可以滿足您的大部分需求,而且解釋起來也比較簡單。


重大修訂

2005 年 2 月 16 日:將這些模式移至 EAA 開發部分。

2001 年 1 月 15 日:將雙維度術語從有效/交易變更為實際/記錄。

2000 年 8 月 28 日:第一份草稿