Skip to content

Instantly share code, notes, and snippets.

@niquola
Created July 10, 2013 15:47
Show Gist options
  • Save niquola/5967445 to your computer and use it in GitHub Desktop.
Save niquola/5967445 to your computer and use it in GitHub Desktop.
Hangout Model Layer
class PendingOrdersService
def initialize(visit, user) # service context
@visit, @user = visit, user
end
## Queries
def pending_orders
...
end
def ordering_providers
end
## Actions
def build
...
end
def add_to_pending
end
def sign_pendings
end
def cancel
end
end
class SignPendingOrdersService
def initialize(visit, user) # service context
@visit, @user = visit, user
end
def call # or execute
Order.for_visit(visit).pendings.each do |order|
order.activate!
OrderTemplate.find_or_create_template_for_order_and_record_template_usage!(order)
end
end
end

Hangout Model Layer

  • Bounded contexts (Dependency management)
  • Concerns
  • Observers
  • Domain Events
  • Datatypes
  • Service Layer
  • Serialization (store, hstore, serialize)
  • NoActiveRecord
op = OrderingSession.new(visit, user)
op.ordering_providers
order = op.build_order(attrs)
order = op.orders.create(attrs)
op.sign!
op.cancel
order = op.orders.find(params[:order_id])
order.destroy
class OrderSession
class OrderProxy
attr_reader :order_session
delegates * to: :_order
def save
puts 'here'
_order.save
end
def default_ordering_provider
order_session.user.role?(:nurse) ? visit.attending_doctor : order_session.user
end
end
def build(attrs)
OrderProxy.new(Order.build(attrs))
end
def orders
Order.pending.for_visit(visit).map { |o| OrderProxy.new(self, o) }
end
def sign!
orders.each do |o|
o.activate!
create_document(o)
remember_template
end
end
end

Why services?

  • They are used for integration code which does not fit in any model.
  • They are used for integrating different context of application.
  • Separate USE CASE (application) LOGIC from DOMAIN LOGIC

Implementations

One-action service

which performs only one action (much like DCI context)

Single-action service: USED IN POINT OF DIFFERENT CONTEXT INTEGRATION OR USE-CASE SPECIFIC CALCULATIONS OR LOGIC

Business-logic controller

Incapsulates anything from Rails controller under procedural API.

Business Controller: THE LAYER BETWEEN DOMAIN AND RAILS, ALLOWS TO EXTEND EVERY STEP OF USE-CASE WITH CUSTOM LOGIC.

Service as Resource:

Implement object-oriented API for service instead of procedural. You can use something like OrderProxy to add custom logic on model create/update/etc.

Resource-based: IF YOU HAVE INTEGRATION OR USE-CASE LOGIC IN CONTROLLER YOU REPLACE MODEL WITH RESOURCE SERVICE.

Other variants:

Other variants of application-wide integration is Observers or Pub/Sub pattern (Events). But they cannot return value, so they are useful only as post-action callbacks.

Single-action | Business controller | Resource

          |                       |
  • use only | - implement 'stub' | + override only when needed | actions for uniformity| actions you need | | | |
  • unit-test | + integration test | + integration test | (as blackbox) | (repends on impl) | |
  • simplest | + simple | - hard to implement | | (a lot of magic) | |
  • single resp | +- use-case respons | +- use-case rsponsibility | |
  • strong api | - variable api | +- model-like api | |
  • a lot of | +- 1 serv for 1 use- | +- same as business contrl different en| case, but may be fat | | |
  • can be easily - hard to reuse and | - same as business contrl reused and | because of single-purpose combined | impl. | | |
  • cannot be | + can be used in prot,| +- can be used for proto, used for proto| with const API | but not easy | |
  • rich model | - can lead to anemic | + rich model | model | | |
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment