Structure of Hexagonal Architecture
A typical Hexagonal Architecture has three main layers:
1️⃣ Core Domain (Application Logic)
- This contains the business rules and domain models.
- It is completely independent of external technologies.
- Example: If you’re building a banking system, this part would include logic for transactions, withdrawals, and deposits.
2️⃣ Ports (Interfaces)
- These are interfaces that define how the core interacts with external components.
- Two types of ports:
- Inbound Ports (driven by external inputs like APIs, UI, or events)
- Outbound Ports (used to interact with external services like databases, messaging systems, etc.)
3️⃣ Adapters (Implementation of Ports)
- These are concrete implementations of the ports, responsible for connecting the core with external systems.
- Examples:
- UI Adapter (REST API, CLI, Web UI)
- Persistence Adapter (Database, File System)
- External Service Adapter (3rd-party API, Payment Gateway)
🟢 Step 1: Define the Core Business Logic (Domain)
This code represents a simple BankAccount class in Java:
public class BankAccount { private String accountId; private double balance; public BankAccount(String accountId, double balance) { this.accountId = accountId; this.balance = balance; } public void deposit(double amount) { this.balance += amount; } public void withdraw(double amount) throws Exception { if (amount > balance) throw new Exception("Insufficient funds"); this.balance -= amount; } public double getBalance() { return balance; } }
2️⃣ Ports (Interfaces)
Step 2 : These define how the core communicates with external systems.
🟢 Inbound Port (Application Use Cases)
- This defines the API for external clients (controllers, CLI, etc.).
- Instead of calling
BankAccountService
, we rename it to BankAccountUseCase
.
BankAccountService
, we rename it to BankAccountUseCase
.// Inbound Port
public interface BankAccountUseCase { void deposit(String accountId, double amount); void withdraw(String accountId, double amount) throws Exception; double getBalance(String accountId); }
// Outbound Port (Interface for external database communication) public interface BankAccountPersistencePort { BankAccount findById(String accountId); void save(BankAccount account); }
🟠Step 3: Implement the Application Service (Core Business Logic)
@Service
public class BankAccountServiceImpl implements BankAccountService { private final BankAccountRepository repository; public BankAccountServiceImpl(BankAccountRepository repository) { this.repository = repository; } @Override public void deposit(String accountId, double amount) { BankAccount account = repository.findById(accountId); account.deposit(amount); repository.save(account); } @Override public void withdraw(String accountId, double amount) throws Exception { BankAccount account = repository.findById(accountId); account.withdraw(amount); repository.save(account); } @Override public double getBalance(String accountId) { return repository.findById(accountId).getBalance(); } }
🟡 Step 3: Define Ports
🔵 Step 4: Implement Adapters (Database & REST API)
Adapter 1: Database (Persistence Adapter)
@Repository public class BankAccountJpaAdapter implements BankAccountPersistencePort {
private final JpaRepository<BankAccount, String> jpaRepository; public BankAccountJpaAdapter(JpaRepository<BankAccount, String> jpaRepository) {
this.jpaRepository = jpaRepository; } @Override public BankAccount findById(String accountId) { return jpaRepository.findById(accountId).orElseThrow(); } @Override public void save(BankAccount account) { jpaRepository.save(account); } }
Advantages of Hexagonal Architecture
✔ Separation of concerns – Business logic is isolated from external dependencies.
✔ Improves testability – Core logic can be tested without DB/API dependencies.
✔ Easier to replace technologies – You can switch databases, frameworks, or APIs without changing core logic.
✔ Supports multiple interfaces – Can be extended with CLI, Web UI, REST API, etc.
✔ Better scalability – Enables independent scaling of different parts of the application.
When to Use Hexagonal Architecture?
✅ When your application needs multiple interfaces (e.g., REST API + CLI + Web UI).
✅ If you plan to switch technologies (e.g., change from SQL to NoSQL).
✅ When maintainability and testability are important.
❌ Not needed for simple applications with no external dependencies.
How does Hexagonal Architecture differ from traditional layered architecture?
Answer: Traditional layered architecture separates concerns into layers like presentation, business logic, and data access. Hexagonal Architecture focuses on separating core business logic from external systems through ports and adapters, providing greater flexibility and decoupling.
How do you test applications built with Hexagonal Architecture?
Answer: Testing applications involves:
- Unit testing the core business logic by mocking the ports.
- Integration testing the adapters.
- End-to-end testing to ensure the entire system works together.
Comments
Post a Comment