Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 1차원 DP
- 2차원 dp
- 99클럽
- @BeforeAll
- @BeforeEach
- @Builder
- @Entity
- @GeneratedValue
- @GenericGenerator
- @NoargsConstructor
- @Query
- @Table
- @Transactional
- Actions
- Amazon EFS
- amazon fsx
- Android Studio
- ANSI SQL
- ApplicationEvent
- assertThat
- async/await
- AVG
- AWS
- Azure
- bind
- builder
- button
- c++
- c++ builder
- c03
Archives
- Today
- Total
기록
[JpaTest] Layered Architecture vs Hexagonal Architecture 본문
시작하면서
소프트웨어 아키텍처는 애플리케이션의 확장성과 유지보수성을 결정짓는 핵심 요소로, 설계 선택에 따라 개발 과정과 결과물에 큰 영향을 미칩니다. Layered Architecture(계층형 아키텍처)와 Hexagonal Architecture(헥사고날 아키텍처)는 서로 다른 설계 철학을 기반으로 한 대표적인 아키텍처 방식입니다. 이 글에서는 두 아키텍처의 이론적 기반과 실무적 차이를 비교하고, Kafka와 MySQL을 활용한 현실적인 예제를 통해 그 특성을 심도 있게 분석합니다.
Layered Architecture (계층형 아키텍처)
Layered Architecture는 소프트웨어를 여러 계층으로 나누어 설계하는 전통적이고 직관적인 방식입니다. 각 계층은 서로 독립적이면서도 특정한 역할을 수행하며, 일반적으로 다음과 같은 구성으로 이루어집니다:
- Presentation Layer: 사용자 인터페이스를 담당하며, 요청을 처리하고 결과를 반환합니다.
- Application Layer: 비즈니스 로직과 흐름을 제어하며, 도메인 계층과 상호작용합니다.
- Domain Layer (Business Logic Layer): 핵심 비즈니스 로직을 캡슐화합니다.
- Infrastructure Layer: 데이터베이스, 외부 API, 메시지 브로커와 같은 외부 시스템과의 통신을 처리합니다.
장점
- 명확한 계층 분리: 초보 개발자도 쉽게 이해할 수 있는 구조.
- 재사용성 증가: 특정 계층의 코드가 다른 프로젝트에서도 재사용 가능.
- 넓은 적용 범위: 다양한 프로젝트에서 검증된 표준적인 방식.
단점
- 테스트 어려움: 계층 간 강한 결합으로 인해 단위 테스트가 복잡해질 수 있음.
- 유연성 부족: 특정 계층의 변경이 다른 계층에 큰 영향을 줄 수 있음.
- 비즈니스 로직과 인프라 결합: 도메인 로직이 데이터 접근 방식에 의존하는 경향.
예제 : 이벤트 처리 (Kotlin + Spring Boot)
사용자 이벤트를 수신하고, 데이터를 MySQL에 저장합니다.
@RestController
@RequestMapping("/users")
public class UserController {
private final UserService userService;
public UserController(UserService userService) {
this.userService = userService;
}
@PostMapping("/events")
public ResponseEntity<Void> handleUserEvent(@RequestBody UserEventDto userEventDto) {
userService.processUserEvent(userEventDto);
return ResponseEntity.ok().build();
}
}
@Service
public class UserService {
private final UserRepository userRepository;
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
public void processUserEvent(UserEventDto userEventDto) {
User user = new User(userEventDto.getId(), userEventDto.getName(), userEventDto.getEmail());
userRepository.save(user);
}
}
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
}
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
}
public class UserEventDto {
private Long id;
private String name;
private String email;
}
구조도
Presentation Layer -> Application Layer -> Domain Layer -> Infrastructure Layer
(UserController) (UserService) (User) (UserRepository)
Hexagonal Architecture (헥사고날 아키텍처)
Hexagonal Architecture는 애플리케이션의 비즈니스 로직(Core)을 외부 의존성으로부터 완전히 분리하는 것을 목표로 합니다. 이를 위해 포트와 어댑터 개념을 사용합니다:
- 포트: 코어와 외부 시스템 간의 인터페이스로, 코어가 외부와 상호작용하는 방법을 정의합니다.
- 어댑터: 포트를 구현하여 외부 시스템(예: 데이터베이스, 메시지 브로커, 사용자 인터페이스 등)과 실제로 연결되는 역할을 수행합니다.
장점
- 테스트 용이성: 외부 시스템과의 의존성이 분리되어 단위 테스트가 간단해짐.
- 확장성과 유연성: 새로운 어댑터를 추가하거나 기존 의존성을 교체하기 쉽습니다.
- 비즈니스 로직의 독립성: 핵심 로직이 외부 기술에 의존하지 않음.
단점
- 복잡성 증가: 초기 설계와 구현이 Layered Architecture보다 어려움.
- 작은 프로젝트에 부적합: 단순한 요구사항에는 과도한 설계로 보일 수 있음.
예제: 이벤트 처리 (Kotlin + Spring Boot)
사용자 이벤트를 수신하고, 데이터를 MySQL에 저장합니다.
// Core
interface UserService {
fun processUserEvent(userEventDto: UserEventDto)
}
class UserServiceImpl(private val userRepository: UserRepository) : UserService {
override fun processUserEvent(userEventDto: UserEventDto) {
val user = User(userEventDto.id, userEventDto.name, userEventDto.email)
userRepository.save(user)
}
}
// Ports
interface UserRepository {
fun save(user: User)
}
// Adapters
@Repository
class JpaUserRepository(private val jpaRepository: SpringDataJpaUserRepository) : UserRepository {
override fun save(user: User) {
jpaRepository.save(user)
}
}
interface SpringDataJpaUserRepository : JpaRepository<User, Long>
@Component
class KafkaUserEventConsumer(private val userService: UserService) {
@KafkaListener(topics = ["user-events"], groupId = "user-service-group")
fun consume(userEventDto: UserEventDto) {
userService.processUserEvent(userEventDto)
}
}
@Entity
data class User(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long,
val name: String,
val email: String
)
data class UserEventDto(
val id: Long,
val name: String,
val email: String
)
구조도
Adapters (Kafka Consumer, Repository)
| ^
v |
Ports (UserService, UserRepository)
|
v
Core (Business Logic)
Layered vs Hexagonal Architecture 비교
항목Layered ArchitectureHexagonal Architecture
구조 | 수직적 계층 구조 | 포트와 어댑터를 통한 수평적 구조 |
비즈니스 로직 | 특정 계층(주로 도메인 계층)에 포함 | 애플리케이션의 중심(core)에 독립적으로 위치 |
테스트 용이성 | 외부 의존성 때문에 테스트가 어려울 수 있음 | 외부 의존성과 분리되어 테스트가 용이 |
유연성 | 특정 계층 간 강한 결합 가능성 | 외부 의존성 변경에 대한 높은 유연성 |
학습 곡선 | 비교적 쉬움 | 비교적 어려움 |
결론
Layered Architecture는 단순성과 접근성 덕분에 소규모 애플리케이션이나 빠른 개발이 필요한 프로젝트에서 적합합니다. 반면, Hexagonal Architecture는 복잡한 시스템에서 확장성과 유지보수성을 극대화할 수 있는 설계 방식입니다.
'교육 > 강의' 카테고리의 다른 글
[TDD] Test Driven Development, Red-Green-Refactor (0) | 2025.01.23 |
---|---|
[단위테스트] 테스트하기 어려운 영역 분리: 현재 시간 의존성 해결 (0) | 2025.01.22 |
[단위테스트] 해피 케이스와 예외 케이스 (0) | 2025.01.21 |
[단위테스트] 수동 테스트 vs 자동화 테스트 : Junit5, AssertJ (0) | 2025.01.20 |
Practical Testing: 시작하면서 (0) | 2025.01.19 |
Comments