Symphonious

Living in a state of accord.

Moolah Diaries – Tracking Account Balances

Moolah has two separate Vuex modules, transactions and accounts. That’s a fairly reasonably logical separation, but in the new Moolah, it’s not a complete separation. Moolah only ever loads a subset of transactions – typically the most recent transactions from the currently selected account.  As a result, accounts have their own current balance property because they can’t always calculate it off of their transactions.

That means that sometimes the transaction Vuex module needs to make changes to state owned by the account module which is a rather unpleasant breach of module separation. While we ensured transaction balances remained consistent inside each mutation, we can’t do that for account balances because Vuex (presumably deliberately) makes it impossible for a mutation to access or change state outside of the module itself. Instead, I’ve used a simple Vuex plugin that watches the balance on the most recent transaction and notifies the account module when it changes:

import {mutations as accountsMutations} from './accountsStore';
export default store => store.watch(
    (state) => {
        return state.transactions.transactions.length > 0 
? state.transactions.transactions[0].balance
: undefined; }, (newValue, oldValue) => { if (store.getters["accounts/selectedAccount"] !== undefined &&
newValue !== undefined) { store.commit('accounts/' + accountsMutations.updateAccount, { id: store.state.selectedAccountId,
patch: {balance: newValue} }); } });

Most of the complexity there is dealing with the case where we don’t yet have transaction or don’t have a selected account. This essentially makes the syncing between transaction balances and account balances a separate responsibility that belongs to this plugin.

The plugin doesn’t completely handle transfers between accounts. The balance for the account currently being edited updates correctly, but the balance for the account on the other side of the transfer doesn’t update. I’m not particularly happy with the solution, but at least for now the responsibility of reacting to transfer changes is in the transaction module’s actions. The action can easily calculate the effects of transaction changes on the balances of other accounts and use Vuex’s dispatch to send a notification over to the account module to perform the update. Mutations can’t dispatch events so it has to be the action that does it.

This leaves responsibility for updating the current account’s balance with the plugin and the responsibility for updating other account’s balances with the transaction module which is ugly. Perhaps the transaction module actions should be responsible for all account balances. Probably the right answer is to extract a separate class/function that the transaction module notifies at the end of each action with the state of the transaction before and after. Then that new class/function is responsible for updating account balances. Will need to prod the code a bit to see if that really can be done…

 

 

Moolah Diaries – Maintaining Invariants with Vuex Mutations

Previously on the Moolah Diaries I had plans to recalculate the current balance at each transaction as part of any Vuex action that changed a transaction. Tracking balances is a good example of an invariant – at the completion of any atomic change, the balance for a transaction should be the initial balance plus the sum of all transaction amounts prior to the current transaction.

The other invariant around transactions is that they should be sorted in reverse chronological order. To make the order completely deterministic, transactions with the same dates are then sorted by ID. This isn’t strictly necessary, but it avoids having transactions jump around unexpectedly.

My original plan was to ensure both of these invariants were preserved as part of each Vuex action, but actions aren’t the atomic operations in Vuex – mutations are. So we should update balances and adjust sort order as part of any mutation that affects transactions. Since only mutations can change state in Vuex, we can be sure that if our mutations preserve the invariants then they will always hold true. With actions, there’s always the risk that something would use mutations directly and break the invariants.

So lesson number 1 – in Vuex, it should be mutations that are responsible for maintaining invariants. That’s probably not news to anyone who’s used Vuex for long.

We could take that a step further and verify that the invariants always hold true using a vuex plugin:

const invariantPlugin = store => {
if (process.env.NODE_ENV !== 'production') {
store.subscribe((mutation, state) => {
// Verify invariants
});
}
};

However, the way vuex recommends testing actions and mutations means registered plugins don’t get loaded. Tests that cover views that use the store will most likely stub out the actions since they’ll usually make HTTP requests.  So mostly the invariant check will only run during manual testing. That may still be worth it, but I’m not entirely sure yet…

 

Moolah Diaries – Multi-tenant Support for Testing

Experience at LMAX has very clearly demonstrated the benefits of good test isolation, so one of the first things I added to Moolah was multi-tenant support. Each account is a completely isolated little world which is perfect for testing.

Given that I’ve completely punted on storing passwords by delegating authentication to Google (and potentially other places in the future since it’s using bell to handle third party authentication), there’s actually no user creation step at all which makes it even easier. As a result it’s not just acceptance tests that are benefiting from the isolation but also the database tests which no longer need to delete everything in the database to give themselves a clear workspace.

It’s all quite civilised really.

Moolah Diaries – Vuex for Front End State

Most of the work I’ve done on Moolah so far has been on the server side – primarily fairly boring setup work and understanding the best way to use any number of libraries that were new to me sensibly. The most interesting work however has been on the front-end. I’ve been using Vue.js and Vuetify for the UI after Vue’s success in the day job. The Moolah UI has much more data-interdependence between components than what we’ve needed at work though so I’ve introduced Vuex to manage the state in a centralised and more managed way.

