One of the goals I had for this project was to apply a bunch of things that I’ve been learning about in combination. I’ve used one or more of these tools / technologies in different projects, but I’ve not had the chance to put them all together in a single project so far. These include things F#, Domain driven design (DDD), Command Query Responsibility Segregation (CQRS), Event sourcing, Xamarin, MVVMCross, Reactive Extensions framework etc.
In this post I’ll try to flesh out some of my thoughts around how I could put these technologies to good use in building the new cross-platform version of this mobile app.
This post is part of a series on my journey rebuilding my app and learning a lot of new stuff. Other posts in the series:
Series: All About Money - A journey to build a mobile app
- The state of the app
- The goal: first x-platform release
- Thinking functional and DDD - in the personal finance tracking context
- The new Project structure
- Design of the existing app
- UI and wireframes
… more to come …
- The final push to release
I’m not going to explain each of these tools / frameworks / patterns here because there has been a lot said about those already. Instead, I’d like to focus on how to apply some of these things to a specific application - and hopefully this will be useful to someone (including a future me), when stuck with a problem that these technologies are designed to help with.
Now, to start off - I already had a set of portable class libraries written in C# that make up the current version of All About Money. As I reviewed the design of the current app, I made a few interesting observations:
- A lot of my service layer code is functional with heavy use of LINQ
- I was not separating concerns properly and was trying to re-use my persistence model (i.e. the C# classes I use to store the data to disk) - as my Domain and also DTOs (data transfer objects)
- I was even binding some of these objects directly to the UI (and implemented
INotifyPropertyChangedon some of them to support MVVM!)
- The cross-device synchronisation logic was more complicated than it needed to be - since I was using a very simple ‘file-per-class-type’ JSON store that I wrote a while ago
- Some of the PCL infrastructure code purely existing due to the way OOP works
- I wrote a lot of my own code to do view model discovery etc
- I had very little to protect me from accidentally writing incorrect code that would violate invariants in the domain
There were more issues - but overall it prompted me to do a full re-think on how I’d design it - since now is a good opportunity, as I’m planning to release a cross-platform version.
Given these problems, tools like F#, DDD, MVVMCross and Xamarin almost chose themselves.
Trying to apply DDD to the personal finance domain
From the perspective of the domain (accounting) - personal finance seems to be no different from a regular/business accounting in the sense that the core accounting principles apply in both cases. However, the user experience and interactions may need to be simplified to use a language and interface that talks about income, expenses, savings, budgets, categorisation and so on - as opposed to credits, debits, journals, ledgers and so on.
Also - I just didn’t want to build yet-another-full-blown-accounting system.
So, I started thinking about what the main ‘business’ events may be, and the language used to describe then came up with this first attempt:
Possible events that involve money:
- I spend money to buy something or pay for a service/fine.
- I earn money and deposit it into an account.
- I budget for a holiday and save money for it.
- I transfer money between my accounts.
- I import my bank/loan/credit card transaction statement.
- I lend money to help out someone.
- I get repaid by someone for the money I lent.
- I borrow money to pay for something.
- I pay off a loan.
- I buy a house / car etc.
- I sell a house / car.
- Is gifts a subset of spending/receiving?
- I gift money to someone.
- I get a gift of money from someone.
This was getting me nowhere. I was discussing this with a colleague and he quickly pointed out that I had no way of knowing every possible event that happens in anyone’s life that involved money.
Then, I started fresh and thought about why people may want to use the app in the first place - to make sure what I’m building actually meets their needs:
Why will people use this app?
- To find out how much money they have in their accounts
- To find out how much they have saved
- To find out how much they can afford - for a holiday/activity/something to buy/invest
- To find out how much they have made in different ways
- To find out how much they have spent in different ways
- To find out how much they can save
- To find out where they can save
- To plan and budget for the future period
- To budget save for a goal
- To calculate their net worth
Given this, I tried to boil down the events to just what an simple accounting system:
- Money is
withdrawnfrom an account
- Money is
depositedinto an account
- Money is transferred between accounts
Each of the above transactions update the relevant account
I later realised, that if I want to express these concepts from a user’s perspective, (say as actions / ‘commands’ the user may execute) these correspond to something like:
spendmoney on something
receivemoney for something
- I set a
budgetfor a spending category
I’ve highlighted all the key events / entities involved.
This helped me get started on a domain model for the command-side of the system. Expressed in F#, the first attempt looks like:
This represents the core domain - with a ‘money’ type that is modelled as a unit of measure - so that I don’t accidentally do incorrect math on it by mixing it up with regular decimals. A transaction is very simple: it has a date when it happened, an amount expressed as money, a description and an optional category. Since transactions may be grouped/categorised/classified in a number of ways and these groupings don’t always conform to a proper heirarchy/structure, I’ve introduced a concept of a ‘Tag’ - where a transaction is tagged with one or more tags. Any number of transactions can be tagged with the same tag. Later, the read-side of the application can query transactions and group them by tags, categories, accounts etc. For now the only aspects of an account that we are interested in - is a name and a number and the type of currency for the account. (The intention is to support using any currency, since I have users from various countries - but a user having multiple accounts with different currencies will be interesting - something I’ll deal with later when building the app.)
The only entities in the above are
Account. All other types are value objects. To make sure I get the free structural equality F# gives me, and also treat a transaction as an entity with a reference, I’ve added a ‘Id’ attribute to the transaction record type. The idea is to treat two CLR references with the same attribute values (including Id) as the same transaction - if I need to compare something read from storage with a transaction in-memory. I’m thinking I won’t have a need for that since I’ll probably be using other record types / classes when reading the data vs those used in writing it to persistent storage.
Now, I still have to flesh it out to ensure some additional constraints are designed into the type system so that I can’t represent invalid states.
Regarding the CQRS aspect of it - I’ve not yet thought about what the commands and queries will be. That’s a post in itself. I can already identify some events like:
Though it looks very CRUD-dy, I’m not sure if there are more specific events for a simple accounting system. I’d imagine some of these will evolve over time as I build more and more of that app - especially the queries on the read side.
For the storage of the data, I’m expecting that using event sourcing (with a simple JSON file store) will help me simplify state management by since I won’t ever have to change a file written to disk - if the file only contains immutable events like the ones listed above.
That’s how far I’ve got at the moment. I’ll share more of my design thinking and flesh it out as we go, in future posts.
I’d like to hear your thoughts / suggestions on what your design may look like…