Design Pattern

A Practical Example of Hexagonal Architecture in Java

Hexagonal architecture is an application design pattern. It solves some problems of the layered architecture by introducing ports-and-adapter for the dependencies between our components of the application toward our domain objects. The domain objects are the core part of the application and it is the part of inside a hexagon. And other parts such as web interface, DB, messaging systems, etc are outside of a hexagon.

Hexagonal Architecture Diagram

Let’s discuss in details each of the stereotypes in this architecture style.

Core application part

Domain Objects are the core parts of an application. These have business rules and validations and also have state and behaviour. This core application part doesn’t have any outward dependency. These are pure core business logic services. Domain objects will be changed when the business requirement will be changed otherwise they never affect the changes in other layers.

Let’s see the following domain class Account of the core application, it has account-related information and business validations.

public class Account{
	
	private String accountHolderName;
	private Long accountNumber;
	private String bankName;
	
	// Constructors , Getters/Setters etc.
}

Inbound and outbound ports

In the Hexagonal architecture pattern, the ports provide the flow to the application from outside and inside.

Inbound ports

An inbound port provides the flow and the application functionality to the outside. An inbound port is a service interface that exposes the core logic and can be called by outside components. You can see the following example of an inbound port:

public interface AccountService { 
	void createAccount(Account account); 
	Account getAccount(Long accountNumber); 
	List<Account> allAccounts(); 
}

Outbound ports

An outbound port provides the outside functionality or interface. The core application calls this output port as per requirement such as external database call etc. For example, a simple repository interface AccountRepository that provides a port to enable communication from the core application to a database. This simple repository interface is an outbound port. Let’s see the following example of an outbound port:

public interface AccountRepository { 
     void createAccount(Account account); 
     Account getAccount(Long accountNumber); 
     List allAccounts();
 }

Adapters

Adapters are nothing but these are the implementation of inbound and outbound ports. The adapters from the outside of the hexagonal architecture and they are not part of the core application. They only interact with the core application from outside by using inbound and outbound ports.

Input adapters

The input adapters are also known as primary or driving adapters. These drive the application by invoking actions on the application using the inbound ports of application.

For example, the AccountController provides REST APIs or web interfaces as the input adapters. The REST controllers use the service interfaces (inbound ports) to interact with the core part of the business logic of the application.

@RestController 
@RequestMapping("/account") 
public class AccountController{ 
	@Autowired private AccountService accountService; 
	
	@PostMapping 
	public void createAccount(@RequestBody Account account) { 
		accountService.createAccount(account); 
	} 
	
	@GetMapping("/{accountNumber}") 
	public Account getAccount(@PathVariable Long accountNumber) { 
		return accountService.getAccount(accountNumber);		
	} 
	
	@GetMapping 
	public List<Account> allAccounts() { 
		return accountService.allAccounts(); 
	} 
}

Output adapters

The output adapters are also known as secondary or driven adapters. These are the implementation of the outbound ports. These are driven by the core application using the outbound ports to find the connections to the database and external APIs.

For example, the AccountRepositoryImpl provides an interface to the core application to communicate to external dependency such as the database.

@Repository 
public class AccountRepositoryImpl implements AccountRepository { 
	
	private Map<Long, Account> accountDB = new HashMap<Long, Account>(); 
	
	@Override 
	public void createAccount(Account account) { 
		accountDB.put(account.getAccountNumber(), account); 
	} 
	
	@Override 
	public Account getAccount(Long accountNumber) { 
		return accountDB.get(accountNumber); 
	} 
	
	@Override 
	public List<Account> allAccounts() { 
		return accountDB.values().stream().collect(Collectors.toList()); 
	} 
}

Use cases of a core application

In the Hexagonal architecture, the use cases and business domain objects are inside of the hexagonal. The use cases are nothing but it is specific use case implementation of the inbound port to communication from the core to the downstream system. Let’s see the following use case implementation AccountServiceImpl provides a use case for a specific requirement:

@Service 
public class AccountServiceImpl implements AccountService { 

	@Autowired 
	private AccountRepository accountRepository; 

	@Override 
	public void createAccount(Account account) { 
		accountRepository.createAccount(account); 
	} 
	
	@Override 
	public Account getAccount(Long accountNumber) { 
		return accountRepository.getAccount(accountNumber); 
	} 
	
	@Override 
	public List<Account> allAccounts() { 
		return accountRepository.allAccounts(); 
	} 
}

Conclusion

In the article, we have discussed the Hexagonal application architecture with a quick example in Java. This architecture focuses to simplify application design with external and internal dependencies.

Previous
Dinesh Rajput

Dinesh Rajput is the chief editor of a website Dineshonjava, a technical blog dedicated to the Spring and Java technologies. It has a series of articles related to Java technologies. Dinesh has been a Spring enthusiast since 2008 and is a Pivotal Certified Spring Professional, an author of a book Spring 5 Design Pattern, and a blogger. He has more than 10 years of experience with different aspects of Spring and Java design and development. His core expertise lies in the latest version of Spring Framework, Spring Boot, Spring Security, creating REST APIs, Microservice Architecture, Reactive Pattern, Spring AOP, Design Patterns, Struts, Hibernate, Web Services, Spring Batch, Cassandra, MongoDB, and Web Application Design and Architecture. He is currently working as a technology manager at a leading product and web development company. He worked as a developer and tech lead at the Bennett, Coleman & Co. Ltd and was the first developer in his previous company, Paytm. Dinesh is passionate about the latest Java technologies and loves to write technical blogs related to it. He is a very active member of the Java and Spring community on different forums. When it comes to the Spring Framework and Java, Dinesh tops the list!

Share
Published by
Dinesh Rajput

Recent Posts

Strategy Design Patterns using Lambda

Strategy Design Patterns We can easily create a strategy design pattern using lambda. To implement…

2 years ago

Decorator Pattern using Lambda

Decorator Pattern A decorator pattern allows a user to add new functionality to an existing…

2 years ago

Delegating pattern using lambda

Delegating pattern In software engineering, the delegation pattern is an object-oriented design pattern that allows…

2 years ago

Spring Vs Django- Know The Difference Between The Two

Technology has emerged a lot in the last decade, and now we have artificial intelligence;…

2 years ago

TOP 20 MongoDB INTERVIEW QUESTIONS 2022

Managing a database is becoming increasingly complex now due to the vast amount of data…

2 years ago

Scheduler @Scheduled Annotation Spring Boot

Overview In this article, we will explore Spring Scheduler how we could use it by…

3 years ago