Skip to content

Commit

Permalink
Focus more on state in extracted_state sample
Browse files Browse the repository at this point in the history
  • Loading branch information
mostlyobvious committed Nov 20, 2023
1 parent e416232 commit 2fd896d
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 119 deletions.
61 changes: 21 additions & 40 deletions examples/extracted_state/lib/project_management/command_handler.rb
Original file line number Diff line number Diff line change
@@ -1,53 +1,34 @@
module ProjectManagement
class CommandHandler
def initialize(event_store)
@event_store = event_store
end

def create(cmd)
with_aggregate(cmd.id) { |issue| issue.create }
end

def resolve(cmd)
with_aggregate(cmd.id) { |issue| issue.resolve }
end

def close(cmd)
with_aggregate(cmd.id) { |issue| issue.close }
end

def reopen(cmd)
with_aggregate(cmd.id) { |issue| issue.reopen }
end

def start(cmd)
with_aggregate(cmd.id) { |issue| issue.start }
end
def initialize(event_store) = @event_store = event_store

def stop(cmd)
with_aggregate(cmd.id) { |issue| issue.stop }
end
def create(cmd) = with_aggregate(cmd.id) { |issue| issue.open }
def resolve(cmd) = with_aggregate(cmd.id) { |issue| issue.resolve }
def close(cmd) = with_aggregate(cmd.id) { |issue| issue.close }
def reopen(cmd) = with_aggregate(cmd.id) { |issue| issue.reopen }
def start(cmd) = with_aggregate(cmd.id) { |issue| issue.start }
def stop(cmd) = with_aggregate(cmd.id) { |issue| issue.stop }

private

attr_reader :event_store

def stream_name(id)
"Issue$#{id}"
end
def stream_name(id) = "Issue$#{id}"

def with_aggregate(id)
version = -1
state = IssueState.new(id)
event_store
.read
.stream(stream_name(id))
.each do |event|
state.apply(event)
version += 1
state, version =
@event_store
.read
.stream(stream_name(id))
.reduce([IssueState.initial(id), -1]) do |(state, version), event|
[state.apply(event), version + 1]
end

yield issue = Issue.new(state)
event_store.publish(issue.changes, stream_name: stream_name(id), expected_version: version)

@event_store.publish(
issue.changes,
stream_name: stream_name(id),
expected_version: version
)
rescue Issue::InvalidTransition
raise Error
end
Expand Down
71 changes: 14 additions & 57 deletions examples/extracted_state/lib/project_management/issue.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,79 +9,36 @@ def initialize(state)
@state = state
end

def create
invalid_transition unless can_create?
apply(IssueOpened.new(data: { issue_id: state.id }))
def open
fail if @state.status
@changes << IssueOpened.new(data: { issue_id: @state.id })
end

def resolve
invalid_transition unless can_resolve?
apply(IssueResolved.new(data: { issue_id: state.id }))
fail unless %i[open in_progress reopened].include? @state.status
@changes << IssueResolved.new(data: { issue_id: @state.id })
end

def close
invalid_transition unless can_close?
apply(IssueClosed.new(data: { issue_id: state.id }))
fail unless %i[open in_progress resolved reopened].include? @state.status
@changes << IssueClosed.new(data: { issue_id: @state.id })
end

def reopen
invalid_transition unless can_reopen?
apply(IssueReopened.new(data: { issue_id: state.id }))
fail unless %i[resolved closed].include? @state.status
@changes << IssueReopened.new(data: { issue_id: @state.id })
end

def start
invalid_transition unless can_start?
apply(IssueProgressStarted.new(data: { issue_id: state.id }))
fail unless %i[open reopened].include? @state.status
@changes << IssueProgressStarted.new(data: { issue_id: @state.id })
end

def stop
invalid_transition unless can_stop?
apply(IssueProgressStopped.new(data: { issue_id: state.id }))
fail unless %i[in_progress].include? @state.status
@changes << IssueProgressStopped.new(data: { issue_id: @state.id })
end

private

attr_reader :state

def invalid_transition
raise InvalidTransition
end

def can_reopen?
state.closed? || state.resolved?
end

def can_start?
state.open? || state.reopened?
end

def can_stop?
state.in_progress?
end

def can_close?
state.open? || state.in_progress? || state.reopened? || state.resolved?
end

def can_resolve?
state.open? || state.reopened? || state.in_progress?
end

def can_create?
state.initial?
end

def apply(event)
store_changes(event)
apply_on_state(event)
end

def store_changes(event)
changes << event
end

def apply_on_state(event)
state.apply(event)
end
def fail = raise InvalidTransition
end
end
42 changes: 20 additions & 22 deletions examples/extracted_state/lib/project_management/issue_state.rb
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
module ProjectManagement
class IssueState < Struct.new(:id, :status)
def initial? = status.nil?
def open? = status == :open
def closed? = status == :closed
def in_progress? = status == :in_progress
def reopened? = status == :reopened
def resolved? = status == :resolved
IssueState =
Data.define(:id, :status) do
def self.initial(id)
new(id: id, status: nil)
end

def apply(event)
case event
when IssueOpened
self.status = :open
when IssueResolved
self.status = :resolved
when IssueClosed
self.status = :closed
when IssueReopened
self.status = :reopened
when IssueProgressStarted
self.status = :in_progress
when IssueProgressStopped
self.status = :open
def apply(event)
case event
when IssueOpened
with(status: :open)
when IssueResolved
with(status: :resolved)
when IssueClosed
with(status: :closed)
when IssueReopened
with(status: :reopened)
when IssueProgressStarted
with(status: :in_progress)
when IssueProgressStopped
with(status: :open)
end
end
end
end
end

0 comments on commit 2fd896d

Please sign in to comment.