-
Notifications
You must be signed in to change notification settings - Fork 3
/
aggregate.go
167 lines (136 loc) · 3.75 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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package eventsourcing
import (
"encoding/json"
)
// AggregateState is an interface defining methods that should be implemented by any state that is part of an aggregate.
type AggregateState interface {
Type() string // Returns the type of the aggregate state.
Zero() AggregateState // Returns a zero value of the aggregate state.
}
type StorageNamer interface {
StorageName() string
}
func StorageName(s AggregateState) string {
if sn, ok := s.(StorageNamer); ok {
return sn.StorageName()
}
return s.Type()
}
type AggregateStateEventMapper[S AggregateState] interface {
EventsMap() map[string]EventState[S]
}
// InitZeroAggregate initializes an aggregate with the zero state.
func InitZeroAggregate[S AggregateState](state S) *Aggregate[S] {
return &Aggregate[S]{
_type: state.Type(),
state: state,
changes: make([]*Event[S], 0),
}
}
// InitAggregate initializes an aggregate with the provided id, version, and state.
func InitAggregate[S AggregateState](id string, version int, state S) *Aggregate[S] {
return &Aggregate[S]{
id: id,
version: version,
_type: state.Type(),
state: state,
}
}
// Aggregate represents an aggregate in event sourcing.
type Aggregate[S AggregateState] struct {
id string
version int
_type string
state S
changes []*Event[S]
metadata Metadata
Invariants []func(*Aggregate[S]) error
}
// Clone creates a deep copy of the Aggregate.
func (a *Aggregate[S]) Clone() *Aggregate[S] {
var clone S
m, _ := json.Marshal(a.state)
_ = json.Unmarshal(m, &clone)
return &Aggregate[S]{
id: a.id,
version: a.version,
_type: a.state.Type(),
state: clone,
}
}
// Metadata returns the metadata of the aggregate.
func (a *Aggregate[S]) Metadata() Metadata {
return a.metadata
}
// MustAlso appends given invariants to the existing list of invariants.
func (a *Aggregate[S]) MustAlso(ii ...func(*Aggregate[S]) error) {
a.Invariants = append(a.Invariants, ii...)
}
// Must replaces the current invariants with the provided ones.
func (a *Aggregate[S]) Must(ii ...func(*Aggregate[S]) error) {
a.Invariants = ii
}
// Check checks all invariants of the aggregate and returns the first error encountered. Check returns nil if all
// Invariants passed without failing.
func (a *Aggregate[S]) Check() error {
for _, inv := range a.Invariants {
if err := inv(a); err != nil {
return err
}
}
return nil
}
// CheckAll checks all invariants of the aggregate and returns errors for every failed Invariants. CheckAll returns nil
// if all Invariants passed without failing.
func (a *Aggregate[S]) CheckAll() []error {
errs := make([]error, 0)
for _, inv := range a.Invariants {
if err := inv(a); err != nil {
errs = append(errs, err)
}
}
if len(errs) != 0 {
return errs
}
return nil
}
// Changes returns all changes/events associated with the aggregate.
func (a *Aggregate[S]) Changes() []*Event[S] {
return a.changes
}
// ID returns the ID of the aggregate.
func (a *Aggregate[S]) ID() string {
return a.id
}
// Type returns the type of the aggregate.
func (a *Aggregate[S]) Type() string {
return a._type
}
// Version returns the version of the aggregate.
func (a *Aggregate[S]) Version() int {
return a.version
}
func (a *Aggregate[S]) incrementVersion() int {
a.version++
return a.version
}
// State returns the current state of the aggregate.
func (a *Aggregate[S]) State() S {
return a.state
}
// MarshalJSON marshals the aggregate into JSON format.
func (a *Aggregate[S]) MarshalJSON() ([]byte, error) {
type J struct {
ID string `json:"id"`
Version int `json:"version"`
Type string `json:"type"`
State S `json:"state"`
}
res := J{
ID: a.id,
Version: a.version,
Type: a._type,
State: a.state,
}
return json.Marshal(&res)
}