Hexagonal Architecture with Spring Boot

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.

Where does the classic structure fall short?

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:

  • A field name change affects the DB migration, the API contract, and unit tests simultaneously.
  • When an existing endpoint also needs to be triggered via Kafka, the same service method must be called from a consumer as well, and transaction boundaries become blurred.
  • Validating a simple business rule requires starting up the Spring context. Tests running with @SpringBootTest lengthen both the local development loop and the CI duration.
  • Inbound port (driving): The interface defined for the outside world to issue commands to the application. For example, CreateOrderUseCase.
  • Outbound port (driven): The interface that defines the functionality the application needs from the outside. For example, LoadOrderPort, PublishOrderEventPort.

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 core proposal of Hexagonal Architecture

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.

Benefits in modern Spring Boot projects

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.

Should it be applied in every project?

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.

The next part

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.

Klasik yapı nerede yetersiz kalır?

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:

  • Bir alan adının değiştirilmesi; DB migration, API kontratı ve birim testlerini aynı anda etkiler.
  • Mevcut bir endpoint’in Kafka üzerinden de tetiklenmesi gerektiğinde, aynı servis metodunun consumer’dan çağrılması kaçınılmaz hale gelir ve transaction sınırları belirsizleşir.
  • Basit bir iş kuralının doğrulanması için Spring context’inin ayağa kaldırılması gerekir. @SpringBootTest ile çalışan testler hem yerel geliştirme döngüsünü hem CI süresini uzatır.
  • Inbound port (driving): Dış dünyanın uygulamaya komut vermesi için tanımlanan arayüz. Örneğin CreateOrderUseCase.
  • Outbound port (driven): Uygulamanın dış dünyadan ihtiyaç duyduğu işlevleri tanımlayan arayüz. Örneğin LoadOrderPort, PublishOrderEventPort.

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 temel önerisi

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.

Modern Spring Boot projelerinde sağladığı faydalar

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.

Her projede uygulanmalı mı?

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.

Sonraki bölüm

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. 

Post Your Comment