헥사고날 아키텍쳐

ksp7331·2024년 4월 3일

레이어드 아키텍쳐

개요

레이어드 아키텍처는 소프트웨어를 관심사에 따라 여러 레이어로 분할하여 구성하는 아키텍처 패턴이다.
레이어드 아키텍쳐는 다음과 같은 구성요소로 이루어져 있다.

Presentation Layer : 사용자 혹은 클라이언트 시스템과 직접적으로 연결되는 부분이다.
Business Layer : 비즈니스 로직을 구현하는 부분이다.
Persistence Layer : 데이터의 영구 저장과 관리를 담당하는 부분이다.

문제점

레이어드 아키텍쳐는 도메인에 대한 작업을 모두 비즈니스 레이어에서 처리하기 때문에 비즈니스 레이어가 비대해진다.
<도메인에 관한 작업을 비즈니스 로직에서 모두 처리>

@Service
@RequiredArgsConstructor
public class Service{
	private Repository repository;
    public Long join(String username){
    	// 도메인 생성이나 수정작업을 모두 비즈니스 레이어에서 수행
    	Member member = new Member(username);
        repository.save(member);
        return member.getId();        
    }
}

그리고 레이어 간의 의존성이 높아져 결합도가 높아지는 문제도 발생할 수 있다.
<Business Layer에서 JPA에 의존>

@Service
@RequiredArgsConstructor
public class Service{
	private Repository repository;
}

@Repository
@RequiredArgsConstructor
public class Repository{
	private EntityManager em;
    public void save(Member member){
    	em.persist(member);
    }
}

<Business Layer에서 Rest 방식으로 들어온 인스턴스 직접 사용>

@RestController
@RequiredArgsConstructor
public class Controller{
	private Service service;
    
    @PostMapping("/post")
    public ResponseEntity<Void> post(PostDto postDto){
    	service.save(postDto);
        return new ResponseEntity(HttpStatus.CREATED);
    }
}

@Service
@RequiredArgsConstructor
public class Service{
	private Repository repository;
    // Presentation Layer의 Dto를 사용, 또는 엔티티를 그대로 사용
    public Long save(PostDto postDto){
    }
}

헥사고날 아키텍쳐

해결방안

도메인 계층 분리

도메인에 대한 작업만 수행하는 도메인 계층을 만들어서 비즈니스 레이어의 규모를 줄일 수 있다.

@Service
@RequiredArgsConstructor
public class Service{
	private Repository repository;
    public Long join(String username){
    	
    	Member member = Member.from(username);
        repository.save(member);
        return member.getId();        
    }
}

public class Member{
	private Long id;
    private String username;
    // 도메인 클래스에서 도메인에 관한 작업 수행
    public static Member from(String username){
    	Member member = new Member();
        member.username = username;
        return member;
    }
}

포트-어댑터를 사용해서 계층 분리

포트는 어댑터와 도메인이 통신하기 위한 인터페이스이다. 어댑터는 외부세계와 실제로 상호작용하는 클래스들이다.

// use case
@Service
@RequiredArgsConstructor
public class Service{
	private Repository repository;
}

// persistence adapter와의 통신을 위한 port
public interface Repository{
	public void save(Member member);
}

// persistence adapter
@Repository
@RequiredArgsConstructor
public class RepositoryImpl implements Repository{
	private EntityManager em;
    @Override
    public void save(Member member){
    	em.persist(member);
    }
}
// web adapter
@RestController
@RequiredArgsConstructor
public class Controller{
	private Service service;
    
    @PostMapping("/post")
    public ResponseEntity<Void> post(PostDto postDto){
    	WebDto webDto = WebDto.from(postDto);
    	service.save(webDto);        
        return new ResponseEntity(HttpStatus.CREATED);
    }
}

// web adapter와의 통신을 위한 port
public interface Service{
	public Long save(WebDto webDto);
}

// use case
@Service
@RequiredArgsConstructor
public class ServiceImpl implements Service{
	private Repository repository;
    
    @Override
    public Long save(WebDto webDto){
    }
}

이 때 레이어드 아키텍쳐의 Presentation Layer나 Persistence Layer에 해당했던 부분은 모두 외부세계로 볼 수 있다. 헥사고날 아키텍쳐에서는 이 부분들을 Infrastructure Layer라고 한다.

특징

장점

  • 도메인 계층을 분리하기 때문에 도메인 비즈니스 모델에 집중할 수 있게 된다.
  • 포트와 어댑터를 사용함으로써 외부 기술 변화에 대응하기가 쉬워진다. 외부 기술의 변경이 필요할 때 어댑터만 변경하면 되기 때문이다.
  • 도메인과 use case를 외부로부터 분리함으로써 독립적으로, 외부 의존성 없이 테스트할 수 있다.

단점

  • 계층이 많아지면서 오버헤드가 발생할 수 있다.
  • ORM을 사용할 경우 도메인을 엔티티로 전환해야 하는데, 이를 위한 코드가 많이 필요해져서 유지보수가 힘들어질 수 있다.

0개의 댓글