-
Notifications
You must be signed in to change notification settings - Fork 2
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The mediator pattern implementation #21
base: main
Are you sure you want to change the base?
Conversation
} | ||
|
||
func (m *Mediator[C, E]) Unregister(commandType mediator.CommandType) { | ||
m.hLock.RLock() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.Lock()
4ea352f
to
bd90698
Compare
}), nil | ||
} | ||
|
||
func (m *RefUntypedMediator) Unsubscribe(event any, handler any) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
В принципе, unsubscribe можно сделать вообще приватным, и удалять по индексу в массиве, а сам индекс хранить в локальной области видимости объекта Disposable
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
По сути да, но думаю публичность его лишней не будет
delete(m.subscribers[eventType], handlerValue) | ||
} | ||
|
||
func (m *RefUntypedMediator) Publish(event any) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
нет объекта Сессии
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Которая в application слое?
type Session interface {
Atomic(SessionCallback) error
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
да, у нас же синхронная обработка доменных событий. Им нужно работать в той же транзакции.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А лучше принимать интерфейс контекста. У нас по этой же причине объект сессии реализует интерфейс контекста.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ага, это больше говей подход. Добавлю первым аргументом
|
||
eventType := getValueType(event) | ||
for handler, _ := range m.subscribers[eventType] { | ||
call(handler, event) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Почему не сделать здесь обычный вызов?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Потому, что используется reflect.ValueOf(handler) чтобы была возможность регистрировать несколько одинаковых обработчиков и при этом корректно их идентифицировать
type Mediator interface { | ||
Register(commandType any, handler any) (disposable.Disposable, error) | ||
Unregister(commandType any) | ||
Send(command any) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Кстати, тут может вернуться результат с ошибкой. Я смотрю, что Саша это предусматривал https://github.com/adzeitor/mediatr/blob/master/mediator.go#L124
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Я думал про это, но знать о хендлерах характерно для Observer. Pub/Sub не должен знать о своих подписчиках, значит и ошибку обработки ему неоткуда вернуть. Да и если ошибка вернётся, как понять откуда она вернется и правильно ли ее обрабатывать в этом месте?
Рассматривая Mediator как частный случай Pub/Sub, решил, что ошибки стоит вернуть только на этапе подписки, а при отправке событий/команд они не нужны.
Но если нужны то можно добавить
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Я думал про это, но знать о хендлерах характерно для Observer. Pub/Sub не должен знать о своих подписчиках, значит и ошибку обработки ему неоткуда вернуть.
Не совсем понял. И Observer, и Pub/Sub, и Mediator - это паттерны инвертирования осведомленности. Они ничего не должны знать о своих подписчиках.
Но, когда мы посышаем команду, то, несмотря на инвертирование осведомленности, вызывающая сторона осведомлена о вызываемой стороне потому, что команда принадлежит доменной модели исполнителя, и команда посылается конкретному исполнителю. Более того, отправитель в заинтересован в результате обработки команды (в отличии от события).
|
||
Subscribe(eventType any, handler any) (disposable.Disposable, error) | ||
Unsubscribe(eventType any, handler any) | ||
Publish(event any) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
А тут multierror может вернуться...
88d450a
to
d61b353
Compare
subscribers = map[reflect.Type]map[reflect.Value]struct{}{} | ||
) | ||
|
||
func Send[T any](ctx context.Context, command T) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Команда может вернуть результат.
Сделал две реализации:
Подобно проекту https://github.com/mehdihadeli/Go-MediatR/. В нем мне не понравилось, что там публикация событий которые не возвращают ошибок выделяется в набор отдельных методов
NotificationHandlers
и так же есть такой же набор для обработчиков которые возвращают результат в сигнатуре и два разных метода на отправку событий. Как по мне переусложнили решение. Но такое решение благодаря джереникам позволяет отлавливать ошибки на этапе компиляции, но ему присущи все +/- глобального доступа. К тому же сейчас глобальная версия использует локи, это вызовет замедление при нагрузке, но там можно будте заменить на подобное https://github.com/orcaman/concurrent-mapВторое решение проверяет сигнатуры только в рантайме, это довольно печально. Ибо нельзя подкидывать разные джерики для метов с одинаковым ресивером. Можно совместить техники из двух вариантов и получить что-то подобное: