Budget: Requirements

Requirements for the budget application.

Requirement 1: Basics

Requirement 1.1

The application infrastructure is defined in CDK (TypeScript). The application itself is implemented as an API written in TypeScript, with a frontend in HTML and CSS and a little bit of JavaScript where needed. The business logic is implemented in the “backend” lambda, which includes page rendering. JavaScript in the browser is used for presentation only.

Requirement 1.2

The application is serverless, using the following AWS services to run:

  1. Lambda to implement application logic and auth
  2. DynamoDB in on-demand pricing mode for storage
  3. S3 for assets such as CSS, favicon and presentational JavaScript.
  4. CloudFront for TLS and caching (if needed)

Requirement 2: System Entities

System entities are split into three groups:

Requirement 2.1: Timeless entities

“Timeless” entities pop into existence when they are needed (for example when another entity needs to refer to them). They may have metadata associated with them, but they can never be delete once they are referred to. For simplicity, they can never be deleted (to avoid having to keep reference counts).

Examples of timeless entities: accounts, pots, plans, categories, goals.

Requirement 2.2: Events

Events have a strict order and are generally immutable apart from changes that are required to maintain the order. Event order never changes and for each event it’s always possible to say which event comes after it.

Examples of events: transaction creation event, transaction update event, transaction deletion event.

Requirement 2.3: Reconstructible entities

Reconstructible entities can be deleted and reconstructed from other entities. This sounds quite abstract, but in reality there are two such entites: transactions and snapshots.

Transactions can be constructed from a stream of events.

Snapshots can be constructed from a stream of transactions.

Requirement 2.4: Metadata

Metadata is attached to entities and is versioned (with a timestamp). For example, a transaction description or a plan name are metadata that can be changed but previous versions can be looked at.

Note the implications: A transaction may refer to a category (a timeless entity) whose current metadata is “name=food”. The category’s metadata can be updated to “name=supermarket”. This change will be reflected immediately in every transaction that uses this category. It doesn’t matter if the transaction was created before the category’s metadata was changed. But it is possible to query the metadata’s previous versions to see that it used to be “name=food” at some point (also when it changed).

Requirement 3: Timeless Entities

Requirement 3.1: Accounts

Requirement 3.1.1

An account is an entity with an account id (uuid) and metadata. The metadata includes:

An account can be the source or the destination of a transaction. An account will have a balance that is derived from a stream of transaction. The balance is not stored in the account and is not part of the account, it is simply a derived property that is associated with the account. The balance can be positive, zero or negative without limitation.

Requirement 3.1.2

The user can add an account.

Requirement 3.1.3

The user can update the account’s metadata.

Requirement 3.2: Pots

A pot is an entity with a pot id (uuid) and metadata. It belongs to an account, and so also holds the account id. The pot metadata includes:

A pot belongs to an account. An account may have zero or more pots.

Requirement 3.2.1

A pot can be the source or the destination of a transaction.

A pot will have a balance that is derived from a stream of transaction. The balance is not stored in the pot and is not part of the pot, it is simply a derived property that is associated with the pot.

Requirement 3.2.2

The balance can be positive or zero but may not be negative. A pot is an abstract allocation of funds, and a negative sum cannot be allocated. If a transaction removes an amount that is greater that the amount currently associated with the pot, that amount becomes zero, and the rest of the transaction’s amount is taken directly from the holding account (which can be associated with a negative balance).

Requirement 3.2.3

A transaction can transfer an amount between pots. The same rule applies. For example, if Pot A is associated with 100 and Pot B is associated with 200, and the transaction moves 400 from Pot B to Pot A, then 200 is take from Pot B (it cannot be associated with a negative balance), and the rest is taken directly from account. The total in the account is 300, so after the transaction Pot A is associated with 500 (100 + 400) and the account is associated with -200. Overall the total is still 300, but the allocation shows that 200 has been “over-allocated” to Pot A.

Requirement 3.2.4

