Skip to content
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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open

The mediator pattern implementation #21

wants to merge 1 commit into from

Conversation

comeuplater
Copy link
Collaborator

@comeuplater comeuplater commented Feb 23, 2024

Сделал две реализации:

Подобно проекту https://github.com/mehdihadeli/Go-MediatR/. В нем мне не понравилось, что там публикация событий которые не возвращают ошибок выделяется в набор отдельных методов NotificationHandlers и так же есть такой же набор для обработчиков которые возвращают результат в сигнатуре и два разных метода на отправку событий. Как по мне переусложнили решение. Но такое решение благодаря джереникам позволяет отлавливать ошибки на этапе компиляции, но ему присущи все +/- глобального доступа. К тому же сейчас глобальная версия использует локи, это вызовет замедление при нагрузке, но там можно будте заменить на подобное https://github.com/orcaman/concurrent-map

Второе решение проверяет сигнатуры только в рантайме, это довольно печально. Ибо нельзя подкидывать разные джерики для метов с одинаковым ресивером. Можно совместить техники из двух вариантов и получить что-то подобное:

var m = NewRefUntypedMediator()

func strictSend[E any](m RefUntypedMediator, ctx context.Context,  event E) error  {
	return m.Send(ctx, event)
}

}

func (m *Mediator[C, E]) Unregister(commandType mediator.CommandType) {
m.hLock.RLock()
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.Lock()

@comeuplater comeuplater force-pushed the mediator branch 4 times, most recently from 4ea352f to bd90698 Compare February 28, 2024 13:33
}), nil
}

func (m *RefUntypedMediator) Unsubscribe(event any, handler any) {
Copy link
Owner

@emacsway emacsway Mar 1, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

В принципе, unsubscribe можно сделать вообще приватным, и удалять по индексу в массиве, а сам индекс хранить в локальной области видимости объекта Disposable

Copy link
Collaborator Author

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) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

нет объекта Сессии

Copy link
Collaborator Author

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
}

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

да, у нас же синхронная обработка доменных событий. Им нужно работать в той же транзакции.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А лучше принимать интерфейс контекста. У нас по этой же причине объект сессии реализует интерфейс контекста.

Copy link
Collaborator Author

@comeuplater comeuplater May 27, 2024

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)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Почему не сделать здесь обычный вызов?

Copy link
Collaborator Author

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)
Copy link
Owner

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

Copy link
Collaborator Author

@comeuplater comeuplater Mar 6, 2024

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, решил, что ошибки стоит вернуть только на этапе подписки, а при отправке событий/команд они не нужны.

Но если нужны то можно добавить

Copy link
Owner

@emacsway emacsway May 25, 2024

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)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А тут multierror может вернуться...

@comeuplater comeuplater force-pushed the mediator branch 2 times, most recently from 88d450a to d61b353 Compare May 29, 2024 09:22
subscribers = map[reflect.Type]map[reflect.Value]struct{}{}
)

func Send[T any](ctx context.Context, command T) error {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Команда может вернуть результат.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants