Sunday, June 28, 2020

Hexagonal Architecture in Java

Hexagonal Architecture, also known as Ports and Adapters architecture, is a method for designing software applications. This approach enhances an application's maintainability and allows for quicker architectural modifications.

Despite the "Hexagonal" term in the name, there's no direct relation to a hexagon shape. Let's think about it in a way that there are just three different layers - application, domain, and infrastructure.

Domain


The domain layer houses the business logic exclusively. Unaware of any underlying technology, it communicates with the external world through interfaces - Ports.

Ports


There are driving and driven ports. Driving ports enable the application layer to interact with the domain layer, while driven ports allow the domain layer to obtain information from the external world - the infrastructure layer.

Adapters


Adapters are concrete implementations of ports. They can be driving and driven, based on the port type they implement. Adapters are easily replaceable, allowing for changes in surrounding components' implementation without affecting the internal logic stored in the domain layer.

Java Example


1. Driving Port


First, the driving port - ExchangeRatesPort - is defined, containing two methods representing particular steps:

public interface ExchangeRatesPort {
   List<String> loadAvailableCurrencies();
   double getCurrencyExchangeRatesForDate(String currencyCode, Date date);
}

2. Driven Port


Next, the driven port representing an exchange rate source for the domain layer is defined:

public interface ExchangeRateInfrastructurePort {
   List<ExchangeRate> loadExchangeRates();
}

3. Domain Layer


ExchangeRatesService represents the domain layer code in the example, using the ExchangeRateInfrastructurePort driven port to access needed information:

public class ExchangeRatesService {
   private ExchangeRatesInfrastructurePort exchangeRatesInfrastructurePort;

// business logic

}

4. Application Layer


The service is then used in the application, a simple console application working with command-line arguments. It's going to access the service through the driving port interface we defined already:

public class ExchangeRatesApplication {
   private ExchangeRatesPort exchangeRatesPort;

// application layer code

}

5. Driving Adapter


But we don't have a concrete implementation of the port yet, so we need to define it - ExchangeRatesApplicationAdapter - to interact directly with the domain service:

public class ExchangeRatesAdapter implements ExchangeRatesPort {
   private ExchangeRatesService exchangeRatesService;

// all code handling translating requests and handling interaction with the domain layer is here

}

6. Driven Adapter


Lastly, the exchange rates service is given access to exchange rates by implementing the driven port with the MonthlyExchangeRateInfrastructureAdapter class, which loads the required information from a file.

public class MonthlyExchangeRateInfrastructureAdapter implements ExchangeRateInfrastructurePort {

// driven port implementation

}

All Together


Connected, the system permits port replacement by providing different implementations. For instance, a different exchange rate source (database, REST API) can be used by providing a different adapter implementing ExchangeRateInfrastructurePort.  The example application code is available on GitHub.

Hexagonal architecture implementation varies, but if applied thoughtfully, it can lead to faster adaptation to changing requirements and infrastructure for long-lasting applications. It also simplifies business logic testing with unit tests and enables more parallel work on independent components.

No comments:

Post a Comment