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 library provides a simple, type-safe way to implement event sourcing in your Gleam applications.
- Event Sourcing: Build systems based on the event sourcing pattern.
- Event Stores: Supports multiple event store implementations:
- In-memory Store: Simple in-memory event store for development and testing.
- Postgres Store: Event store implementation using PostgreSQL.
- SQLite Store: Event store implementation using SQLite.
- Command Handling: Handle commands and produce events with robust error handling.
- Event Application: Apply events to update aggregates.
- Snapshotting: Optimize aggregate rebuilding with configurable snapshots.
- Type-safe Error Handling: Comprehensive error types and Result-based API.
import eventsourcing
import eventsourcing/memory_store
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)
}
}
_, _ -> Error(CantOperateOnUnopenedAccount)
}
}
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
}
}
// Enable snapshots every 100 events
let event_sourcing =
eventsourcing.new(mem_store, [query])
|> eventsourcing.with_snapshots(eventsourcing.SnapshotConfig(100))
// Load latest snapshot if available
let assert Ok(maybe_snapshot) = eventsourcing.get_latest_snapshot(
event_sourcing,
"account-123"
)
pub fn main() {
let query = fn(aggregate_id, events) {
io.println_error(
"Aggregate Bank Account with ID: "
<> aggregate_id
<> " committed "
<> events |> list.length |> int.to_string
<> " events.",
)
}
let event_sourcing =
eventsourcing.new(
event_store: memory_store.new(),
queries: [query],
handle: handle,
apply: apply,
empty_state: UnopenedBankAccount,
)
case eventsourcing.execute(
event_sourcing,
"account-123",
OpenAccount("account-123"),
) {
Ok(_) -> io.println("Account created successfully")
Error(error) -> io.println("Failed to create account: " <> debug(error))
}
}
The library now provides comprehensive error handling through the EventSourcingError type:
pub type EventSourcingError(domainerror) {
DomainError(domainerror)
EventStoreError(String)
EntityNotFound
}
All operations that might fail return a Result type, making error handling explicit and type-safe.
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.
Eventsourcing is published on Hex! You can add it to your Gleam projects from the command line:
gleam add eventsourcing
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.
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.
This project is licensed under the MIT License. See the LICENSE file for details.