DBDelve
Dive deep into your data. A fast, native, multi-engine database client written in Rust. Free and open source, no telemetry.
Macula: the Internet, owned by its users. Connect anything, from anywhere, at any time. Infrastructure you own, software you control.
Developer toolsErlang · ShellGitHub @reckon-db-orgAvailability needs another checkOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Reckon Evoq is a bridge library between Evoq and ReckonDB
Loading the full README.
Read the full README ↗Adapter for connecting the evoq CQRS/ES framework to a reckon-db event store, reached through the reckon-gater gateway API.
reckon-evoq depends on evoq and reckon_gater only. It does not depend on reckon_db: it reaches the store through the gater API (see Reckon stack).
reckon-evoq is a thin adapter layer that implements the evoq behavior interfaces:
evoq_adapter - Event store operations (append, read, delete), DCB conditional-append append_if_no_tag_matches/4 (2.2.0+, paired with reckon-db 3.1.1), and the CCC payload-condition callbacks ccc_read_by_payload/4, ccc_read_by_payload_hash/4, payload_indexes/1, payload_hash_indexes/1 (2.7.0+, paired with evoq 1.22 / reckon-gater 3.7)evoq_snapshot_adapter - Snapshot operations (save, read, delete)evoq_subscription_adapter - Subscription operations (subscribe, ack, checkpoint)evoq_checkpoint_store - Persistent projection checkpoints via ReckonDB snapshotsEvery callback is a thin translation: it maps evoq-shaped terms to the gater types, calls reckon_gater_api, and maps the result back through events_to_evoq/1 and friends. The adapter holds no state and runs no logic of its own.
The DCB passthrough is what backs evoq's evoq_decision behaviour, see
evoq's decisions guide
for the high-level pattern. The CCC callbacks extend that to payload-conditioned decisions: declared {payload, Key} / {payload_hash, [Keys]} indexes drive conditional reads, and the introspection callbacks let evoq's decision runtime fail loudly on an undeclared payload index.
All operations are routed through reckon-gater, which provides:
Add to your rebar.config:
{deps, [
{reckon_evoq, "~> 2.7"}
]}.
| Component | Version |
|---|---|
reckon_evoq (this repo) | 2.7.2 |
evoq (dep) | ~> 1.22 |
reckon_gater (dep) | ~> 3.7 |
telemetry (dep) | ~> 1.3 |
| Erlang/OTP | 27+ |
reckon-evoq depends on evoq and reckon_gater directly. The underlying
reckon_db event store must be running and reachable through the gateway, but
is not a build dependency of this adapter.
This adapter requires:
ccc_read_by_payload* / get_payload_indexes surfaceIn your sys.config:
{evoq, [
{event_store_adapter, reckon_evoq_adapter},
{snapshot_store_adapter, reckon_evoq_adapter},
{subscription_adapter, reckon_evoq_adapter}
]}
The adapter requires an reckon-db cluster to be available. Gateway workers automatically discover and connect to reckon-db nodes.
%% Create a command. The aggregate id is the id of the stream its events
%% are stored in, so it must be a valid stream id: <prefix>-<32 hex>.
%% reckon_gater_stream_id:new(account) makes one. The rule:
%% https://hexdocs.pm/reckon_gater/stream_ids.html
AccountId = <<"account-018f6a7b8c9d4e5f60718293a4b5c6d8">>,
Command = evoq_command:new(deposit, bank_account, AccountId, #{amount => 100}),
%% Dispatch through evoq (uses this adapter internally)
{ok, Version, Events} = evoq_router:dispatch(Command).
%% Append events to a stream
reckon_evoq_adapter:append(StoreId, StreamId, ExpectedVersion, Events).
%% Returns: {ok, NewVersion} | {error, Reason}
%% Read events from a stream
reckon_evoq_adapter:read(StoreId, StreamId, StartVersion, Count, Direction).
%% Direction: forward | backward
%% Returns: {ok, [Event]} | {error, Reason}
%% Read all events from a stream (batched internally)
reckon_evoq_adapter:read_all(StoreId, StreamId, Direction).
%% Returns: {ok, [Event]} | {error, Reason}
%% Read events by type (server-side Khepri filtering)
reckon_evoq_adapter:read_by_event_types(StoreId, EventTypes, BatchSize).
%% Returns: {ok, [Event]} | {error, Reason}
%% Get stream version
reckon_evoq_adapter:version(StoreId, StreamId).
%% Returns: Version (integer) | -1 (no stream)
%% Check if stream exists
reckon_evoq_adapter:exists(StoreId, StreamId).
%% Returns: boolean()
%% List all streams
reckon_evoq_adapter:list_streams(StoreId).
%% Returns: {ok, [StreamId]} | {error, Reason}
%% Delete a stream
reckon_evoq_adapter:delete_stream(StoreId, StreamId).
%% Returns: ok | {error, Reason}
This is the beginning of the README. Read the rest at the original. Read the full README ↗