go-api-boilerplate icon indicating copy to clipboard operation
go-api-boilerplate copied to clipboard

[question] domain pollution

Open SpareShade opened this issue 1 year ago • 5 comments

Hello @vardius

Thank you for sharing this library, it is very helpful in many regards!

My question is about your choice of appending the metadata in the aggregate itself. Eg.

func (u *User) trackChange(ctx context.Context, event *domain.Event) error {
	var meta domain.EventMetadata
	if i, hasIdentity := identity.FromContext(ctx); hasIdentity {
		meta.Identity = i
	}
	if m, ok := metadata.FromContext(ctx); ok {
		meta.IPAddress = m.IPAddress
		meta.UserAgent = m.UserAgent
		meta.Referer = m.Referer
	}
	if !meta.IsEmpty() {
		event.WithMetadata(&meta)
	}

	u.changes = append(u.changes, event)
	u.version++

	return nil
}

Seeing the UserAgent or IP exposed in the aggregate feels a bit like pollution of a domain with application/infra data. I understand that the aggregate essentially proxies this data from the command handler, which usually is acceptable practice, as aggregate is not making use of that data.

I am wondering if you would not mind sharing your thoughts as to why you chose to enrich the event in the aggregate as opposed to eg. in the aggregate repository implementation.

Thanks again for sharing!

SpareShade avatar Jul 19 '22 01:07 SpareShade

The reason why I did that is because with the message bus I am using the context is not carried over

vardius avatar Jul 20 '22 04:07 vardius

Thank you for getting back on this question.

But aren't the events handled first by the repository, which then publishes them to the event bus? The repository should still have access to the request context, or is this not so.

// Save current user changes to event store and publish each event with an event bus
func (r *userRepository) Save(ctx context.Context, u user.User) error {
	if err := r.eventStore.Store(ctx, u.Changes()); err != nil {
		return apperrors.Wrap(err)
	}

	for _, event := range u.Changes() {
		if err := r.eventBus.Publish(ctx, event); err != nil {
			return apperrors.Wrap(err)
		}
	}

	return nil
}

I don't mean to nitpick. I'm just drawing a lot from your code (very nicely written too), and want to see whether there are potential pitfalls in my implementation/thinking. And of course, it's best to think together :)

SpareShade avatar Jul 22 '22 02:07 SpareShade

yes you are right! i suppose i have missed that. i agree it would feel much better in the repository instead of having this in the domain. would you like to contribute and move it yourself? i would happily merge your pr :) and it feels like an easy change

vardius avatar Jul 22 '22 03:07 vardius

Yes, I can absolutely, glad you agree :).

I am tied up atm, but should have a gap next weekend, it won't be too long in any case :)

SpareShade avatar Jul 22 '22 04:07 SpareShade

ok cool, will w8 ;)

vardius avatar Jul 22 '22 05:07 vardius