Putting the domain to work

Results of putting the domain at work

tl;dr

In this post I create a way to import transactions and categorize them. Basically putting the earlier created domain to work. In the end I get insight into my spendings. However I’m not completely done yet. Find the code at GitHub.

Putting the domain to work

It has been too long since my last post. However the goal of my favorite pet project is more important than ever. Inflation is rising to extreme heights, especially in Europe.

First a short recap: I still want insight in my finances. To achieve this I build an application to give me insight. The end goal is to reach financial independence way before I reach my retirement age. Next to that I try to get more experience applying DDD. Let us look at the ubiquitous language I defined a long time ago:

To achieve financial insight the application needs to import and categorize financial transactions. I want to set budgets for a year and compare them to the actual amounts. Furthermore based on the actual numbers the savings rate and years till financial independence must be calculated. In order to calculate the years till financial independence there is something to assess my savings and investments.

Last time I created an important part of the domain. I can create budgets, categories and transactions. Most important thing for me now is to get actual transactions into my application and categorize these transactions. After that I have some insights where I spent my money.

Importing transactions

My bank allows me to download a CSV file of all my transactions. That CSV file contains many columns that I can use. However in the domain I only need a few fields. Using Apache commons CSV this is rather easy to do.

You might think how does importing transaction fit into the domain. Actually importing is not part of the domain, at least not in my mind. I use the bank’s model to read the transactions and then put them in the domain. So for now the code is part of the presentation code.

The import functionality is quite easy and can be found here.

Categorization rules

Now I have a list of transactions. To get insight it is important to categorize these transactions. I’ve tried several things in the past to categorize transactions. However the easiest way of doing that is creating a Rule based solution. This is done by applying a set of rules to the properties of transactions.

As determined earlier the best predicting property is the contra account. So let’s first create a ContraAccountCatgegoryRule. The rule takes a Category and a contraAccount bank number. This code adds every transaction that has the contraAccount to the Category.

public class ContraAccountCatgegoryRule implements CategoryRule {

    @Override
    public boolean categorize(final Transaction transaction) {
        if (transaction.contraAccountNumber().equals(contraAccount)) {
            category.addTransactions(transaction);
            return true;
        }
        return false;
    }
}

Poor mans user interface

Ok how can I use this new rule to get that much wanted insight? I need an user interface to see the result. Building a great user interface is a hell of a job. In the past I fell in the trap not creating business value by creating and customizing my interface to an extreme degree. So how do we prevent that, while still getting insight.

For a developer the console is a brilliant output device. Let’s use it. I can print the categories to the screen together with the amount spent for that category. The code to put the domain to work resides in an application service: AdministrationOverview. This class manages my UI now. The code looks something like this:

public AdministrationOverview() {  
    //Create the event listener
    new AmountUsedUpdater(budgetRepository);  
    //create the initial categories
    createCategories();  
    final List<Transaction> transactionsList = importTransactions();  
    //Create my rules  
    CategorizationRules categorizationRules = createCategorizationRules();  
    //apply the rules
    categorizationRules.apply(transactionsList);  
    //print the amounts to the console
    printCategoryAmounts();  
}

Categorize all the things

My createCategorizationRules looks as follows:

private CategorizationRules createCategorizationRules() {  
    CategorizationRules categorizationRules = new CategorizationRules();  
    categorizationRules.add(new ContraAccountCatgegoryRule(salary, "NL98INGB0003856625"));  
    categorizationRules.add(new ContraAccountCatgegoryRule(taxes, "NL98INGB0003856626"));  
    categorizationRules.add(new ContraAccountCatgegoryRule(rent, "NL98INGB0003856627"));  
    return categorizationRules;  
}

So all transactions with contraAccount NL98INGB0003856625 are categorized as salary. Similary NL98INGB0003856626 as contraAccount will be categorized as taxes. You probably get how this works.

For demo purposes I’ll import a fake CSV bank file. This example can be found on GitHub.

First results

Let’s run the code. The initial result looks as follows:

Salary amount EUR 1221.49
Coffee to go amount EUR 0.00
Groceries amount EUR 0.00
Rent amount EUR -400.00

That result makes me quite happy.

However one thing I notice is that the total amount of money in all categories does not add up to the total amount of all transactions in the import file. This is because some of the transactions are not categorized yet. Most of the transactions are not categorized due to the fact that the ContraAccountRule cannot categorize transactions that don’t have a contraAccount. This will happen if these transactions are debit card transactions.

Next to that if I look closely I can see that I forgot a categorization rule for NL98INGB0003856628. Actually this should be energy. It’s pretty strange that I forgot to input the energy rule, especially now energy prices are skyrocketing. Let’s add it. The new output looks as follows:

Salary amount EUR 1221.49
Coffee to go amount EUR 0.00
Groceries amount EUR 0.00
Energy amount EUR -300.00
Rent amount EUR -400.00

Uncategorized thingy

As said some transactions are not categorized yet. If the amount spent on these transactions is small, this is not a big deal. However in the current setup I have no clue how large this amount is.

So to get a feeling for the amount I want all those transactions in a uncategorized category. Let’s create a UncategorizedRule that takes any transaction and add the transaction to a predefined Category called UNCATEGORIZED. This new category can be added in the constructor of CategorizationRules.

The implementation of CategorizationRules is left as an exercise to the reader (or can be found here 😉).

Let’s run it again. The output looks as follows:

Results of putting the domain at work

financial insight!

Looking at the results above I finally have insight where my money goes. Of course this is not complete as still there are a lot of transactions uncategorized.

One thing I notice is that the categorization rules now really feel like a service bolted on top of the domain. To fix this, I probably need to refactor this somehow. But let’s celebrate 🎉 for now the fact that I get insight in where my money is going at last.

Conclusion

I’ve imported my transactions and I built some code to categorize them. With the poor mans interface I can now see where I spent my money and thus have insight. I’m putting the domain to work.

However for now I only get insight in my bank transactions. For many transactions I use my debit card. These transactions don’t have a contra account. Therefore the uncategorized category amount is rather big. Furthermore I’m not happy with my rules solutions. To fix both is probably a topic for a next post.

Find the resulting code on GitHub.


One response to “Putting the domain to work”

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.