Created
August 5, 2011 00:16
-
-
Save bencrouse/1126643 to your computer and use it in GitHub Desktop.
DDDish Rails
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
# | |
# Controller - Application service layer until a separation has clear value | |
# | |
# | |
class CartController < ApplicationController | |
before_filter :find_variant | |
rescue_from Model::InvalidOperation, with: :domain_error | |
def add_item | |
current_cart.add_variant(@variant, params[:quantity].to_i) | |
resource = CartResource.save_model(current_cart) | |
if resource.saved? | |
redirect_to cart_path | |
else | |
@errors = resource.errors | |
render 'products/show' | |
end | |
end | |
def remove_item | |
current_cart.remove_variant(@variant) | |
resource = CartResource.save_model(current_cart) | |
if resource.saved? | |
redirect_to cart_path | |
else | |
@errors = resource.errors | |
render 'show' | |
end | |
end | |
protected | |
def domain_error(error) | |
flash[:error] = error.message | |
render :show | |
end | |
def find_variant | |
@variant = VariantResource.get_model(params[:variant_id]) | |
end | |
def current_cart | |
@current_cart ||= CartResource.get_model(session[:cart_id]) | |
end | |
end | |
# | |
# Resource - responsible for domain model persistence | |
# | |
# | |
class CartResource | |
include DataMapper::Resource | |
storage_names[:default] = 'carts' | |
property :id, Serial | |
property :subtotal_price, Money | |
property :discount_price, Money | |
property :total_price, Money | |
belongs_to :user, 'UserResource', required: false | |
has 1, :order, 'OrderResource', constraint: :destroy! | |
has n, :items, 'CartItemResource', constraint: :destroy! | |
# DSL for saving domain models (allow .get_model and .save_model to be abstracted) | |
aggregate_root Cart | |
aggregates items: :items | |
end | |
# | |
# Domain Model - responsible for business logic and rules | |
# | |
# | |
class Cart | |
include Model | |
include Pricing::Model | |
attribute :id, String | |
attr_reader :items | |
calculates :subtotal_price, SubtotalCalculator | |
calculates :discount_price, DiscountCalculator | |
calculates :total_price, TotalCalculator | |
def initialize(*) | |
super | |
@items ||= [] | |
end | |
def quantity | |
items.inject(0) { |sum, i| sum + i.quantity } | |
end | |
def calculate_prices | |
items.each(&:calculate_prices) | |
super | |
end | |
def add_variant(variant, quantity) | |
validate variant, :purchasable?, 'sorry, this item is unavailable' | |
items << CartItem.new(variant: variant, quantity: quantity) | |
calculate_prices | |
self | |
end | |
def remove_variant(variant) | |
items.reject! { |i| i.variant == variant } | |
calculate_prices | |
self | |
end | |
end | |
# | |
# Infrastructure | |
# | |
# | |
module Model | |
class InvalidOperation < StandardError; end | |
def self.included(klass) | |
klass.class_eval { include Virtus } | |
end | |
def initialize(*) | |
super | |
yield self if block_given? | |
end | |
def validate(value, validation, message) | |
if value.respond_to?(validation) | |
raise InvalidOperation, message if !value.send(validation) | |
elsif validation =~ /^not_/ && value.respond_to?(validation[4..-1]) | |
raise InvalidOperation, message if value.send(validation[4..-1]) | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment