SebastianEdwards/event_sorcerer

View on GitHub
lib/event_sorcerer/aggregate_loader.rb

Summary

Maintainability
A
35 mins
Test Coverage
module EventSorcerer
  # Public: Service class for loading aggregates from the event store.
  class AggregateLoader
    # Public: Returns the id for the aggregate to be loaded.
    attr_reader :id

    # Public: Creates a new AggregateLoader instance.
    #
    # klass        - class for the aggregate to be loaded.
    # id           - id for the aggregate to be loaded.
    # prohibit_new - whether or not to raise an error if aggregate not existing.
    def initialize(klass, id, events, version, prohibit_new = true)
      @events       = events
      @id           = id
      @klass        = klass
      @prohibit_new = prohibit_new
      @version      = version
    end

    # Public: Wraps and returns aggregate in a proxy.
    #
    # Returns an AggregateProxy.
    # Raises AggregateNotFound if aggregate not found and prohibit_new is true.
    def load
      fail AggregateNotFound if prohibit_new && new_aggregate?

      AggregateProxy.new(aggregate)
    end

    private

    # Private: Returns array of Events to hydrate with.
    attr_reader :events

    # Private: Returns the class for the aggregate to be loaded.3
    attr_reader :klass

    # Private: Returns whether new aggregates will be allowed.
    attr_reader :prohibit_new

    # Private: Returns current version of aggregate.
    attr_reader :version

    # Private: Memorizes and returns a new instance of an aggregate. Applies
    #          existing events from the event store.
    def aggregate
      @aggregate ||= klass.build.tap do |aggregate|
        aggregate.instance_variable_set(:@id, id)
        aggregate.instance_variable_set(:@local_version, version)
        aggregate.instance_variable_set(:@persisted_version, version)
        events.each { |event| EventApplicator.apply_event!(aggregate, event) }
      end
    end

    # Private: Checks whether the aggregate is completely new.
    #
    # Returns true if aggregate is new.
    # Returns false if aggregate is not new.
    def new_aggregate?
      !version || version == 0
    end
  end
end