帳戶

收集相關會計分錄並提供摘要行為

2005 年 1 月 22 日

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

帳戶已經存在很長一段時間了。無論是個人銀行帳戶、專案成本帳戶或企業會計科目表;它們出現在大多數涉及會計的問題中。

有各種方法可以思考帳戶是什麼。一個好方法是將它們視為分錄容器 會計分錄)。每當您建立分錄時,您就會將它放入帳戶中。然而,帳戶不僅僅是一個容器,它還具有提供摘要資訊的行為,例如餘額。

您也可以將帳戶視為某個價值的 稽核記錄,您用於金額,您不只想了解目前的價值,您還想了解它在過去任何時間點的價值,而且您想知道發生在該價值上的每個離散變化。

思考帳戶的第三種方法是將分錄中描述分錄種類的所有元素結合在一起的方法。在其中,它在分錄及其描述符之間提供了一個間接步驟。使用此方法可以更輕鬆地彙整所有具有類似特徵的分錄。

圖 1:帳戶可以作為分錄及其描述符之間的間接層級

當人們思考帳戶時,他們通常會想到貨幣帳戶。然而,帳戶可以用於金錢以外的事物。如果我的冰箱裡有二十四瓶 Black Butte,我可以將其視為一個帳戶。當我拿出兩瓶給辛蒂和我搭配我們的 Rogan Josh 食用時,我可以將其視為提取兩瓶。我不想為我的冰箱使用會計系統,但如果您有倉庫網路,那麼這種模式可能會很有意義。

運作方式

帳戶模式有兩個基本品質:保留分錄集合並提供這些分錄的摘要資訊。

由於您通常會取得大量的分錄,因此您經常需要最佳化計算餘額的方式。最佳化技術將取決於您需要從帳戶取得什麼類型的資訊。例如,如果您經常需要目前餘額,您可以將目前值快取在欄位中,並使用分錄向後計算以取得較早的餘額。

分錄通常會回溯到帳戶開啟的時間。但是,如果分錄太多,而且您不再需要它們的詳細資料,您可以用一個彙總分錄取代一堆分錄,其值為已移除分錄的餘額。這將維持彙總後的日期的餘額,但表示您將無法取得切入摘要分錄的資訊。但是,這個分錄可用作詳細分錄的代理。

您通常會預期帳戶中的所有分錄都使用相同的貨幣。因此,您通常會看到考量特定貨幣而建立的帳戶。可以建立在給予第一筆分錄時動態選擇貨幣的帳戶,但如何處理空帳戶的繁瑣行為表示這幾乎不值得花費力氣。

如果您允許帳戶持有不同貨幣的分錄,您將需要使用 金錢袋 進行摘要計算。

您看到的典型摘要行為是取得某個日期或某段時間的餘額、提款和存款。

何時使用

我傾向於在幾種不同的情況下感到使用 帳戶 的衝動。

第一種情況是我需要目前值、歷史值,以及能夠保留該值變更記錄的地方。如果我只需要目前值,我可以只使用一個欄位。如果我只需要目前值和歷史值,我可以使用 暫時屬性 - 儘管我不確定 帳戶 是否比 暫時屬性 複雜。

第二種情況是我已經在使用會計分錄 (15),但我想要彙整一組常見說明的所有分錄。這使得比較類似的分錄變得更容易。

您經常發現自己想知道是否要使用 帳戶 或單獨使用 會計分錄。如果您使用 帳戶,一些基於事件的模式(例如過帳規則網路 (52) 和差異調整 (59))會更容易使用。另一個因素是領域專家如何看待領域。如果他們使用帳戶來思考世界,那麼使用 帳戶 是合理的,如果不是,就不要使用。

不難看出 帳戶 和版本控制系統的概念之間的類比。事實上,您可以將 帳戶 視為將版本控制的概念套用在金錢上。但是,我不認為這表示您可以將 帳戶 用於非量化情況。雖然我猜您可以將程式碼版本控制視為文字檔案的餘額、提款和存款;對我來說,這有點過度延伸類比。

範例:簡單範例(Java)

帳戶的基本行為實際上非常簡單,包括收集分錄(Accounting Entry)並提供摘要資訊。收集分錄非常簡單。

class Account ...
  private Collection entries = new HashSet();
  private Currency currency;
  void addEntry(Money amount, MfDate date){
    Assert.equals(currency, amount.currency());
    entries.add(new Entry(amount, date));
  } 

摘要資訊沒有複雜太多。

class Account...
  Money balance(DateRange period) {
    Money result = new Money (0, currency);
    Iterator it = entries.iterator();
    while (it.hasNext()) {
      Entry each = (Entry) it.next();
      if (period.includes(each.date())) result = result.add(each.amount());
    }
    return result;
  }
  Money balance(MfDate date) {
    return balance(DateRange.upTo(date));
  }
  Money balance() {
    return balance(MfDate.today());
  }

通常,提供不同的行為來決定隨著時間推移從帳戶中新增或移除的總計很有用。