Created
December 30, 2016 16:40
-
-
Save mpraglowski/e744d720e5340ec87aedc6e4c82dd86f to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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