Bi-Temporal Events

Bi-Temporal Events

Two Commands CQRS

Introduction

What are bi-temporal events? They are simply regular events with some timestamp'ish Date field like valid_at.

Examples of these events are: BlackFridayProductDiscount | ApplySalaryIncreaseAt | etc.

In a nutshell, bi-temporal means that we can work with past and future. Martin Fowler describes it as a 2 distinct vectors(each with time) one for when we wanted to do something and when it should be applied.

Martin Fowler focuses on salaries (opens in a new tab), they were used as an example for bitemporal-history.

Top level view

Simplest showcase of time and 2 start with end commands

Examples

For Bi-Temporal Events we should be using Event Sourcing with any kind of an EventStore Database*(even raw SQL/NoSQL-with-Timeseries)*. Otherwise we will end up with row/doc fields: timestamp, valid_at, valid_til.

Salary Increase

  • ApplySalaryIncrease:

    Command comes from CQRS or CQS if you are using one of them. This is simple showcase for a basic command.

    class ApplySalaryIncreaseAt extends Command {
        data: {employeeID: string, increasedSalaryValue: number}
        validAt: Date
        timestamp = new Date()
        version = 1
     
        constructor(employeeID: string, increasedSalaryValue: number, validAt: Date) {
            super({employeeID, increasedSalaryValue})
            this.validAt = validAt
        }
    }
    And handler for this command:
    @CommandHandler(ApplySalaryIncreaseAt)
    class ApplySalaryIncreaseAtHandler implements ICommandHandler<ApplySalaryIncreaseAt> {
        constructor(private repository: SalariesRepository, private politic: SalariesPolitic) {}
     
        async execute(command: ApplySalaryIncreaseAt): Promise<void> {
            await this.politic.isValidSalaryIncreaseOrThrow({...command.data, validAt: command.validAt})
            const accountant = this.repository.getEmployeeAccountant(command.data.employeeID)
            await accountant.increaseSalaryAt({
                newValue: command.data.increasedSalaryValue,
                validSince: command.validAt
            })
        }
    }

What it gives us?

If you are using Event Sourcing you will have perfect history due to the append-only rule.

Combine data in event store stored as a timeline of events with valid_at fields and you will be able to activate:

  • pricing changes,
  • new rules for car parts production,
  • black friday rules
  • salaries changes over period of time,
  • etc.

Next

Check next:

READ

Latest readings

  • Readings are sites which will help you with detailed

  • information about given topic. Read latest ones from Learn.

AI

06-03-2026

Local Voice Assistant with Ollama
  • Build your own local voice assistant powered by Ollama.

AI

06-03-2026

AI YouTube Thumbnail Generator
  • Generate YouTube thumbnails with FastAPI and Ollama.

Architecture

05-09-2024

Graph DB usage comparison
  • Compare Neo4j and Tigergraph databases, which is easier to work with, etc.