The user can add a pot to an account.

Requirement 3.2.5

The user can update the pot’s metadata.

Requirement 3.3: Plans

A plan is an event/transaction generator. The plan has an id (a uuid). Its metadata includes

Requirement 3.3.1: Plan Schedule

The schedule can be one-off (in which case the start date is the plan’s date) or recurring, with various rules, at a minimum:

The monthly schedule is a bit tricky, for example if the day is 31, then it will take place on:

In addition, it can be “the Friday before the date, if it falls on a weekend”, “the Monday after the date, if it falls on a weekend” or even more complicated, taking bank holidays and national holidays into account. However, at a minimum it will support plain monthly schedule (without weekend / holidays).

Requirement 3.3.2: Last Realised Date

The last realised date of a plan tells use when to generate events from. For example, if the last realised date is 2025-12-10 and the schedule is monthly on the 20th of the month, then the next event the plan generates is on 2025-12-20. If we try to generate all the events before 2025-12-16, then we will get an empty list, but the “last realised” date will be updated to 2025-12-16.

Requirement 3.3.3: Transactions and Events

The plan can generate both events and transactions. Events are generated when the plan is realised for a date, and are then appended to the event log. Transactions are generated for information only, and are not persisted.

Generating transactions is used for budget predictions. For example, it should be possible to see all the planned transactions for a specific month, two years in the future, and also the impact on balances of accounts and pots, and whether goals will be achieved.

Requirement 3.3.4

The user can create a new plan.

Requirement 3.3.5

The user can update the plans’s metadata.

Requirement 3.3.6

Plans are realised regularly in order to keep accounts and pots up to date. Events generated from plans include the plan id and the plan version. Transactions that are created from those events also include the plan id and the plan version. It is possible to update such transactions (for example, amend their actual date vs. planned date, or actual amount vs. planned amount) using new events. It is possible to compare the transaction to the planned transaction at any time (even if the plan has changed since the transaction was created), as it includes the plan id and the plan version.

Requirement 3.4: Categories

A category has an id (a uuid) and metadata:

Requirement 3.4.1

The user can create a new category.

Requirement 3.4.2

The user can update the category’s metadata.

Requirement 3.5: Goals

A goal has an id (a uuid). It is associated with a pot. A pot may be associated with zero or one goal.

The goal also has the following metadata:

Requirement 3.5.1

Based on the goal’s target amount, the pot’s associated balance and the goal’s date, it is possible to calculate the required amount per unit of time to reach the goal. For example, if the pot is associated with 900 and the goal is 1500 in four month’s time, then a monthly addition of 150 is required to reach the goal. Is it possible to tell whether the goal has been achieved (the pot is assoicated with a sum >= the goal) and whether it has been achieved on time.

Requirement 4: Events

The data model and behaviour is based on the “event source” model, with respect to transactions. Events are strictly ordered, and can be used to reconstruct a stream of transactions.

The system does not model metadata changes as event as they are applied immediately. For example, changing an account name applies immediately everywhere, whereas if it were modelled as an event it would have only applied to transactions that were created after the name change.

There are three event types:

  1. Create transaction event
  2. Update transaction event
  3. Delete transaction event

Each event has an event creation timestamp, used for strictly ordering the events in the order they were created. An event created on 2025-02-01 may be a transaction creation event for a transaction with a date of 2025-01-09. They do not have to match.

An event has an event id (a ULID).

Requirement 4.1: Create Event

Transaction creation events include:

The core information affects account balance calaculations. Metadata does not.

Requirement 4.2: Update Event

Transaction update events include:

Updates do not touch the metadata which is considered “timeless”.

Requirement 4.3: Delete Event

Transaction delete events include:

Deletes also delete the metadata, to keep things clean.

Requirement 5: Transactions

A transaction models the movement of an amount of money between entities (accounts or pot). In addition a transaction id (a uuid), transactions have the following core data:

