Skip to content

Commit b810d5b

Browse files
authored
Merge pull request #49 from ccamel/refactor/store-apps
Refactor/store apps
2 parents 61731ec + 1c6b31e commit b810d5b

34 files changed

Lines changed: 798 additions & 336 deletions

.github/scripts/sync_readme.escript

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
%% between <!-- DEMO-START --> and <!-- DEMO-END --> markers.
77

88
main([]) ->
9-
main(["README.md", "examples/demo_bank.script"]);
9+
main(["README.md", "apps/es_xp/examples/demo_bank.script"]);
1010
main([ReadmePath, ScriptPath]) ->
1111
{ok, ReadmeBin} = file:read_file(ReadmePath),
1212
{ok, ScriptBin} = file:read_file(ScriptPath),

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ deps
1616
_build/
1717
_checkouts/
1818
doc/
19+
20+
# application files
21+
data/

README.md

Lines changed: 68 additions & 76 deletions
Large diffs are not rendered by default.

apps/es_kernel/src/es_contract_aggregate.erl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,17 @@ Function shall return The initial aggregate state.
2929
""".
3030
-callback init() -> aggregate_state().
3131

32+
-doc """
33+
Return the event type identifier for a given domain event payload.
34+
35+
This callback is responsible for mapping a payload to its canonical event type
36+
(e.g. `user_created`, `user_deleted`). It must return a valid
37+
`es_contract_event:type()` for the payload.
38+
""".
39+
-callback event_type(Event) -> Type when
40+
Event :: es_contract_event:payload(),
41+
Type :: es_contract_event:type().
42+
3243
-doc """
3344
Handle a domain command.
3445

