Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Hexagonal Architecture: Robust Software With In...

Hexagonal Architecture: Robust Software With Interfaces Instead of Layers

We all know this situation: the older and larger an application gets, the more complex and expensive it becomes to expand and maintain it. The widespread layered architecture is inadequate as a solution approach: direct and indirect dependencies of all layers on the database and other infrastructure components often lead to a blurring of the layer boundaries and an interweaving of technical and functional code.

Hexagonal architecture places the business logic at the center, and technical details are isolated as adapters behind interfaces (ports). Business and technical code can thus be developed and tested independently of each other.

Based on the objectives of a software architecture and a critical look at the layered architecture, we take a detailed look at the hexagonal architecture. You will learn how the dependency rule ensures that there are no dependencies between functional and technical code and how the application core can still access the infrastructure. Does the hexagonal architecture fulfill the goals of a software architecture? What challenges does it bring with it? How does it differ from onion and clean architecture, and what synergies arise in interaction with microservices and domain-driven design?

Equipped with new knowledge, you can increase the quality and lifespan of your software projects and react more quickly to new requirements in the future.

Sven Woltmann

April 07, 2024
Tweet

More Decks by Sven Woltmann

Other Decks in Programming

Transcript

  1. Copyright © 2025, HappyCoders.eu “The goal of a software architecture

    is to minimize the human resources required to build and maintain the required system.” “If that effort is low, and stays low throughout the lifetime of the system, the design is good.” What Is the Goal of Software Architecture?
  2. Copyright © 2025, HappyCoders.eu A good architecture makes it possible

    to change software during its lifetime with as little effort as possible. “To keep software soft.” — Robert C. Martin What Is the Goal of Software Architecture?
  3. Copyright © 2025, HappyCoders.eu Goals 1. 2. 3. Isolation from

    controlling infrastructure Isolation from controlled infrastructure Modernization of infrastructure without adapting the business logic Business logic @
  4. Copyright © 2025, HappyCoders.eu Adapters Core ? ? ? ?

    ? ? ? Model Business logic Port Port Port Port
  5. Copyright © 2025, HappyCoders.eu Adapters Core Model Business logic Persistence

    Port Sending Port Registration Port Web UI REST API JPA SMTP
  6. Copyright © 2025, HappyCoders.eu Registration Port Copyright © 2025, HappyCoders.eu

    name=Sven&email= sven@happycoders.eu Submit Web UI Adapter registerPort.registerUser( "Sven", "sven@happycoders.eu"); { "name": "Sven", "email": "… " } REST API Adapter Fill out
  7. Copyright © 2025, HappyCoders.eu Copyright © 2024, HappyCoders.eu Persistence Port

    JPA Adapter INSERT INTO User (name, email, registrationDate) VALUES ('Sven', 'sven@happycoders.eu', now()); persistencePort .saveUser(user); Copyright © 2025, HappyCoders.eu
  8. Copyright © 2025, HappyCoders.eu Adapters Core Model Business logic Persistence

    Port Sending Port Registration Port Web UI REST API JPA SMTP Primary / Driving Secondary / Driven
  9. Copyright © 2025, HappyCoders.eu RegisterUserService +registerUser() UserRestController +registerUser() RESTEasy JpaAdapter

    +saveUser() Hibernate Copyright © 2025, HappyCoders.eu Module “Application” Module “Controllers” «interface» RegisterUserPort +registerUser() Direction of call
  10. Copyright © 2025, HappyCoders.eu Copyright © 2025, HappyCoders.eu «interface» RegisterUserPort

    +registerUser() UserRestController +registerUser() RESTEasy Hibernate RegisterUserService +registerUser() JpaAdapter +saveUser() «interface» PersistencePort +saveUser() D irection of call D ependency
  11. Copyright © 2025, HappyCoders.eu Copyright © 2025, HappyCoders.eu «interface» RegisterUserPort

    +registerUser() UserRestController +registerUser() RESTEasy Hibernate RegisterUserService +registerUser() JpaAdapter +saveUser() «interface» PersistencePort +saveUser() D ependency
  12. Copyright © 2025, HappyCoders.eu Copyright © 2025, HappyCoders.eu «interface» RegisterUserPort

    +registerUser() UserRestController +registerUser() RESTEasy Hibernate RegisterUserService +registerUser() JpaAdapter +saveUser() «interface» PersistencePort +saveUser() “Dependency” “Rule” Dependency D ependency
  13. Copyright © 2025, HappyCoders.eu Adapters Core Port Port Port Web

    UI REST API JPA SMTP Dependency Dependency
  14. Copyright © 2025, HappyCoders.eu import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id;

    import java.time.LocalDateTime; @Entity public class User { @Id private Long id; @Column(nullable = false) private String name; @Column(nullable = false) private String email; @Column(nullable = false) private Instant registeredAt; // ... }
  15. Copyright © 2025, HappyCoders.eu Copyright © 2025, HappyCoders.eu «interface» RegisterUserPort

    +registerUser() UserRestController +registerUser() RESTEasy Hibernate RegisterUserService +registerUser() JpaAdapter +saveUser() «interface» PersistencePort +saveUser()
  16. Copyright © 2025, HappyCoders.eu Hibernate RegisterUserService +registerUser() JpaAdapter + saveUser()

    «interface» PersistencePort +saveUser() Copyright © 2025, HappyCoders.eu JpaUser User
  17. Copyright © 2025, HappyCoders.eu import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id;

    import java.time.Instant; @Entity public class JpaUser { @Id private Long id; @Column(nullable = false) private String name; @Column(nullable = false) private String email; @Column(nullable = false) private Instant registeredAt; // ... }
  18. Copyright © 2025, HappyCoders.eu import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Id;

    import java.time.Instant; @Entity public class JpaUser { @Id private Long id; @Column(nullable = false) private String name; @Column(nullable = false) private String email; @Column(nullable = false) private Instant registeredAt; // ... } import java.time.Instant; public class User { private Long id; private String name; private String email; private Instant registeredAt; public User(String name, String email) { verifyName(name); verifyEmail(email); this.name = name; this.email = email; this.registeredAt = Instant.now(); } // ... }
  19. Copyright © 2025, HappyCoders.eu Hibernate RegisterUserService +registerUser() JpaAdapter + saveUser()

    «interface» PersistencePort +saveUser() Copyright © 2025, HappyCoders.eu JpaUser User Mapping
  20. Copyright © 2025, HappyCoders.eu Hibernate RegisterUserService +registerUser() JpaAdapter + saveUser()

    «interface» PersistencePort +saveUser() Copyright © 2025, HappyCoders.eu JpaUser User Mapping + loadUser()
  21. Copyright © 2025, HappyCoders.eu public class JpaAdapter implements PersistencePort {

    @Override @Transactional public void saveUser(User user) { JpaUser jpaUser = mapper.toJpaUser(user); repository.save(jpaUser); } } @Override @Transactional public Optional<User> loadUser(long userId) { Optional<JpaUser> jpaUser = repository.findById(userId); return jpaUser.map(mapper::toUser); }
  22. Copyright © 2025, HappyCoders.eu public class JpaAdapter implements PersistencePort {

    @Override @Transactional public void saveUser(User user) { JpaUser jpaUser = mapper.toJpaUser(user); repository.save(jpaUser); } } @Override @Transactional public Optional<User> loadUser(long userId) { Optional<JpaUser> jpaUser = repository.findById(userId); return jpaUser.map(mapper::toUser); }
  23. Copyright © 2025, HappyCoders.eu Copyright © 2025, HappyCoders.eu «interface» RegisterUserPort

    +registerUser() UserRestController +registerUser() RESTEasy Hibernate RegisterUserService +registerUser() JpaAdapter +saveUser() «interface» PersistencePort +saveUser()
  24. Copyright © 2025, HappyCoders.eu import com.fasterxml.jackson.annotation.JsonFormat; import java.time.Instant; public class

    UserDto { private Long id; private String name; private String email; @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss'Z'") private Instant registeredAt; // ... }
  25. Copyright © 2025, HappyCoders.eu «interface» RegisterUserPort +registerUser() UserRestController + registerUser()

    RESTEasy RegisterUserService +registerUser() Copyright © 2025, HappyCoders.eu User UserDto Jackson Mapping
  26. Copyright © 2025, HappyCoders.eu «interface» RegisterUserPort +registerUser() UserRestController + registerUser()

    RESTEasy RegisterUserService +registerUser() Copyright © 2025, HappyCoders.eu User UserDto Jackson Mapping + getUser()
  27. Copyright © 2025, HappyCoders.eu Core (subject under test) Port Port

    Port Port Adapter test double Test Method calls ✓ ✓
  28. Copyright © 2025, HappyCoders.eu Test Method calls REST Adapter (subject

    under test) REST Assured HTTP Port test double Copyright © 2025, HappyCoders.eu ✓ ✓
  29. Copyright © 2025, HappyCoders.eu Adapters Web UI REST API JPA

    SMTP Core Port Port Port Dependency Dependency
  30. Copyright © 2025, HappyCoders.eu A good architecture makes it possible

    to change software during its lifetime with as little effort as possible. What Is the Goal of Software Architecture?
  31. Copyright © 2025, HappyCoders.eu Adapters Web UI REST API JPA

    SMTP Core Model Business logic Port Port Port
  32. Copyright © 2025, HappyCoders.eu Adapters Web UI REST API JPA

    SMTP Core Model Business logic Port Port Port
  33. Copyright © 2025, HappyCoders.eu Adapters Web UI REST API JPA

    SMTP Core Model Business logic Port Port Port
  34. Copyright © 2025, HappyCoders.eu Adapter Modules Persistenz Port Web UI

    REST API JPA Module “Application” Persistence Port Registration Port
  35. Copyright © 2025, HappyCoders.eu Core Port Port Port Port Test

    Test double Test REST Adapter REST Assured Port test double Test Test- containers database JPA Adapter
  36. Copyright © 2025, HappyCoders.eu Hexagonal Architecture ✔ Changeability ✔ Loose

    Coupling ✔ Independent Development ✔ Independent Testability