eventsourcing
Types
Used by the EventStore implementations
pub type Aggregate(entity, command, event, error) {
Aggregate(
entity: entity,
handle: Handle(entity, command, event, error),
apply: Apply(entity, event),
)
}
Constructors
-
Aggregate( entity: entity, handle: Handle(entity, command, event, error), apply: Apply(entity, event), )
Used by the EventStore implementations
pub type AggregateContext(entity, command, event, error) {
AggregateContext(
aggregate_id: AggregateId,
aggregate: Aggregate(entity, command, event, error),
sequence: Int,
)
}
Constructors
-
AggregateContext( aggregate_id: AggregateId, aggregate: Aggregate(entity, command, event, error), sequence: Int, )
An EventEnvelop is a wrapper around your domain events
used by the Event Stores. You can use this type constructor
if the event store provides a load_events
function.
pub type EventEnvelop(event) {
MemoryStoreEventEnvelop(
aggregate_id: AggregateId,
sequence: Int,
payload: event,
)
SerializedEventEnvelop(
aggregate_id: AggregateId,
sequence: Int,
payload: event,
event_type: String,
event_version: String,
aggregate_type: String,
)
}
Constructors
-
MemoryStoreEventEnvelop( aggregate_id: AggregateId, sequence: Int, payload: event, )
-
SerializedEventEnvelop( aggregate_id: AggregateId, sequence: Int, payload: event, event_type: String, event_version: String, aggregate_type: String, )
The main record of the library. It holds everything together and serves as a reference point for other functions such as execute, load_aggregate_entity, and load_events.
pub opaque type EventSourcing(
eventstore,
entity,
command,
event,
error,
aggregatecontext,
)
Wrapper around the event store implementations
pub type EventStore(eventstore, entity, command, event, error) {
EventStore(
eventstore: eventstore,
load_aggregate: fn(eventstore, AggregateId) ->
AggregateContext(entity, command, event, error),
commit: fn(
eventstore,
AggregateContext(entity, command, event, error),
List(event),
) ->
List(EventEnvelop(event)),
)
}
Constructors
-
EventStore( eventstore: eventstore, load_aggregate: fn(eventstore, AggregateId) -> AggregateContext(entity, command, event, error), commit: fn( eventstore, AggregateContext(entity, command, event, error), List(event), ) -> List(EventEnvelop(event)), )
Functions
pub fn execute(
event_sourcing: EventSourcing(a, b, c, d, e, f),
aggregate_id aggregate_id: String,
command command: c,
) -> Result(Nil, e)
The main function of the package. Run execute with your event_sourcing instance and the command you want to apply. It will return a Result with Ok(Nil) or Error(your domain error) if the command failed.
pub fn new(
event_store: EventStore(a, b, c, d, e),
queries: List(fn(String, List(EventEnvelop(d))) -> Nil),
) -> EventSourcing(a, b, c, d, e, f)
Create a new EventSourcing instance providing an Event Store and a list of queries you want run whenever events are commited.
Examples
pub type BankAccount {
BankAccount(opened: Bool, balance: Float)
}
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 fn handle(
bank_account: BankAccount,
command: BankAccountCommand,
) -> Result(List(BankAccountEvent), Nil) {
case command {
OpenAccount(account_id) -> Ok([AccountOpened(account_id)])
DepositMoney(amount) -> {
let balance = bank_account.balance +. amount
case amount >. 0.0 {
True -> Ok([CustomerDepositedCash(amount:, balance:)])
False -> Error(Nil)
}
}
WithDrawMoney(amount) -> {
let balance = bank_account.balance -. amount
case amount >. 0.0 && balance >. 0.0 {
True -> Ok([CustomerWithdrewCash(amount:, balance:)])
False -> Error(Nil)
}
}
}
}
pub fn apply(bank_account: BankAccount, event: BankAccountEvent) {
case event {
AccountOpened(_) -> BankAccount(..bank_account, opened: True)
CustomerDepositedCash(_, balance) -> BankAccount(..bank_account, balance:)
CustomerWithdrewCash(_, balance) -> BankAccount(..bank_account, balance:)
}
}
fn main() {
let mem_store =
memory_store.new(BankAccount(opened: False, balance: 0.0), handle, apply)
let query = fn(
aggregate_id: String,
events: List(eventsourcing.EventEnvelop(BankAccountEvent)),
) {
io.println(
"Aggregate Bank Account with ID: "
<> aggregate_id
<> " commited "
<> events |> list.length |> int.to_string
<> " events.",
)
}
let event_sourcing = eventsourcing.new(mem_store, [query])
}