Eventsourcing
Table of contents
Introduction
Eventsourcing is a Gleam library designed to help developers build event-sourced systems. Event sourcing is a pattern where changes to the application’s state are stored as a sequence of events. This allows for better traceability, easier debugging, and the ability to recreate the application’s state at any point in time.
Features
- Event Sourcing: Build systems based on the event sourcing pattern.
- In-memory Store: Simple in-memory event store for development and testing.
- Command Handling: Handle commands and produce events.
- Event Application: Apply events to update aggregates.
Example
Command Handling
import eventsourcing
import eventsourcing/memory_store
import gleam/result
pub const bank_account_event_type = "BankAccountEvent"
pub const bank_account_type = "BankAccount"
pub type BankAccount {
BankAccount(balance: Float)
UnopenedBankAccount
}
pub type BankAccountCommand {
OpenAccount(account_id: String)
DepositMoney(amount: Float)
WithDrawMoney(amount: Float)
}
pub type BankAccountEvent {
AccountOpened(account_id: String)
CustomerDepositedCash(amount: Float, balance: Float)
CustomerWithdrewCash(amount: Float, balance: Float)
}
pub type BankAccountError {
CantDepositNegativeAmount
CantOperateOnUnopenedAccount
CantWithdrawMoreThanCurrentBalance
}
pub fn handle(
bank_account: BankAccount,
command: BankAccountCommand,
) -> Result(List(BankAccountEvent), BankAccountError) {
case bank_account, command {
UnopenedBankAccount, OpenAccount(account_id) ->
Ok([AccountOpened(account_id)])
BankAccount(balance), DepositMoney(amount) -> {
let balance = balance +. amount
case amount >. 0.0 {
True -> Ok([CustomerDepositedCash(amount:, balance:)])
False -> Error(CantDepositNegativeAmount)
}
}
BankAccount(balance), WithDrawMoney(amount) -> {
let balance = balance -. amount
case amount >. 0.0 && balance >. 0.0 {
True -> Ok([CustomerWithdrewCash(amount:, balance:)])
False -> Error(CantWithdrawMoreThanCurrentBalance)
}
}
_, _ -> Error(CantOperateOnUnopenedAccount)
}
}
Event Application
pub fn apply(bank_account: BankAccount, event: BankAccountEvent) {
case bank_account, event {
UnopenedBankAccount, AccountOpened(_) -> BankAccount(0.0)
BankAccount(_), CustomerDepositedCash(_, balance) -> BankAccount(balance:)
BankAccount(_), CustomerWithdrewCash(_, balance) -> BankAccount(balance:)
_, _ -> panic
}
}
Running the Example
pub fn main() {
let mem_store =
memory_store.new(UnopenedBankAccount, handle, apply)
let query = fn(
aggregate_id: String,
events: List(eventsourcing.EventEnvelop(BankAccountEvent)),
) {
io.println_error(
"Aggregate Bank Account with ID: "
<> aggregate_id
<> " commited "
<> events |> list.length |> int.to_string
<> " events.",
)
}
let event_sourcing = eventsourcing.new(mem_store, [query])
eventsourcing.execute(
event_sourcing,
"92085b42-032c-4d7a-84de-a86d67123858",
OpenAccount("92085b42-032c-4d7a-84de-a86d67123858"),
)
}
Philosophy
Eventsourcing is designed to make building event-sourced systems easy and intuitive. It encourages a clear separation between command handling and event application, making your code more maintainable and testable.
Installation
Eventsourcing is published on Hex! You can add it to your Gleam projects from the command line:
gleam add eventsourcing
Support
Eventsourcing is built by Renatillas. Contributions are very welcome! If you’ve spotted a bug, or would like to suggest a feature, please open an issue or a pull request.
Contributing
Contributions are welcome! Please follow these steps:
- Fork the repository.
- Create a new branch (
git checkout -b my-feature-branch
). - Make your changes and commit them (
git commit -m 'Add new feature'
). - Push to the branch (
git push origin my-feature-branch
). - Open a pull request.
Please ensure your code adheres to the project’s coding standards and includes appropriate tests.
License
This project is licensed under the MIT License. See the LICENSE file for details.