apps/es_kernel/src/es_contract_command.erl

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
-export([
44
new/6,
5-
key/1,
5+
target/1,
66
with_metadata/2,
77
put_metadata/3,
88
with_tags/2,
@@ -12,7 +12,7 @@
1212

1313
-export_type([
1414
domain/0,
15-
stream_id/0,
15+
aggregate_id/0,
1616
sequence/0,
1717
type/0,
1818
metadata_key/0,
@@ -22,7 +22,7 @@
2222
tags/0,
2323
payload/0,
2424
t/0,
25-
key/0
25+
target/0
2626
]).
2727

2828
%%--------------------------------------------------------------------
@@ -32,8 +32,12 @@
3232
-doc "Domain identifier, representing a bounded context.".
3333
-type domain() :: atom().
3434

35-
-doc "Stream identifier, uniquely identifying the target aggregate stream.".
36-
-type stream_id() :: binary().
35+
-doc """
36+
Aggregate identifier, uniquely identifying an aggregate instance.
37+
38+
Can be any term (UUID, binary, integer, etc.).
39+
""".
40+
-type aggregate_id() :: term().
3741

3842
-doc "Sequence of the command when batching operations (optional semantic).".
3943
-type sequence() :: non_neg_integer().
@@ -67,43 +71,49 @@ A command represents an intent to change the state of an aggregate in a specific
6771
It consists of:
6872
- `domain`: The domain this command belongs to
6973
- `type`: The type of command to execute
70-
- `stream_id`: Identifier of the target aggregate stream
74+
- `aggregate_id`: Identifier of the target aggregate instance
7175
- `sequence`: Optional sequencing information for idempotency/correlation
7276
- `metadata`: Additional contextual information (user, timestamp, correlation ID, etc.)
7377
- `tags`: Labels for categorization or routing
74-
- `payload`: The actual command data (self-sufficient, includes aggregate ID(s))
78+
- `payload`: The actual command data
7579
""".
7680
-type t() :: #{
7781
domain := domain(),
7882
type := type(),
79-
stream_id := stream_id(),
83+
aggregate_id := aggregate_id(),
8084
sequence := sequence(),
8185
metadata := metadata(),
8286
tags := tags(),
8387
payload := payload()
8488
}.
8589

86-
-type key() :: {domain(), stream_id(), sequence()}.
90+
-doc """
91+
Command target identifier.
92+
93+
The target is a tuple of the Domain and the AggregateId that identifies
94+
which aggregate this command is addressed to.
95+
""".
96+
-type target() :: {domain(), aggregate_id()}.
8797

8898
%%--------------------------------------------------------------------
8999
%% Functions
90100
%%--------------------------------------------------------------------
91101

92-
-spec new(domain(), type(), stream_id(), sequence(), metadata(), payload()) -> t().
93-
new(Domain, Type, StreamId, Sequence, Metadata, Payload) ->
102+
-spec new(domain(), type(), aggregate_id(), sequence(), metadata(), payload()) -> t().
103+
new(Domain, Type, AggregateId, Sequence, Metadata, Payload) ->
94104
#{
95105
domain => Domain,
96106
type => Type,
97-
stream_id => StreamId,
107+
aggregate_id => AggregateId,
98108
sequence => Sequence,
99109
metadata => Metadata,
100110
tags => [],
101111
payload => Payload
102112
}.
103113

104-
-spec key(t()) -> key().
105-
key(#{domain := D, stream_id := S, sequence := Seq}) ->
106-
{D, S, Seq}.
114+
-spec target(t()) -> target().
115+
target(#{domain := D, aggregate_id := AggId}) ->
116+
{D, AggId}.
107117

108118
-spec with_metadata(metadata(), t()) -> t().
109119
with_metadata(Meta, Command) when is_map(Command) ->

apps/es_kernel/src/es_contract_event.erl

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,20 @@
3232
-doc "Domain identifier, representing a bounded context.".
3333
-type domain() :: atom().
3434

35-
-doc "Stream identifier, uniquely identifying an event stream within a domain.".
36-
-type stream_id() :: binary().
35+
-doc """
36+
Aggregate identifier, uniquely identifying an aggregate instance.
37+
38+
Can be any term (UUID, binary, integer, etc.).
39+
""".
40+
-type aggregate_id() :: term().
41+
42+
-doc """
43+
Stream identifier, uniquely identifying an event stream.
44+
45+
A stream is identified by a tuple of {Domain, AggregateId}, ensuring no collisions
46+
across different domains and aggregate instances.
47+
""".
48+
-type stream_id() :: {domain(), aggregate_id()}.
3749

3850
-doc "Sequence number of the event within its stream, starting from 0.".
3951
-type sequence() :: non_neg_integer().
@@ -84,10 +96,10 @@ An event represents a fact that something has happened in the system. It consist
8496
-doc """
8597
Composite key uniquely identifying an event.
8698

87-
The key is a tuple of the Domain, the StreamId and the Sequence that uniquely identifies
99+
The key is a tuple of the StreamId and the Sequence that uniquely identifies
88100
an event within the entire event store.
89101
""".
90-
-type key() :: {domain(), stream_id(), sequence()}.
102+
-type key() :: {stream_id(), sequence()}.
91103

92104
%%--------------------------------------------------------------------
93105
%% Functions
@@ -106,8 +118,8 @@ new(Domain, Type, StreamId, Sequence, Metadata, Payload) ->
106118
}.
107119

108120
-spec key(t()) -> key().
109-
key(#{domain := D, stream_id := S, sequence := Seq}) ->
110-
{D, S, Seq}.
121+
key(#{stream_id := S, sequence := Seq}) ->
122+
{S, Seq}.
111123

112124
-spec with_metadata(metadata(), t()) -> t().
113125
with_metadata(Meta, Event) when is_map(Event) ->

apps/es_kernel/src/es_contract_snapshot.erl

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,18 @@
2727
-doc "Domain identifier, representing a bounded context.".
2828
-type domain() :: atom().
2929

30-
-doc "Stream identifier, uniquely identifying an event stream within a domain.".
31-
-type stream_id() :: binary().
30+
-doc """
31+
Aggregate identifier, uniquely identifying an aggregate instance (can be UUID, binary, etc.).
32+
""".
33+
-type aggregate_id() :: term().
34+
35+
-doc """
36+
Stream identifier, uniquely identifying an event stream.
37+
38+
A stream is identified by a tuple of {Domain, AggregateId}, ensuring no collisions
39+
across different domains and aggregate instances.
40+
""".
41+
-type stream_id() :: {domain(), aggregate_id()}.
3242

3343
-doc "Sequence number of the event within its stream, starting from 0.".
3444
-type sequence() :: non_neg_integer().
@@ -60,10 +70,11 @@ A snapshot represents a point-in-time capture of an aggregate's state. It consis
6070

6171
-doc """
6272
Composite key uniquely identifying a snapshot.
63-
The key is a tuple of the Domain, the StreamId and the Sequence that uniquely identifies
73+
74+
The key is a tuple of the StreamId and the Sequence that uniquely identifies
6475
a snapshot within the entire snapshot store.
6576
""".
66-
-type key() :: {domain(), stream_id(), sequence()}.
77+
-type key() :: {stream_id(), sequence()}.
6778

6879
%%--------------------------------------------------------------------
6980
%% Functions
@@ -80,8 +91,8 @@ new(Domain, StreamId, Sequence, Metadata, State) ->
8091
}.
8192

8293
-spec key(t()) -> key().
83-
key(#{domain := D, stream_id := S, sequence := Seq}) ->
84-
{D, S, Seq}.
94+
key(#{stream_id := S, sequence := Seq}) ->
95+
{S, Seq}.
8596

8697
-spec with_metadata(metadata(), t()) -> t().
8798
with_metadata(Meta, Snap) when is_map(Snap) ->

apps/es_kernel/src/es_kernel.app.src

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
{applications, [kernel, stdlib]},
77
{env, [
88
{event_store, es_store_ets},
9-
{snapshot_store, es_store_ets}
9+
{snapshot_store, es_store_ets},
10+
{snapshot_interval, 0}
1011
]},
1112
{modules, [
1213
es_contract_aggregate,

0 commit comments

Comments
 (0)