Explore Hexagonal Architecture in Ruby, focusing on Ports and Adapters to build scalable, maintainable applications. Learn how to structure Ruby applications for flexibility and testability.
Hexagonal Architecture, also known as the Ports and Adapters pattern, is a design approach that aims to create loosely coupled application components. This architecture promotes the separation of concerns, making applications more maintainable, testable, and adaptable to change. In this section, we’ll delve into the principles of Hexagonal Architecture, its implementation in Ruby, and the benefits it brings to software development.
Hexagonal Architecture was introduced by Alistair Cockburn in 2005. The primary goal of this architecture is to allow an application to be equally driven by users, programs, automated tests, or batch scripts, and to be developed and tested in isolation from its eventual runtime devices and databases.
Hexagonal Architecture is built around the concepts of Ports and Adapters. Let’s explore these concepts in detail:
Ports define the entry points to the application. They are interfaces that describe the operations the application can perform. Ports can be categorized into two types:
Adapters are the implementations of the ports. They translate the data and operations from the external systems to the application’s core logic and vice versa. Adapters can also be categorized into two types:
To implement Hexagonal Architecture in Ruby, we need to structure our application to separate the core business logic from the external systems. Here’s a step-by-step guide on how to achieve this:
The core domain contains the business logic and is independent of external systems. It includes:
1# app/domain/entities/order.rb
2class Order
3 attr_reader :id, :items, :status
4
5 def initialize(id, items)
6 @id = id
7 @items = items
8 @status = :pending
9 end
10
11 def complete
12 @status = :completed
13 end
14end
15
16# app/domain/value_objects/item.rb
17class Item
18 attr_reader :name, :price
19
20 def initialize(name, price)
21 @name = name
22 @price = price
23 end
24end
Ports are interfaces that define the operations the application can perform. In Ruby, we can use modules to define these interfaces.
1# app/ports/inbound/order_service.rb
2module OrderService
3 def create_order(order)
4 raise NotImplementedError
5 end
6
7 def complete_order(order_id)
8 raise NotImplementedError
9 end
10end
11
12# app/ports/outbound/payment_gateway.rb
13module PaymentGateway
14 def process_payment(order)
15 raise NotImplementedError
16 end
17end
Adapters implement the ports and handle the interaction with external systems.
1# app/adapters/inbound/web/order_controller.rb
2class OrderController
3 include OrderService
4
5 def create_order(order)
6 # Handle HTTP request and call the core logic
7 order = Order.new(order[:id], order[:items])
8 # Call the core domain logic
9 order.complete
10 end
11
12 def complete_order(order_id)
13 # Handle HTTP request and call the core logic
14 # Find order and complete it
15 end
16end
17
18# app/adapters/outbound/payment_gateway_adapter.rb
19class PaymentGatewayAdapter
20 include PaymentGateway
21
22 def process_payment(order)
23 # Interact with an external payment service
24 puts "Processing payment for order #{order.id}"
25 end
26end
Hexagonal Architecture offers several benefits that make it an attractive choice for building scalable and maintainable applications:
While Hexagonal Architecture offers many benefits, there are also challenges to consider:
To better understand Hexagonal Architecture, let’s visualize the structure using a Mermaid.js diagram:
graph TD;
A["Inbound Adapter"] -->|Calls| B["Inbound Port"]
B -->|Executes| C["Core Domain"]
C -->|Uses| D["Outbound Port"]
D -->|Interacts with| E["Outbound Adapter"]
E -->|Communicates with| F["External System"]
Diagram Description: This diagram illustrates the flow of data and operations in a Hexagonal Architecture. The Inbound Adapter receives input and calls the Inbound Port, which executes the Core Domain logic. The Core Domain uses the Outbound Port to interact with the Outbound Adapter, which communicates with the External System.
To get hands-on experience with Hexagonal Architecture, try modifying the code examples provided:
Order entity, such as a method to cancel an order.Before moving on, let’s summarize the key takeaways:
Remember, this is just the beginning. As you progress, you’ll build more complex and interactive applications. Keep experimenting, stay curious, and enjoy the journey!