I really quite like the flow – Vue components still own any state related directly to the UI like whether a dialog is shown or hidden etc but all the business model is stored in and managed by the Vuex store. The Vue components dispatch actions which perform computation, make requests to the backend or whatever is required then commit any changes to the store (via mutations). The usual Vue data-binding then kicks in to update the UI to reflect those changes.

The big advantage of this is that it naturally pulls business logic out of .vue files, preventing them getting to big. Without Vuex that basically depends on having the discipline to notice when a .vue file is doing too much and then untangling and splitting out the business logic.  Vuex provides a much clearer and more consistent way to delineate business logic from view code because you can’t modify state directly from the Vue component and it then becomes natural to split out an action.

Vuex’s module support also makes it easy to avoid your Vuex store from becoming the big ball of mud that does everything.

However, I’m still searching for a good, efficient way to calculate and update the current balance for each transaction. The actual calculation is simple enough – the balance for any transaction is the sum of the amount of every transaction before it in the account. Simplistically we could just start from the first transaction and iterate through calculating all the balances in a single O(n) pass. However, recalculating the balance for every transaction on each change is incredibly wasteful and is a big part of why the original version of Moolah takes so long to get started – it’s calculating all those balances. Each transaction balance actually only depends on two things, the transaction amount and the balance of the previous transaction. Since most new or changed transactions are at or near the very end of the transaction list, we should be able to avoid recalculating most of the balances.

I don’t think Vue/Vuex’s lazy evaluation will be able to avoid doing a lot of extra recalculation, not least of all because the only way to represent this would be a transactionsWithBalances computed view and it would output the entire list of transactions so would recalculate every balance on every change.

However, it’s reasonably straight forward to build the lazy evaluation manually, but where does that sit in the Vuex system? I’m guessing pre-calculated balance is just a property of every transaction in the state and actions take responsibility for updating any balances they might have affected.

I’m leaning towards having a dedicated ‘updateBalances’ action that can be triggered at the end of any action that changes transactions and is given the first transaction that requires it’s balance recalculated. Since every transaction after that depends on the balance of the one before they’ll also need updating.

I think that works and am now reminded about how useful it is to write a diary like this as a way to think through issues like this.

Modernising Our JavaScript – Vue.js To The Rescue

I’ve previously written about how Angular 2 didn’t work out for us, our second attempt was to use Vue.js which has much far more successful. The biggest difference with Angular is that it’s a much smaller library. It has far less functionality included in the box but a wide range of plugins to flesh out functionality as needed. That avoided the two big issues we had with Angular 2:

  • Build times were faster because there were way fewer dependencies to install, manage and bundle together. Not using Typescript may have also helped but I never had a clear indication of how much that affected build time.
  • It integrated much more easily with our existing system because it didn’t try to do everything. We just kept using the bits of our system that worked well.

We’ve been so happy with how Vue.js fits in for us, we’re now in the process of replacing the things we had built in Angular 2 with Vue.js versions.

We set out looking for a more modern UI framework primarily because we wanted the data binding functionality they provide. As expected, that’s been a very big benefit for any parts of the UI that are even slightly more than simple CRUD. We were using mustache for our templates and the extra power and flexibility of Vue’s templating has been a big advantage. There is a risk of making the templates too complex and hard to understand, but that’s mitigated by how easy it is to break out separate components that are narrowly focused.

In fact, the component model has turned out to be the single biggest advantage of Vue over jquery. We did have some patterns built up around jquery that enabled component like behaviour but they were very primitive compared to what Vue provides. We’ve got a growing library of reusable components already that all fit in nicely with the existing look and feel of the UI.

The benefit of components is so great that I’d use Vue even for very straight-forward UIs where jquery by itself could handle it simply. Vue adds almost no overhead in terms of complexity and makes the delineation of responsibilities between components very clear which leads to often unexpected re-usability. With mixins it’s also possible to reuse cross-cutting concerns easily.

All those components wind up being built in .vue files which combine HTML, JavaScript and styles for the component into one file. I was quite sceptical of this at first but Vue provides a good justification for the decision and in practice it works really well as long as you are a bit disciplined at splitting things out into separate files if they become at all complex. Typically I try to have the code in the .vue file entirely focused on managing the component state and split out the details of interacting with anything external (e.g. calling server APIs and parsing responses) into helper files.

Ultimately, it’s the component system that is really bringing us the most value which is a bit of a surprise given we had expected data-binding to be the real powerhouse. And data-binding is great, but it’s got nothing on the advantages of a clear component system that’s at just the right level of opinionated-ness for our system. We’re not only building UIs faster, but the UIs we build are better because any time we spend polishing a component applies everywhere it’s used.

I’m really struggling to think of a case where I wouldn’t use Vue now, and if I found one it would likely only be because one of the other similar options (e.g. Angular 2) was a better fit for that case.