Re: aggregate boundries



Responding to Kurbylogic...

I'm attempting to create a program to help me budget my money.  I
want to do something different form money/quicken in that I assign my
existing assets to categories such as mortgage and savings each expense
will then be deducted from the category and credited by my paychecks
allowing me to more easily determine how much money I have set aside
for the category.    But I'm having a bit of trouble determining how
to maintain the relationship invariants in OO.

Account, Category, and Payee are root objects
Category is a composite containing other Categories
Account owns Transactions and Transaction owns TransactionCategory

OK, so far this sounds like:

              * identified in  R1
[Transaction] -------------------------- [Payee]
     | *                          pays 1
     | identified in
     |
     | R2
     |
     | updates
     | 1                           1 child of
 [Account]              [Category] -----------+
     |                       |                |
     +--------+--------------+                |
              | R3                            | R4
              _                               |
              V         * contains            |
      [CategoryContext] ----------------------+
      + currentBalance

Note that the Category structure is just a simple GoF Composite pattern. What I am unclear about is what [Payee] represents. I assume this is who the money goes to when a check is written. You suggest below that [Payee] may be hierarchically organized as well. In that case one would need a similar Composite pattern for [Payee] as well (i.e., the name on the check is only provided by a leaf).

The invariants are that Transaction.Payee is member of Payees and
TransactionCategory.Category is member of Categories

I'm not sure why Transaction needs to know the Category. Isn't a Transaction just an update of a specific Account? If so, then the Category is defined by the R4 hierarchical relationship. So if one has a Transaction in hand one just needs to navigate R2 -> R4 to obtain the immediate Category. One can then "walk" all the way up the Category hierarchy via R4 to handle the debits and credits at each level.


I've created a larger aggregate DataContext to encapsulate the list
of accounts, payees and root categories.  I use an identity map during

I suspect that Accounts and Payees are leaves in orthogonal data structures. So I would probably not have as wide an umbrella as you suggest here for DataContext.


load to ensure the correct instances are assigned to each object.  The
model is not bi-directional so if I ask for all transactions by
category I search every transactioncategory of every transaction in
every account.  My primary view will be by category so I think a
bi-directional relation here might be best but at the moment category
knows nothing about transaction, and I've optimized the search a bit
by assigning left/right tree numbers to each category.  The question I
have is what the best way to control modifications is.  My first
thought was that when context is saved my thinking was I would
enumerate all transactions in the datacontext if no id is assigned its
new, if transaction.HasChanges I update and if the transaction was
loaded but is no longer in the transaction collection it was deleted,
and likewise for each payee, category, and transaction.

The problem is that this doesn't ensure the invariants above are not
violated, context.getAccounts()[0].getTransactions()[0].Payee = new
Payee("Test");  and similarly in relation to categories.  I don't
want to reload the data on every change and in some cases is not
possible as I'm also performing updates via an Indigo service to
allow me to access my info from home or work but at some clients I'm
not allowed to connect my laptop to the network and so I wanted to be
able to save a serialized copy of the datacontext to disk and queue
modifications so that they can be sent later when connection is
available.

This is a very different and potentially very nasty problem. Essentially your application is caching data from the DB than is not in synch with the DB for extended periods of time. Money/Quicken don't have this problem because they update the DB with every transaction immediately. (Note that newer versions of Quicken don't even have a Save button anymore.) Enter stage left, tripping: DB replication.


You have an added problem due to the complexity (nesting) of your summaries. Money/Quicken basically have only one total number to keep track of at the Account level, the current balance. Your summaries add another level of complexity due to the nesting and the requirement that the subcatagories sum to the supercategory.

However, I don't think the problem is insurmountable if you have a complete copy of the DB on your laptop. [If you don't then you will have a problem reading Transactions for UI display when the base is unavailable. B-)] So long as that laptop copy is internally consistent for recent activity, you can just do the brute force approach and copy it to you home desktop or whatever when you get the chance. IOW, do a poor man's replication by treating the home server as a backup.

For the posting mechanics to ensure internal consistency you just need to "walk" the Composite tree above and make the same adjustment at every level above Account _when you update Account_. That will ensure that the numbers are consistent so long as you do the update prior to processing any other change. [Note that if the transaction in hand is changed at the account level you obviously have to post the delta of the change to Account and the categories. But that delta will be the same at every level.]

To do that you just need to keep a lastBalance attribute in Transaction. When you create a Transaction or have completed the update of Account and the Categories for a change, you set lastBalance to currentBalance. So the only time they will be different is when you have made a change to an existing transaction in the UI. You just need to look for that when you post to Account to determine whether you need the straight amount or a delta. IOW, just make sure you complete the posting before processing another Return in the UI.


************* There is nothing wrong with me that could not be cured by a capful of Drano.

H. S. Lahman
hsl@xxxxxxxxxxxxxxxxx
Pathfinder Solutions  -- Put MDA to Work
http://www.pathfindermda.com
blog: http://pathfinderpeople.blogs.com/hslahman
(888)OOA-PATH



.


Quantcast