-
Notifications
You must be signed in to change notification settings - Fork 0
/
entityMetadata.go
120 lines (98 loc) · 3.16 KB
/
entityMetadata.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
package eventuate
import "reflect"
type EntityMetadata struct {
EntityTypeName string `json:"typeName"`
EntityId Int128 `json:"entityId"`
EntityVersion Int128 `json:"version"`
HasEntity bool `json:"-"`
EntityInstance interface{} `json:"-"`
metadata *AggregateMetadata `json:"-"`
}
func (entity *EntityMetadata) ApplyEvent(event Event) (*EntityMetadata, error) {
//eventNamePattern := regexp.MustCompile(`^(\w+)Event$`)
if !entity.HasEntity {
return nil, AppError("ApplyEvent: cannot apply events to un-synced entity")
}
aggregate := entity.EntityInstance
meta := entity.metadata
eventData := getUnderlyingValue(event)
eventType := reflect.TypeOf(eventData)
eventMethodName := "ApplyEvent"
if eventMethod, specificMethodExists := meta.eventMethodsMap[eventType]; specificMethodExists {
eventMethodName = eventMethod.Name
}
values, callErr := callMethod(aggregate, eventMethodName, event)
if callErr != nil {
return entity, callErr
}
// len(values) == 1 is ensured in the CreateAggregateMetadata(..)
nextInstanceInterface := values[0].Interface()
if !checkUnderlyingType(nextInstanceInterface, meta.UnderlyingType) {
return entity, AppError("Signature mismatch. Method: %s is either missing or returned unexpected type: %T, not %v", eventMethodName, nextInstanceInterface, meta.UnderlyingType)
}
return &EntityMetadata{
EntityTypeName: entity.EntityTypeName,
HasEntity: true,
EntityInstance: nextInstanceInterface,
metadata: entity.metadata}, nil
}
func (entity *EntityMetadata) applyEvents(events []interface{}) (*EntityMetadata, error) {
result := entity
for _, event := range events {
nextEntity, err := entity.ApplyEvent(event)
if err != nil {
return nil, AppError("Event Application Error: %v", err)
}
result = nextEntity
}
return result, nil
}
func (entity *EntityMetadata) ProcessCommand(command Command) ([]Event, error) {
if !entity.HasEntity {
return nil, AppError("ProcessCommand: cannot process commands against an un-synced entity")
}
commandType := reflect.TypeOf(command)
underlyingType := getUnderlyingType(commandType)
commandMethodName := "ProcessCommand"
commandMethod, doesMethodExist := entity.metadata.commandMethodsMap[underlyingType.Name()]
if doesMethodExist {
commandMethodName = commandMethod.Name
} else {
_, doesMethodExist = entity.metadata.commandMethodsMap[""]
}
if !doesMethodExist {
return nil, AppError("ProcessCommand: command argument cannot be processed, type: %v", underlyingType)
}
aggregate := entity.EntityInstance
values, callErr := callMethod(aggregate, commandMethodName, command)
if callErr != nil {
return nil, callErr
}
result1 := values[0].Interface()
switch t := result1.(type) {
case *[]Event:
{
return *t, nil
}
case []Event:
{
return t, nil
}
case []interface{}:
{
result := make([]Event, len(t))
for idx, evt := range t {
if castEvt, castOk := evt.(Event); castOk {
result[idx] = castEvt
}
}
return result, nil
}
default:
{
return nil,
AppError("ProcessCommand: unexpected type %T in method: %s",
t, commandMethodName)
}
}
}