-
Notifications
You must be signed in to change notification settings - Fork 0
/
midt.go
148 lines (117 loc) · 3.68 KB
/
midt.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
// Copyright 2023 Ssen Galanto. All rights reserved.
// Package midt provides mediator pattern that reduces coupling between components
// of a program by making them communicate indirectly, through a special mediator struct.
package midt
import (
"context"
"fmt"
"reflect"
)
type RequestHandler interface {
Name() string
Handle(ctx context.Context, request any) (any, error)
}
type NotificationHandler interface {
Name() string
Handle(ctx context.Context, notification any) error
}
type PipelineBehaviour interface {
Handle(ctx context.Context, request any, next RequestHandlerFunc) (any, error)
}
type RequestHandlerFunc func() (any, error)
type Midt struct {
requestHandlerRegistry map[string]RequestHandler
notificationHandlerRegistry map[string]NotificationHandler
pipelineBehaviourRegistry []PipelineBehaviour
}
// New creates a new Midt instance.
func New() *Midt {
return &Midt{
requestHandlerRegistry: map[string]RequestHandler{},
notificationHandlerRegistry: map[string]NotificationHandler{},
pipelineBehaviourRegistry: []PipelineBehaviour{},
}
}
// RegisterRequestHandler register a RequestHandler in the registry.
func (m *Midt) RegisterRequestHandler(handler RequestHandler) error {
hn := handler.Name()
_, ok := m.requestHandlerRegistry[hn]
if ok {
return fmt.Errorf("%w: %s", ErrRequestHandlerAlreadyExists, hn)
}
m.requestHandlerRegistry[hn] = handler
return nil
}
// RegisterNotificationHandler register a NotificationHandler in the registry.
func (m *Midt) RegisterNotificationHandler(handler NotificationHandler) error {
hn := handler.Name()
_, ok := m.notificationHandlerRegistry[hn]
if ok {
return fmt.Errorf("%w: %s", ErrNotificationHandlerAlreadyExists, hn)
}
m.notificationHandlerRegistry[hn] = handler
return nil
}
// RegisterPipelineBehaviour register a PipelineBehaviour in the registry.
func (m *Midt) RegisterPipelineBehaviour(behaviour PipelineBehaviour) error {
bt := reflect.TypeOf(behaviour)
exists := m.existsPipeType(bt)
if exists {
return fmt.Errorf("%w: %s", ErrPipelineBehaviourAlreadyExists, bt)
}
m.pipelineBehaviourRegistry = append(m.pipelineBehaviourRegistry, behaviour)
return nil
}
// Send sends the request to its corresponding RequestHandler.
func (m *Midt) Send(ctx context.Context, request any) (any, error) {
rt := reflect.TypeOf(request).String()
handler, ok := m.requestHandlerRegistry[rt]
if !ok {
return nil, fmt.Errorf("%w: %s", ErrRequestHandlerNotFound, rt)
}
if len(m.pipelineBehaviourRegistry) > 0 {
var lastHandler RequestHandlerFunc = func() (any, error) {
return handler.Handle(ctx, request)
}
var aggregateResult = lastHandler
for _, pipe := range m.pipelineBehaviourRegistry {
pipeValue := pipe
nextValue := aggregateResult
aggregateResult = func() (any, error) {
return pipeValue.Handle(ctx, request, nextValue)
}
}
response, err := aggregateResult()
if err != nil {
return nil, err
}
return response, nil
}
response, err := handler.Handle(ctx, request)
if err != nil {
return nil, err
}
return response, nil
}
// Publish publishes the notification event to its corresponding NotificationHandler.
func (m *Midt) Publish(ctx context.Context, request any) error {
rt := reflect.TypeOf(request).String()
handler, ok := m.notificationHandlerRegistry[rt]
if !ok {
return fmt.Errorf("%w: %s", ErrRequestHandlerNotFound, rt)
}
err := handler.Handle(ctx, request)
if err != nil {
return err
}
return nil
}
// existsPipeType checks if a pipeline behaviour exists in the registry.
func (m *Midt) existsPipeType(p reflect.Type) bool {
for _, pipe := range m.pipelineBehaviourRegistry {
if reflect.TypeOf(pipe) == p {
return true
}
}
return false
}