Change information includes the change type: income, expense, transfer. Depending on the type, it also includes a source, a destination or both. Source and destination can be an account or pot. It is not possible to transfer between the same entity as both source and destination.

Requirement 5.1

There is some ambiguity with date. Transactions are not ordered if they have the same date. It is not possible to say which one happens first. This may lead to some issues, because not all transactions are commutative. For example, if pot A has 200 and there are two transactions on the same day, one income of 300 and one expense of 300, the result will differ according to the order we apply the transactions. For now this will stay ambiguous, additional rules may be added later.

Requirement 5.2

Transactions are reconstructed from a stream of events. We will obtain the exact same transactions if we process the same stream of events.

Requirement 6: States

A state is the result of “applying” a transaction to the previous state. The initial state of the system is one where all balances of all entities (accounts and pots) are zero.

For example, applying an income transaction of 100 for account X on the initial state produces a new state where a balance of 100 is associated with account X.

Requirement 6.1: Dates

Transactions are applied to states in date order. For the same date there could be multiple transactions, in which case they are applied in a consistent order. The “natural” order is by creation timestamp (a transaction that is created later is applied later). However, this is arbitrary, because transaction date may change, and so an “old” transaction which is originally applied last on its original date may get a new date, where it will be the first transaction to be applied.

Requirement 6.2: Snapshots

A state can be stored as a snapshot. If transactions that are applied before the state’s date don’t change, it is possible to use the snapshot as a base for later transactions. This can serve as a caching mechanism to avoid applying all the transactions from the earliest one.

Requirement 6.2.1: Invalidation

If a transaction that was used to construct the state is changed in any way, that invalidates all snapshots with a date equal to or later than that transaction.

While it may be possible to avoid this invalidation by carefully considering the impact of the change (for example, if it’s a date change that is “harmless”), for simplicity all “potentially impacted” snapshots are invalidated.

Requirement 6.2.2

If a transaction’s date changes, the earlier of the old date and the new date must be considered for snapshot invalidation.

Requirement 7: User Experience

The application serves HTML pages to the user. This section is under-specified on purpose, as requirements are vague and multiple iterations will be required to find what works.

Requirement 7.1: Accounts, Pots and Goals

Requirement 7.1.1

The application will serve a page that allows the user to see the accounts, the pots and the goals (if any), including names, dates, descriptions and notes.

The user will be able to edit any piece of metadata on the page and submit it.

Requirement 7.2: Transactions

Requirement 7.2.1

The application will serve a page that shows the transactions for a given year and month. The page will default to the current year and month, but will have naviation buttons which will request a new page for the given year and month. For example, if the user clicks on the “next month” button, it will request the page with parameters that specify the year and month.

Requirement 7.2.2

The page is specific to an account (and optionally pot). Dropdown menus will allow the user to select a different account and/or pot. The year and month settings will be the same when a different account/pot is selected

Requirement 7.2.3

The transactions will be displayed as a list, ordered by date and creation timestamp. A running balance for the selected account/pot will be included in the list, as well as starting and ending balance.

Requirement 7.2.4

The transaction page will allow the user to submit a new transaction, including:

Requirement 7.3: Categories

Requirement 7.3.1

The application will serve a page that allows the user to see the list of categories with an edit icon next to each category name and notes.

Requirement 7.3.2

The application will serve a page that allows the user to edit a category name and notes, and submit the change. The application will then serve the category list as in 7.3.1.

Requirement 7.4: Plans

Requirement 7.4.1

The application will serve a page with the list of active plans. The user will be able to edit any plan on the page and submit the page.

Requirement 7.5: Calendar View

Requirement 7.5.1

The application will serve a page, similar to the transaction page, only where the data is presented on a monthly calendar for the selected year, month and account/pot. The running total will be shown on the calendar as well as starting balance and ending balance.

The user will be able to see additional information for any day in the calendar: A list of all the transactions for that day with all the details.

Edit Page Create Sub Page Delete Page