RailsEventStore / rails_event_store

A Ruby implementation of an Event Store based on Active Record
http://railseventstore.org
MIT License
1.42k stars 121 forks source link

Issue with optional command attributes #34

Closed gottfrois closed 7 years ago

gottfrois commented 8 years ago

Let's say we have the following command:

module Commands
  class CreateOrder
    include ActiveModel::Model
    include ActiveModel::Validations

    attr_accessor :order_id, :customer_id, :some_optional_attribute

    validates :order_id, :customer_id, presence: true
  end
end

Now in my domain:

# ...
def apply_events_order_created(event)
  self.order_id = event.order_id
  self.customer_id = event.customer_id
  self.some_optional_attribute = event.some_optional_attribute
end
# ...

The way the event handles data currently is by defining a getter method for each attributes. If the optional attribute is not present when we create the event, it will throw a NoMethodError exception when trying to access event.some_optional_attribute.

I think it should return nil.

What are your thoughts on this?

mpraglowski commented 8 years ago

I would go with idea that RES should not be opinionated about how Event is implemented. Anything that fulfils the Event "contract" should be ok for it. The discussion is here https://github.com/arkency/rails_event_store/issues/27

I do not understand the issue here. With command defined like this when you publish your event you should have in your Order class:

class Order
  def create(customer, some_optional_attribute)
    apply OrderCreated.new(order_id: self.id, customer_id: customer.id, self.some_optional_attribute: self.some_optional_attribute)
  end
end

That way a some_optional_attribute will be always defined in domain event, event if its value is nil (implemented here: https://github.com/arkency/ruby_event_store/blob/master/lib/ruby_event_store/event.rb#L6-L9 )

gottfrois commented 8 years ago

Thing is, I generate the argument list from the command doing something like command.to_h. If the optional argument was not set in the command, it will not be present in the resulting hash, not even with a nil value.

Killavus commented 8 years ago

I believe your case with to_h can be easily fixed by introducing a better-suited method called to_event which does basically this:

def to_event
  { <optional_argument_1>: nil, <optional_argument_2>: nil }.merge(self.to_h)
end

This way you'll be sure your optional arguments are defined within an event even if they're not available in the to_h method.

What do you think about it?

gottfrois commented 8 years ago

Thanks for the suggestion. Unfortunately I have many commands with different argument signature. I don't see myself specifying each optional attributes in each one of them, does not sound flexible.

What i'm doing now, define a base event class which inherits from RailsEventStore::Event and define:

def method_missing(method_name, *args, &block)
  data.fetch(method_name, nil)
end
mpraglowski commented 8 years ago

@gottfrois I would like to avoid "magic" here. The domain events are the contract between different parts (bounded contexts) of your applications (or even between 3rd party systems). In issue #27 the expectations are to have strict contract on event's attributes - this could also solve your issue here - you will define the contract for an event and you won't need a method_missing.

gottfrois commented 7 years ago

Deprecated