Hexagonal Architecture with Spring Boot
Part 1 — Why Hexagonal Architecture?
The vast majority of Spring Boot projects are built around the Controller → Service → Repository trio. This structure is sufficient for many scenarios. However, as a product grows and business rules become more complex, the limitations of this structure become apparent. In this article, we will examine why some teams turn to Hexagonal Architecture — not as a theoretical framework, but through the concrete problems encountered in day-to-day development.
In a typical Spring project, OrderService contains @Transactional, OrderRepository extends JpaRepository, and the entity class combines both @Entity annotations and business rules. In this setup, a single class takes on three different responsibilities: representing a database table, conforming to JSON serialization, and carrying domain logic.
While this structure works well in the short term, it eventually leads to the following situations:
The core problem is clear: business logic is tightly bound to the framework and the database. The domain layer is expected to have no knowledge of JPA or Spring; in the current structure, this separation cannot be achieved.
The central proposal of Hexagonal Architecture is a single idea: business logic is placed at the center, and every connection to the outside world is placed behind an interface.
The application consists of two regions. The inside contains the domain and application layers; these layers are written in plain Java or Kotlin and contain no frameworks or annotations. The outside contains the adapters: REST controllers, JPA repositories, Kafka consumers, external service clients.
The two regions communicate through interfaces called ports:
The critical rule of the model is the direction of dependencies: all dependencies flow from the outside inward. The controller depends on the use case interface; the use case calls the outbound port but does not know its implementation. The JPA adapter implements this port. As a result, the domain depends on no technology.
Recent developments in the Spring ecosystem have significantly increased the value of the Hexagonal approach.
Microservices and modular monoliths. If a service contains more than one bounded context, each context can have its own domain model. Maintaining this separation with code tightly coupled to a framework is difficult; with the port-adapter separation, boundaries are drawn much more clearly.
Event-driven architectures. With the widespread adoption of technologies such as Kafka, RabbitMQ, and SQS, services now typically include both a REST interface and at least one message consumer. When the same business logic needs to be triggered from multiple entry points, code duplication becomes inevitable without a use case layer.
Testability and fast feedback. In cloud-native environments, pipeline speed is a critical productivity metric. Being able to run domain tests in milliseconds without starting a Spring context noticeably accelerates the development loop.
Resilience to technology changes. Decisions such as moving from JPA to jOOQ, from REST to gRPC, or from a single database to a CQRS structure are on the agenda of many teams. When the domain is isolated, these transitions can be completed with a limited intervention; otherwise, a significant portion of the code needs to be rewritten.
Observability and cross-cutting concerns. Horizontal concerns such as tracing, metric collection, and structured logging should be placed at the adapter level. The Hexagonal approach defines a clear location for such functionality: the inner layers are left untouched, and these concerns are placed at the boundaries.
No. For a CRUD service consisting of three endpoints, a short-lived prototype, or a small-scale internal tool, the additional cost introduced by this approach usually does not pay off. Additional interfaces, extra mapping layers, and a broader package structure increase the initial cost.
Hexagonal Architecture proves its value in systems where business rules are rich and long-term development is expected. Payments, orders, subscription management, pricing engines, and rule-based systems are the examples in which the return on this approach materializes fastest. From the point where the domain itself begins to require more attention than the framework, the investment starts to amortize itself.
In this part, we examined the practical value of Hexagonal Architecture. The next part will move to concrete steps: the concepts of port and adapter will be clearly defined, the example domain to be used throughout the series will be selected, and the package skeleton of the Spring Boot project will be established.
Spring Boot ile Hexagonal Mimari
Bölüm 1 — Neden Hexagonal Mimari?
Spring Boot ile geliştirilen projelerin büyük çoğunluğu Controller → Service → Repository üçlüsü üzerine kuruludur. Bu yapı pek çok senaryo için yeterlidir. Ancak ürün büyüdükçe ve iş kuralları karmaşıklaştıkça yapının sınırları belirgin hale gelir. Bu yazıda, bazı ekiplerin neden Hexagonal Mimari’ye yöneldiğini teorik bir çerçeve olarak değil, günlük geliştirme sürecinde karşılaşılan somut sorunlar üzerinden ele alacağız.
Tipik bir Spring projesinde OrderService içinde @Transactional, OrderRepository extends JpaRepository ve entity sınıfında hem @Entity anotasyonları hem iş kuralları bir arada bulunur. Bu yapıda tek bir sınıf üç farklı sorumluluğu üstlenir: veritabanı tablosunu temsil etmek, JSON serileştirmesine uygun olmak ve domain mantığını taşımak.
Kısa vadede sorunsuz çalışan bu yapı, zamanla şu tür durumlara yol açar:
Temel sorun nettir: iş mantığı framework’e ve veritabanına sıkı biçimde bağlanmıştır. Domain katmanının JPA ya da Spring hakkında bilgi sahibi olmaması beklenir; mevcut yapıda bu ayrım sağlanamaz.
Hexagonal Mimari’nin merkezi önerisi tektir: iş mantığı ortaya alınır ve dış dünyayla olan her bağlantı bir arayüzün arkasına yerleştirilir.
Uygulama iki bölgeden oluşur. İçeride domain ve application katmanları bulunur; bu katmanlar saf Java ya da Kotlin ile yazılır, herhangi bir framework veya anotasyon içermez. Dışarıda ise adapter’lar yer alır: REST controller, JPA repository, Kafka consumer, harici servis istemcileri.
İki bölge, port adı verilen arayüzler üzerinden iletişim kurar:
Modelin kritik kuralı bağımlılık yönüdür: tüm bağımlılıklar dışarıdan içeriye doğru akar. Controller use case arayüzüne bağımlıdır; use case outbound port’u çağırır ancak port’un implementasyonunu tanımaz. JPA adapter bu port’u implemente eder. Sonuç olarak domain hiçbir teknolojiye bağımlı değildir.
Spring ekosistemindeki son dönem gelişmeler Hexagonal yaklaşımın değerini belirgin biçimde artırmıştır.
Mikroservisler ve modüler monolit. Bir servis içinde birden fazla bounded context varsa, her context’in kendi domain modeli bulunabilir. Framework’e sıkı bağlı bir kodla bu ayrımı korumak zordur; port-adapter ayrımıyla sınırlar çok daha net çizilir.
Event-driven mimariler. Kafka, RabbitMQ ve SQS gibi teknolojilerin yaygınlaşmasıyla servisler artık çoğunlukla hem REST hem de en az bir mesaj tüketicisine sahiptir. Aynı iş mantığının birden fazla giriş noktasından tetiklenmesi gerektiğinde, use case katmanı olmadan kod tekrarı kaçınılmaz hale gelir.
Test edilebilirlik ve hızlı geri bildirim. Cloud-native ortamlarda pipeline hızı kritik bir verimlilik ölçütüdür. Domain testlerinin Spring context’i olmadan milisaniyeler içinde çalıştırılabilmesi, geliştirme döngüsünü belirgin biçimde hızlandırır.
Teknoloji değişimine dayanıklılık. JPA’dan jOOQ’ya, REST’ten gRPC’ye veya tek veritabanından CQRS yapısına geçiş gibi kararlar pek çok ekibin gündemindedir. Domain izole edildiğinde bu geçişler sınırlı bir müdahaleyle tamamlanır; aksi halde kodun önemli bir kısmının yeniden yazılması gerekir.
Observability ve cross-cutting concerns. Tracing, metrik toplama ve yapılandırılmış loglama gibi yatay konular adapter seviyesinde konumlandırılmalıdır. Hexagonal yaklaşım bu tür işlevler için net bir yer tanımlar: iç katmanlara dokunulmaz, bu işlevler sınırlara yerleştirilir.
Hayır. Üç endpoint’ten oluşan bir CRUD servisi, kısa ömürlü bir prototip veya küçük ölçekli bir internal tool için bu yaklaşımın getirdiği ek maliyet çoğunlukla karşılık bulmaz. Ek arayüzler, ek mapping katmanları ve daha geniş paket yapısı başlangıç maliyetini artırır.
Hexagonal Mimari, iş kurallarının zenginleştiği ve uzun süreli geliştirilmesi beklenen sistemlerde karşılığını verir. Ödeme, sipariş, abonelik yönetimi, fiyatlandırma motoru ve kural tabanlı sistemler bu yaklaşımın getirisini en hızlı gösteren örneklerdir. Domain’in kendisi framework’ten daha fazla dikkat gerektirmeye başladığı andan itibaren yatırım kendini amorti etmeye başlar.
Bu bölümde Hexagonal Mimari’nin pratik değerini ele aldık. Bir sonraki bölümde somut adımlara geçilecektir: port ve adapter kavramları net biçimde tanımlanacak, seri boyunca kullanılacak örnek domain seçilecek ve Spring Boot projesinin paket iskeleti oluşturulacaktır.
This article was prepared by İlknur Bişirci.