In the oldest system I’m maintaining right now, we have an account aggregate that, next to mutating various balances, produces immutable financial transactions. These transactions are persisted together with the aggregate itself to a relational database. The transactions can be queried by the owner of the account in an immediate consistent fashion.
The table with these transactions looks similar to this:
CREATE TABLE [dbo].[Transaction] ( [Id] [int] IDENTITY(1,1) NOT NULL, [Timestamp] [datetime] NULL, [AccountId] [int] NOT NULL, [TransactionType] [varchar](25) NOT NULL, [CashAmount] [decimal](19, 2) NOT NULL, [BonusAmount] [decimal](19, 2) NOT NULL, [...] [...] () NULL /* Too much metadata I'm not very happy about */ CONSTRAINT [Tx_PK] PRIMARY KEY CLUSTERED ( [Id] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 100) ON [PRIMARY] ) ON [PRIMARY] There’s an index on the timestamp, the account identifier and the transaction type, which allows for fast enough reads for the most common access patterns which only return a small subset.
...