Last active
May 27, 2018 06:29
-
-
Save andrewhao/46d16664d938f42c77733dc9a0d54d4e to your computer and use it in GitHub Desktop.
Evented Rails - Decoupling complex domains in Rails with Domain Events
This file contains hidden or 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
# ----------------------------- | |
# Passenger (Experience) Domain | |
# ----------------------------- | |
module Passenger | |
# app/domains/passenger/find_driver_match.rb | |
class FindDriverMatch | |
include Wisper::Publisher | |
def call(passenger, ride_requirements) | |
# Publishes the event (a fact), but lets someone else do the rest of the hard work. | |
broadcast("passenger_ride_requested", { | |
ride_requirements: ride_requirements, | |
passenger: passenger | |
}) | |
end | |
end | |
# A class devoted to handling incoming events from the Routing domain. | |
# Handlers are added to specific events as-needed from within this (Passenger) domain. | |
# | |
# app/domains/passenger/routing_event_handler.rb | |
class RoutingEventHandler | |
def self.routing_driver_notified | |
# Inform the passenger that the driver is on their way | |
Passenger::SendDriverArrivalNotification.new.call(passenger, driver) | |
end | |
def self.routing_no_driver_found | |
# Inform the passenger that no driver is available at this time. | |
Passenger::SendRideUnavailableNotification.new.call(passenger) | |
end | |
end | |
end | |
# -------------- | |
# Routing Domain | |
# -------------- | |
module Routing | |
# A service class devoted to sending a notification or text to the driver and | |
# acknowledging that they are on their way to the passenger. | |
# | |
# app/domains/routing/notify_driver.rb | |
class NotifyDriver | |
include Wisper::Publisher | |
def call(driver, passenger) | |
# ... | |
# Perform some Routing related code that notifies the driver's mobile device | |
# and sends them on their way | |
# ... | |
broadcast("routing_driver_notified", driver: driver, passenger: passenger) | |
end | |
end | |
# Lightweight notification class that broadcasts the event that a driver is not available. | |
class NoDriverFound | |
include Wisper::Publisher | |
def call(passenger) | |
broadcast("routing_no_driver_found", passenger: passenger) | |
end | |
end | |
# A class devoted to handling incoming events from the Passenger domain. | |
# Handlers are added to specific events as-needed from within this (Routing) domain. | |
# | |
# app/domains/routing/passenger_event_handler.rb | |
class PassengerEventHandler | |
# Handles the event appropriately with domain-specific code. | |
# The method name here corresponds with the name of the event. | |
def self.passenger_ride_requested(payload) | |
driver = Routing::FindDrivers.new(payload[:passenger]).call | |
.select { |d| d.meets?(payload[:ride_requirements]) && d.matches_some_deeply_specific_thing? } | |
.sample | |
if driver | |
Routing::NotifyDriver.new.call(driver, payload[:passenger]) | |
else | |
Routing::NoDriverFound.new.call(payload[:passenger]) | |
end | |
end | |
end | |
end | |
# The app is configured to hook up a subscriber to the "passenger_experience_ride_requested" event | |
# config/initializers/domain_event_subscriptions.rb | |
Wisper.subscribe(Passenger::FindDriverMatch, scope: Routing::PassengerEventHandler) | |
Wisper.subscribe(Routing::NotifyDriver, scope: Passenger::RoutingEventHandler) |
This file contains hidden or 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
module Passenger | |
class FindDriverMatch | |
def call(passenger, ride_requirements) | |
# Call another part of the system that has to do with routing | |
# but requires deep internal knowledge of that code! | |
driver = Routing::FindDrivers.new(passenger).call | |
.select! { |d| d.meets?(ride_requirements) && d.matches_some_deeply_specific_thing? } | |
.sample | |
if driver | |
Routing::NotifyDriver.new.call(driver) | |
# Inform the passenger that the driver is on their way | |
Passenger::SendDriverArrivalNotification.new.call(passenger, driver) | |
else | |
# Inform the passenger that no driver is available at this time. | |
Passenger::SendRideUnavailableNotification.new.call(passenger) | |
end | |
end | |
end | |
end |
This file contains hidden or 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
# The app is configured to hook up a subscriber to the | |
# "passenger_experience_ride_requested" event | |
# config/initializers/domain_event_subscriptions.rb | |
Wisper.subscribe( | |
Passenger::RideRequested, | |
scope: Routing::PassengerEventHandler, | |
async: true | |
) | |
Wisper.subscribe( | |
Routing::NotifyDriver, | |
scope: Passenger::RoutingEventHandler | |
async: true | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment