-
Notifications
You must be signed in to change notification settings - Fork 3
/
aggregate.go
146 lines (133 loc) · 5.62 KB
/
aggregate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package dogma
// A AggregateMessageHandler models business logic and state.
//
// Aggregates are the primary building blocks of an application's domain logic.
// They enforce the domain's strict invariants.
//
// Aggregates use [Command] messages to represent requests to perform some
// specific business logic and change the state of the application
// accordingly. [Event] messages represent those changes.
//
// Aggregates are stateful. An application typically uses multiple instances of
// an aggregate, each with its own state. For example, a banking application may
// use one instance of the "account" aggregate for each bank account.
//
// The state of each instance is application-defined. Often it's a tree of
// related entities and values. The [AggregateRoot] interface represents the
// "root" entity through which the handler accesses the instance's state.
type AggregateMessageHandler interface {
// Configure describes the handler's configuration to the engine.
Configure(AggregateConfigurer)
// New returns an aggregate root instance in its initial state.
//
// The return value MUST NOT be nil. It MAY be the zero-value of the root's
// underlying type.
//
// Each call SHOULD return the same type and initial state.
New() AggregateRoot
// RouteCommandToInstance returns the ID of the instance that handles a
// specific command.
//
// The return value MUST not be empty. RFC 4122 UUIDs are the RECOMMENDED
// format for instance IDs.
RouteCommandToInstance(Command) string
// HandleCommand executes business logic in response to a command.
//
// The handler inspects the root to determine which events to record, if
// any.
//
// The handler SHOULD NOT have any side-effects beyond recording events.
// Specifically, the implementation MUST NOT modify the root directly. Use
// AggregateCommandScope.RecordEvent() to record an event that represents
// the state change. See also AggregateRoot.ApplyEvent().
//
// If this is the first command routed to this instance, the root is the
// return value of New(). Otherwise, it's the value of the root as it
// existed after handling the command.
//
// While the engine MAY call this method concurrently from separate
// goroutines or operating system processes, the state changes and events
// that represent them always appear to have occurred sequentially.
HandleCommand(AggregateRoot, AggregateCommandScope, Command)
}
// AggregateRoot is an interface for the domain-specific state of a specific
// aggregate instance.
type AggregateRoot interface {
// ApplyEvent updates aggregate instance to reflect the occurrence of an
// event.
//
// This implementation of this method is the only code permitted to
// modify the instance's state.
//
// The method SHOULD accept historical events that are no longer routed to
// this aggregate type. This is typically required by event-sourcing engines
// that sometimes load aggregates into memory by applying their entire
// history.
ApplyEvent(Event)
}
// An AggregateConfigurer configures the engine for use with a specific
// aggregate message handler.
type AggregateConfigurer interface {
// Identity configures the handler's identity.
//
// n is a short human-readable name. It MUST be unique within the
// application at any given time, but MAY change over the handler's
// lifetime. It MUST contain solely printable, non-space UTF-8 characters.
// It must be between 1 and 255 bytes (not characters) in length.
//
// k is a unique key used to associate engine state with the handler. The
// key SHOULD NOT change over the handler's lifetime. k MUST be an RFC 4122
// UUID, such as "5195fe85-eb3f-4121-84b0-be72cbc5722f".
//
// Use of hard-coded literals for both values is RECOMMENDED.
Identity(n string, k string)
// Routes configures the engine to route certain message types to and from
// the handler.
//
// Aggregate handlers support the HandlesCommand() and RecordsEvent() route
// types.
Routes(...AggregateRoute)
// Disable prevents the handler from receiving any messages.
//
// The engine MUST NOT call any methods other than Configure() on a disabled
// handler.
//
// Disabling a handler is useful when the handler's configuration prevents
// it from operating, such as when it's missing a required dependency,
// without requiring the user to conditionally register the handler with the
// application.
Disable(...DisableOption)
}
// AggregateCommandScope performs engine operations within the context of a call
// to the HandleCommand() method of an [AggregateMessageHandler].
type AggregateCommandScope interface {
// InstanceID returns the ID of the aggregate instance.
InstanceID() string
// RecordEvent records the occurrence of an event.
//
// It applies the event to the root such that the applied changes are
// visible to the handler after this method returns.
//
// Recording an event cancels any prior call to Destroy() on this scope.
RecordEvent(Event)
// Destroy signals destruction of the aggregate instance.
//
// Destroying an aggregate instance discards its state. The first command to
// target a destroyed instance operates on a new root.
//
// Destruction occurs once the HandleCommand() method returns. Any future
// call to RecordEvent() on this scope prevents destruction.
//
// The precise destruction semantics are engine defined. For example,
// event-sourcing engines typically do not destroy the record of the
// aggregate's historical events.
Destroy()
// Log records an informational message.
Log(format string, args ...any)
}
// AggregateRoute describes a message type that's routed to or from a
// [AggregateMessageHandler].
type AggregateRoute interface {
Route
isAggregateRoute()
}