Skip to content

Instantly share code, notes, and snippets.

@mpraglowski
Created December 30, 2016 16:40
Show Gist options
  • Save mpraglowski/e744d720e5340ec87aedc6e4c82dd86f to your computer and use it in GitHub Desktop.
Save mpraglowski/e744d720e5340ec87aedc6e4c82dd86f to your computer and use it in GitHub Desktop.
require 'rails_event_store'
require 'aggregate_root'
PaymentAuthorized = Class.new(RailsEventStore::Event)
PaymentSuccessed = Class.new(RailsEventStore::Event)
PaymentFailed = Class.new(RailsEventStore::Event)
PaymentCaptured = Class.new(RailsEventStore::Event)
class Payment
InvalidOperation = Class.new(StandardError)
include AggregateRoot
def self.authorize(amount:, payment_gateway:)
transaction_id = payment_gateway.authorize(amount)
puts "Domain model: create new authorized payment #{transaction_id}"
Payment.new.tap do |payment|
payment.apply(PaymentAuthorized.new(data: {
transaction_id: transaction_id,
amount: amount,
}))
end
end
def success
puts "Domain model: handle payment gateway OK notification #{transaction_id}"
raise InvalidOperation unless state == :authorized
schedule_capture
apply(PaymentSuccessed.new(data: {
transaction_id: transaction_id,
}))
end
def fail
puts "Domain model: handle payment gateway NOK notification #{transaction_id}"
raise InvalidOperation unless state == :authorized
apply(PaymentFailed.new(data: {
transaction_id: transaction_id,
}))
end
def capture(payment_gateway:)
puts "Domain model: get the money here! #{transaction_id}"
raise InvalidOperation unless state == :successed
payment_gateway.capture(transaction_id, amount)
apply(PaymentCaptured.new(data: {
transaction_id: transaction_id,
amount: amount,
}))
end
attr_reader :transaction_id
private
attr_reader :amount, :state
def schedule_capture
puts "Domain model: schedule caputre #{transaction_id}"
# send it to background job for performance reasons
end
def apply_payment_authorized(event)
@transaction_id = event.data.fetch(:transaction_id)
@amount = event.data.fetch(:amount)
@state = :authorized
puts "Domain model: apply payment authorized #{transaction_id}"
end
def apply_payment_successed(event)
@state = :successed
puts "Domain model: apply payment successed #{transaction_id}"
end
def apply_payment_failed(event)
@state = :failed
puts "Domain model: apply payment failed #{transaction_id}"
end
def apply_payment_captured(event)
@state = :captured
puts "Domain model: apply payment captured #{transaction_id}"
end
end
class PaymentsService
def initialize(event_store:, payment_gateway:)
@event_store = event_store
@payment_gateway = payment_gateway
end
def authorize(amount:)
payment = Payment.authorize(amount: amount, payment_gateway: payment_gateway)
payment.store("Payment$#{payment.transaction_id}", event_store: event_store)
end
def success(transaction_id:)
payment = Payment.new
payment.load("Payment$#{transaction_id}", event_store: event_store)
payment.success
payment.store("Payment$#{transaction_id}", event_store: event_store)
end
def fail(transaction_id:)
payment = Payment.new
payment.load("Payment$#{transaction_id}", event_store: event_store)
payment.fail
payment.store("Payment$#{transaction_id}", event_store: event_store)
end
def capture(transaction_id:)
payment = Payment.new
payment.load("Payment$#{transaction_id}", event_store: event_store)
payment.capture(payment_gateway: payment_gateway)
payment.store("Payment$#{transaction_id}", event_store: event_store)
end
private
attr_reader :event_store, :payment_gateway
end
class PaymentGateway
def initialize(transaction_id_generator)
@generator = transaction_id_generator
end
def authorize(amount)
puts "Payment gateway: authorize #{amount}"
@generator.call # let's pretend we starting some process here and generated transaction id
end
def capture(transaction_id, amount)
# always ok, yeah we just mock it ;)
puts "Payment gateway: capture #{amount} for #{transaction_id}"
end
end
event_store = RailsEventStore::Client.new(repository: RailsEventStore::InMemoryRepository.new)
random_id = SecureRandom.uuid
gateway = PaymentGateway.new(-> { random_id })
service = PaymentsService.new(event_store: event_store, payment_gateway: gateway)
service.authorize(amount: 500)
# here we wait for notification from payment gateway and when it is ok then:
service.success(transaction_id: random_id)
# now let's pretend our background job has been scheduled and performed:
service.capture(transaction_id: random_id)
puts "Domain events stored in event store"
stream = event_store.read_stream_events_forward("Payment$#{random_id}")
stream.each do |event|
puts event.inspect
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment