hanami / events

[Experimental] Events framework for Hanami
MIT License
43 stars 7 forks source link

[FIX #64] Introduce typed event attributes #65

Open smakagon opened 6 years ago

smakagon commented 6 years ago

This PR adds typed attributes to events using dry-struct.

Example of event definition:

class UserCreated < Hanami::Event::Type
  event_name 'user.created'

  attribute :id, Hanami::Types::UUID
  attribute :name, Hanami::Types::String
end

We can instantiate and broadcast event now:

user_created = UserCreated.new(id: "f1306260-6268-4a19-80dd-54ad1ac82039", name: "Sergii")
event_bus.broadcast(user_created)

Subscriber will receive object of UserCreated event:

event_bus.subscribe('user.*') do |event|
  event # => <UserCreated id="f1306260-6268-4a19-80dd-54ad1ac82039" name="Sergii">
end

PS: @davydovanton I have a question regarding naming. I included Event to Hanami::Event namespace, I'm not sure if it's the best way. As well as use of Hanami::Types to define attribute types for an event. Probably we should discuss it too.

davydovanton commented 6 years ago

Thanks for PR 👍

But I think we need to discuss some parts and understand what we want.


First of all, I think we must set types as optional. If you want to use type object - use it, if not - use a payload hash. Because we need to be easy and open for new developers. And provide high-level features if other want it. (I know that it will be harder for maintaining, but we need it )

password_changed = Users::Events::PasswordChanged.new(payload)
events.broadcast(password_changed)
# here you need to create more objects but you have more control

# or

events.broadcast('event.name', payload)
# simple and easy for start

About types. Just drop it from our gem :) I think we should understand that each event has user data (user_id, email, etc) and meta-information (event_id, version, etc). And we need to separate all this data. And for me it will be better to use something like this:

Event

class Users::Events::PasswordChanged < Hanami::Event::Type
  event_name 'user.password_changed'

  attribute :user_id, Types::UUID
  attribute :password, Types::String
end

Broadcaster

password_changed = Users::Events::PasswordChanged.new(payload)
events.broadcast(password_changed)

Subscriber

events.subscribe(Users::Events::PasswordChanged) do |object|
  object # => Users::Events::PasswordChanged

  object.meta # => { id: 'uuid', version: '1.0', ... }
  # or
  object.meta
  # => dry-s imutable object like this one:
  # =>
  # =>   EventMeta.new(id: 'uuid', version: '1.0', ...)
end

WDYT?

smakagon commented 6 years ago

@davydovanton thanks for the feedback! Yes, I agree that this functionality should be optional. I'm just thinking about the way to specify it. Maybe using something like schema: strict or schema: weak during initialization of event_bus with an adapter.

We should discuss other details as well. I just wanted to start moving in that direction, because it seems promising and I'm sure there is a need for events with a schema.

You're right about meta information, it should be included in the event object. We should come up with meta information we want to have and the way to specify it if needed.

davydovanton commented 6 years ago

About event type: I prefere if lib will raise error if types will different. Because we need to be sure that event log will have only valid data.

Again, great work and I'm happy to start think about it 👍 💯

Meta: let's think how we will store it :)

davydovanton commented 6 years ago

@smakagon any updates? :)

smakagon commented 6 years ago

@davydovanton I'll update this PR soon, sorry for the delay.

Morozzzko commented 5 years ago

@smakagon do you have any plans on continuing the work? I'd love to have this feature, so I'd try and experiment with it in a while. Do you think you could fix the merge conflicts so we could continue?

smakagon commented 5 years ago

@Morozzzko sorry, I do not have time to work on this PR at this moment. If you're interested in this feature, please feel free to take it from here and make the required updates.