Class: FourthDimensional::AggregateRoot

Inherits:
Object
  • Object
show all
Includes:
Eventable
Defined in:
lib/fourth_dimensional/aggregate_root.rb

Overview

FourthDimensional::AggregateRoot

An aggregate root is an object whose entire state is built by applying events in sequential order.

Often you will see an aggregate root providing a method to create the event followed by a event binding to apply the changes to the current object.

class Post < FourthDimensional::AggregateRoot
  attr_reader :state, :title

  def initialize(*args)
    super

    @state = :draft
  end

  def add(title:)
    apply PostAdded, data: { title: title }
  end

  def delete
    apply PostDeleted
  end

  on PostAdded do |event|
    @state = :added
    @title = event.data.fetch('title')
  end

  on PostDeleted do |event|
    @state = :deleted
  end
end

aggregate = Post.new(id: SecureRandom.uuid)
aggregate.state # => :draft
aggregate.title # => nil

aggregate.add(title: 'post-title')
aggregate.state # => :added
aggregate.title # => 'post-title'

aggregate.delete
aggregate.state # => :deleted

Constant Summary collapse

UnknownEventError =
Class.new(Error)

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Eventable

included

Constructor Details

#initialize(id:) ⇒ AggregateRoot

Initializes an aggregate with an id



62
63
64
65
66
67
# File 'lib/fourth_dimensional/aggregate_root.rb', line 62

def initialize(id:)
  @id = id

  @applied_events = []
  @version = 1
end

Instance Attribute Details

#applied_eventsObject (readonly)

array of events applied



56
57
58
# File 'lib/fourth_dimensional/aggregate_root.rb', line 56

def applied_events
  @applied_events
end

#idObject (readonly)

aggregate id



53
54
55
# File 'lib/fourth_dimensional/aggregate_root.rb', line 53

def id
  @id
end

#versionObject (readonly)

current version



59
60
61
# File 'lib/fourth_dimensional/aggregate_root.rb', line 59

def version
  @version
end

Instance Method Details

#apply(event_class, **args) ⇒ Object

Applies an event to the aggregate when a callback is bound. **args are merged with the id of the aggregate.

Callbacks are invoked within the instance of the aggregate root.



73
74
75
76
77
# File 'lib/fourth_dimensional/aggregate_root.rb', line 73

def apply(event_class, **args)
  event = event_class.new(args.merge(aggregate_id: id))
  apply_existing_event(event)
  applied_events << event
end

#apply_existing_event(event) ⇒ Object

Calls the event binding without persisting the event being applied. Used when loading an aggregate from an existing store.

post.apply_existing_event(title_updated_event)


83
84
85
86
87
88
89
90
91
92
# File 'lib/fourth_dimensional/aggregate_root.rb', line 83

def apply_existing_event(event)
  callback = self.class.event_bindings[event.class]

  if callback.nil?
    raise UnknownEventError.new("#{self.class.name} doesn't have a binding for '#{event.class}'")
  end

  instance_exec(event, &callback)
  @version = next_version
end