Facade Pattern as the way to implement Aggregate

Subscribe to receive new articles. No spam. Quality content.

Hi guys. Today I would like to discuss Facade Pattern and a little bit of Domain-Driven Design. I will start from description of Facade Pattern and show how it can help us to implement Aggregate - one of the main concepts of DDD.

I'll use definition from book "Design Patterns: Elements of Reusable Object-Oriented Software" to give you brief understanding and then we will go down to example.

Intent

Provide a unified interface to a set of interfaces in a subsystem. Facade defines a higher-level interface that makes the subsystem easier to use.

Motivation

Structuring a system into subsystems helps reduce complexity. A common design goal is to minimize the communication and dependencies between subsystems. One way to achieve this goal is to introduce a facade object that provides a single, simplified interface to the more general facilities of a subsystem.

Without Facade with clean interface our system might look like this:

Facade Pattern

With a Facade it will look like this:

Facade Pattern

Seems much better. Without going too deep into implementation, last picture shows that having one facade for underlying implementation simplifies the structure and decouples objects.

We can think about Facade as a way to encapsulate complex logic into container with nice interface. In Ruby we usually encapsulate logic using public and private methods. We want to have nice public method and couple private methods-helpers that support public method. In case of Facade we have public interface - class that interacts with clients, and "private" classes that implement business logic. I call those classes "private" with quotes because we can't restrict other classes from calling it, but we should try to keep that boundary and use interface of Facade.

Gang of Four suggest to use Facade when:

  • you want to provide simple interface to a complex subsystem
  • there are many dependencies between clients and the implementation classes of an abstraction
  • you want to layer your subsystems

With Facade pattern client classes should communicate with subsystem only using facade, and facade is responsible for forwarding those messages to appropriate objects. Also clients that use the facade don't have to access its subsystem objects directly. That's why I called those classes "private".

Now we have understanding of Facade and how it can help us to structurize and decouple elements of system, we can move forward and learn something new called Aggregate.

Aggregate and Domain-Driven approach

An Aggregate is a cluster of associated objects that we treat as a unit for the purpose of data changes. Each Aggregate has root and a boundary. The boundary defines what is inside the Aggregate. The root is single specific Entity contained in Aggregate. The root is the only member of the Aggregate that outside objects are allowed to hold references to, although objects within the boundary may hold references to each other.

That's the definition of Aggregate from book "Domain-Driven Design: Tackling Complexity in the Heart of Software" by Eric Evans

Definition of Aggregate is pretty similar to definition of Facade. Idea is the same: to encapsulate complex logic into some boundaries and have Aggregate Root - Entity which will forward all requests to inner classes.

I attended a really solid workshop on Domain-Driven Design by Arkency. After that workshop I've prepared talk to describe main concepts of DDD, and here is one of the slides from my talk that describes Aggregate:

Aggregate

The same concept, right? One Aggregate Root - Order with clean interface, boundary and inner implementation with OrderItems entity and VatRate value object. Customer should deal with order, order items and price using facade - Order class.

One important part that I should mention is this line in definition: "An Aggregate is a cluster of associated objects that we treat as a unit for the purpose of data changes."

Aggregate is responsible for keeping data integrity.

In any system with persistent storage of data, there must be a scope for a transaction that changes data, and a way of maintaining the consistency of the data (that is, maintaining its invariants).

Aggregate sets this boundary, the scope for transaction that changes data. It leads us to the following requirement:

When a change to any object within the Aggregate boundary is committed, all invariants of the whole Aggregate should be satisfied.

Example

I prepared really-really simple example just to show the concept. We will create Order Aggregate. We will use Order as an Aggregate Root and create couple additional classes that will be used within the boundary of aggregate. Client will use Order as a facade.

class Order
  attr_reader :items

  PriceError = Class.new(StandardError)
  QuantityError = Class.new(StandardError)

  def initialize
    @items = []
  end

  def add_item(id:, name:, price:, quantity:)
    raise PriceError if price <= 0
    raise QuantityError if quantity <= 0
    @items << OrderItem.new(id: id, name: name, price: price, quantity: quantity)
  end

  def remove_item(id:)
    @items.delete_if { |item| item.id == id }
  end

  def total_price
    items.map(&:total_price).sum
  end
end

class OrderItem
  attr_reader :id, :name, :price, :quantity

  def initialize(id:, name:, price:, quantity:)
    @id = id
    @name = name
    @price = price
    @quantity = quantity
  end

  def total_price
    @total_price = begin
      total = price * quantity
      total + (total * VatRate.value)
    end
  end
end

class VatRate
  def self.value
    0.05
  end
end

aggregate = Order.new

# client uses aggregate
aggregate.add_item(id: 10, name: 'DDD Book', price: 100, quantity: 1)
puts aggregate.total_price # => 105 (100 with Vat)

aggregate.add_item(id: 11, name: 'Implementing DDD Book', price: 50, quantity: 3)
puts aggregate.total_price # => 262.5 (105 + (150 + (150 * 0.05))

aggregate.remove_item(id: 10)
puts aggregate.total_price # => 157.5

To forward requests to OrderItem we could use SimpleDelegator. Doing that we will not break Law of Demeter. Also Aggregate Root should be an Entity, that means that it should have identity. For the matter of simplicity I omitted this part here.

Thanks for reading! Let me know how you use Facade pattern in your projects and which aggregates you have.

Subscribe to receive new articles. No spam. Quality content.

Comments