反向调整
2006年1月2日
这是我在2000年代中期进行的进一步的企业应用程序架构开发写作的一部分。不幸的是,太多其他事情吸引了我的注意力,所以我没有时间进一步研究它们,而且在可预见的将来我也没有看到太多时间。因此,这些材料非常草稿形式,我不会进行任何更正或更新,直到我能找到时间再次处理它。
工作原理
对于每个需要调整的条目,您创建两个新条目。一个条目是对先前条目的简单反转,使用相同的发生日期和相同的金额,但符号相反。然后,您发布一个新的条目,该条目是旧金额应该是什么。
假设我们有 3 月份 50kwh 小时的用电量记录。此事件最初记录在 4 月 5 日,并在 4 月 10 日处理。这产生了 图 1中的对象。
图 1:调整前的事件。
然后在 6 月 1 日,我们意识到犯了一个错误,金额应该是 80 kwh。这导致了 图 2的结构。
您可能会惊讶地发现,反转条目出现在旧事件上,而不是新事件上。这是因为新事件本身可能会在以后被调整,在这种情况下,我们不想生成反转反转条目的条目。因此,将它们放在原始事件上更有意义。我们还将该事件标记为已调整,以明确表明它已被调整,因此不应再次调整。
图 2:调整后的事件
使用反向调整会导致这些反转对中出现大量条目。这意味着,如果任何人想查看条目列表,他们通常会想要查看排除所有反转对的条目。因此,您需要提供一个过滤此信息的查询方法。由于可能涉及大量数据,这通常也会影响您的数据库查询。
何时使用它
反向调整是条目不可变时的简单替代方案,因此您无法使用替换调整。主要缺点是它会导致大量条目:完成后,您将拥有之前每个条目的三个条目。这样做几次,您最终会得到很多条目,其中许多是反转对。虽然您可以过滤掉它,但它们仍然很麻烦。
差异调整是主要的替代方案。通常,它们之间的选择将取决于用户希望如何查看信息。如果条目是可变的,那么最好使用替换调整。通常,您会发现条目在某个日期之前是可变的,之后是不可变的。这将导致您将反向调整和替换调整结合起来。
示例:反转不正确的用电量(Java)
这是我们关于不正确的用电量以及如何反转它的示例。
public void setUp() { MfDate.setToday(2004,4,1); watson = new Customer("Dr Watson"); watson.setServiceAgreement(testAgreement()); usageEvent = new Usage(Unit.KWH.amount(50), new MfDate(2004, 3, 31), watson); eventList.add(usageEvent); eventList.process(); MfDate.setToday(2004,6,1); replacement = new Usage(Unit.KWH.amount(70), new MfDate(2004, 3, 31), watson, usageEvent); eventList.add(replacement); eventList.process(); }
在此实现中,我们为我们的会计事件提供了一个构造函数,其中包含一个用于调整事件的插槽。
class AccountingEvent...
public AccountingEvent(EventType type, MfDate whenOccurred, Subject subject, AccountingEvent adjustedEvent) { this.type = type; this.whenOccurred = whenOccurred; this.whenNoticed = MfDate.today(); this.subject = subject; this.adjustedEvent = adjustedEvent; adjustedEvent.setReplacementEvent(this); }
private AccountingEvent adjustedEvent;
在处理过程中,如果存在调整后的事件,则首先将其反转。反转只是为每个原始条目发布一个相反的条目。然后,事件处理的其余部分可以照常进行。
class AccountingEvent...
public void process() { assert !isProcessed; if (adjustedEvent != null) adjustedEvent.reverse(); subject.process(this); markProcessed(); }
public void reverse() { assert isProcessed(); for (Entry e : getResultingEntries()) reverseEntry(e); for(AccountingEvent ev : getSecondaryEvents()) ev.reverse(); } public void reverseEntry(Entry arg) { Entry reversingEntry = new Entry(arg.getAmount().negate(), arg.getDate()); Account targetAccount = subject.accountFor(arg.getAccount().type()); targetAccount.post(reversingEntry); }