Episode ini membahas struktur aplikasi yang sehat: arsitektur clean, hexagonal, dan onion; struktur modul fitur dan package- by-feature; design patterns populer seperti service layer dan repository; serta event-driven architecture dengan Spring Events dan Kafka.

Aplikasi yang berumur panjang tidak mati karena teknologi — ia mati karena struktur yang buruk membuat setiap perubahan semakin sulit. Episode 18 membahas modular architecture dan design patterns yang menjaga aplikasi Spring Boot tetap bisa dirawat.
Kalian akan mempelajari arsitektur clean, hexagonal, dan onion; struktur modul berbasis fitur; design patterns yang umum dipakai Spring; serta event-driven architecture dengan Spring Events dan Kafka untuk memisahkan komponen secara lebih jauh.
Tiga arsitektur ini berbagi prinsip yang sama: logika bisnis berada di pusat, dan dependency selalu mengarah ke dalam — domain tidak bergantung pada detail teknis seperti framework atau database.
Domain / Use Case
-> Application Services
-> Adapters (controller, repository, external API)Clean Architecture memisahkan entities dan use cases dari controllers dan gateways. Hexagonal (Ports and Adapters) mengekspos domain melalui ports (interface) yang diimplementasi adapters (implementasi teknis). Onion Architecture adalah konsep serupa dengan lapisan berpusat pada domain.
Dalam Spring Boot, prinsipnya berarti: domain layer tidak boleh mengimpor Spring MVC atau JPA. Controller dan repository menjadi adapter yang berada di lapisan luar:
public interface ItemRepositoryPort {
Optional<Item> findById(Long id);
Item save(Item item);
}Domain hanya bergantung pada ItemRepositoryPort. Implementasi JPA — JpaItemRepositoryAdapter — memenuhi port tersebut dan bisa diganti tanpa mengubah domain. Inilah inti hexagonal architecture.
Daripada memecah package berdasarkan teknologi (controller, service, repository), kelompokkan berdasarkan fitur:
com.example/
├── item/
│ ├── ItemController.java
│ ├── ItemService.java
│ ├── ItemRepository.java
│ └── Item.java
├── order/
│ ├── OrderController.java
│ ├── OrderService.java
│ └── OrderRepository.java
└── shared/
└── ApiError.javaDengan struktur ini, semua file yang berkaitan dengan satu fitur berada dalam satu package — perubahan fitur hanya menyentuh satu area. Package-by-feature membuat aplikasi besar jauh lebih mudah dinavigasi daripada package-by-layer.
Untuk codebase yang sangat besar, pisahkan menjadi modul Maven/Gradle terpisah — misalnya domain, application, infrastructure, dan web. Detailnya akan dibahas di episode 19 tentang build automation.
@Service
@Transactional
public class ItemService {
private final ItemRepository repository;
public ItemService(ItemRepository repository) {
this.repository = repository;
}
public Item create(Item item) {
return repository.save(item);
}
}Kedua pattern ini sudah menjadi idiom Spring — kalian memakainya sejak episode 4 dan 6. Transaction di taruh di service layer karena di situlah batas unit kerja aplikasi.
@Primary bean atau proxy.JdbcTemplate dan RestTemplate adalah contoh Spring yang memakai pola ini.Spring sendiri sangat bergantung pada pola proxy: transaction, security, dan caching bekerja lewat proxy yang membungkus bean asli. Memahami ini membantu kalian menebak perilaku annotation seperti @Transactional.
Untuk memisahkan komponen di dalam satu aplikasi, gunakan Spring Events. Publisher melempar event tanpa tahu siapa konsumennya:
@Service
public class OrderService {
private final ApplicationEventPublisher publisher;
public OrderService(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public Order placeOrder(Order order) {
Order saved = repository.save(order);
publisher.publishEvent(new OrderPlacedEvent(saved));
return saved;
}
}Konsumen menangkap event dengan @EventListener:
@Component
public class EmailNotifier {
@EventListener
public void onOrderPlaced(OrderPlacedEvent event) {
// kirim email, tanpa bergantung pada OrderService
}
}OrderService tidak perlu tahu tentang EmailNotifier — komunikasi terbalik lewat event. Ini memecah ketergantungan langsung antar komponen. Untuk melihatnya bekerja, jalankan aplikasi dengan ./mvnw spring-boot:run lalu pemicu pembuatan order akan memunculkan log dari listener.
Spring Events berjalan dalam satu proses — cocok untuk decoupling internal. Untuk komunikasi antar service atau yang butuh reliability, gunakan message broker seperti Kafka atau RabbitMQ:
spring:
kafka:
bootstrap-servers: localhost:9092
consumer:
group-id: order-consumer
auto-offset-reset: earliestKafka menyediakan persistensi event, replay, dan skala horizontal — pola yang tepat untuk arsitektur microservices event-driven yang dibahas di episode 14.
Episode 18 membekali kalian dengan arsitektur dan pola yang menjaga aplikasi tetap sehat: clean, hexagonal, dan onion architecture; package-by-feature dan modul; design patterns service layer, repository, dan decorator; serta event-driven architecture dengan Spring Events dan Kafka.
Inti yang harus dibawa pulang:
Di episode 19 selanjutnya kita akan membahas tooling modern dan build automation — perbandingan Maven vs Gradle Kotlin DSL, Spring Boot plugin dan dependency management BOM, live reload dengan Spring DevTools, serta reproducible builds dan multi-module project.