8장 레이어드 아키텍처
- 레이어드 아키텍처는 구체적인 사양이 아님.
레이어드 아키텍처의 조건 (제약)
- 레이어 구조를 사용 (필수)
- 레이어 간 의존 방향은 단방향 (필수)
- 레이어 간 통신은 인접한 레이어에서만 이뤄지게 함 -> 작가는 필수는 아니라 생각함
아키텍처란? - 제약
- 아키텍트들이 아키텍처를 설명할 때 항상 빠지지 않고 말하는 한 가지 특징은 제약조건
- 아키텍처는 제약 조건을 이용해 개발자가 해도 되는 것과 하지 말아야 하는 것을 결정한다.
- 더 나아가, 해서는 안되는 일이 개발 단계에서 일어나지 않게 원천적으로 차단함.
제약 - 목적을 달성하기 위한 것
- 제약 조건은 목적에 따라 변경될 수 있음
잘못된 레이어드 아키텍처
JPA 엔티티 우선 접근 개발 방식 vs 엔드포인트 우선 접근 개발 방식
- JPA 먼저 떠올렸다는 것은 DDL을 고민했다는 것
-> 데이터 종속
-> 데이터 위주의 사고방식의 문제가 동일하게 생김
- 잘못된 레이어드 아키텍처는 레이어를 지나치게 추상화함
-> 인프라스트럭처 레이어를 먼저 만들면 어떤 문제가 있을까 ?
- API 엔드포인트 우선 접근
-> 스프링 종속
- 시스템을 도메인 요구사항 관점에서 봤다는 것에서 JPA 우선 방식보다는 낫다..
- 하지만 이런 방식은 프레임워크 종속적임
- API 엔드포인트를 고민하는 것은 도메인 요구사항이 무엇인지 파악하는데 도움을 주는 단계 뿐임
-> 요청 응답형식 먼저 개발하는 것도 데이터 위주의 생각을 할 가능성이 높다.
애플리케이션의 본질은 도메인이다
- 애플리케이션을 개발한다는 것은 도메인을 파악하고, 이에 따른 도메인 모델을 구성하고,
- -> 도메인 모델을 표현하는데 적합한 언어를 선택하고, 도메인 모델을 만들고, 도메인 기능을 제공할 기술을 선택한다는 것임.
진화하는 아키텍처 - 비즈니스 레이어 -> 어플리케이션 + 도메인 레이어
- 레이어드 아키텍처를 사용하면서도 객체지향을 챙길 수 있음
-> 시스템 개발의 첫 시작을 도메인으로 두기 !
- 도메인을 개발하려면 비즈니스 레이어 부터 개발하자
cf) 비즈니스 레이어 = 서비스 컴포넌트 + 도메인
-> 비즈니스 레이어 중에서도 도메인을 가장 먼저 개발해야함.
- 비즈니스 레이어안에 도메인이 존재해서 인지 모델에 혼란이 발생
-> 도메인 레이어를 분리하자 : 서비스 레이어 = 어플리케이션 레이어 + 도메인 레이어
- 프레젠테이션 -> 어플리케이션 -> 도메인 -> 인프라스트럭처 레이어
- 도메인 레이어는 순수 자바 코드로 작성 하자
-> 외부 라이브러리에 의존하지 않고 자유롭게 만들기 위함
- 애플리케이션 서비스는 단순하게 도메인에 있는 코드를 실행하는 역할은 함!!
-> '서비스는 J2EE패턴중 하나인 비즈니스 서비스 파사드처럼 사용될 수 있다'
-> 애플리케이션 레이어에 위치한 서비스 컴포넌트는 이제 도메인 레이어의 파사드가 됐다.
Account & AccountJpaEntity
- 순수한 도메인 모델과 영속성 객체를 사용하는 애플리케이션 서비스
public interface AccountRepository {
public Account findById(long id);
public void save(Account account);
}
@Builder
@RequiredArgsConstructor
public class Account {
public final Long id;
public final String email;
public final String nickname;
public Account withNickname(String nickname) { //불변객체수정
return Account.builder().is(this.id).email(this.email).nickname(nickname).build();
}
}
@Service
@RequiredArgsConstructor
public class AccountService {
private final AccountRepository accountRepository;
/**
* 이렇게 되면 서비스 레이어에서 JPA 기능을 사용하지 않는 것..
*/
@Transactional
public Account updateNicknameById(long id, String nickname) {
Account account = accountRepository.findById(id);
account = account.withNickname(nickname);
accountRepository.save(account);
}
}
@Repository
@RequiredArgsConstructor
public class AccountRepositoryImpl implements AccountRepository {
private final AccountJpaRepository accountJpaRepository;
@Override
public Account findById(long id) {
return accountJpaRepository.findById(id).orElseThrow(() -> new NotFoundException("account", id)).toModel();
}
@Override
@Transactional
public void save(Account account) {
accountJpaRepository.save(AccountJpaEntity.from(account));
}
}
- 현재 구조에서는 도메인 레이어가 인프라스트럭처 레이어를 참조하지 않는다
- -> 도메인 레이어는 JPA나 스프링을 임포트하지 않는다.
- 개인적인 의견 : 그것은 조회를 비즈니스 로직(도메인 로직)으로 보지 않았을 경우 아닌가?
- 조회를 복잡한 비즈니스 로직으로 본다면 어떻게 되는 것일까???
- 도메인 모델을 인터페이스로 작성하면 도메인 서비스에서 DB의존하더라도 기술을 의존하지 않을 수 있지 않을까?
- 엔티티의 기능도 살아 있을 거 같은데.. 더 고민 해보자
JPA와의 결합 끊기
- 어플리케이션 레이어에 위치한 AccountService 컴포넌트가 인프라스트럭처 레이어 위에 JPA에 의존하는 상황을 제거하기
- 어플리케이션 레이어에서 Repository 라는 인터페이스를 새롭게 배치하기
웹 프레임워크와의 결합 끊기
- AccountService를 인터페이스로 -> 헥사고날 아키텍처가 된다.
- 헥사고날 아키텍처는 외부세계를 다루는 방식으로 포트-어댑터 패턴을 사용함
- -> 실은 의존성 역전을 사용함 -> 의존성 역전의 또 다른 이름이 포트-어댑터 패
- 스프링 웹과 끊은 것이지 스프링과 끊은 것은 아님
- 하지만 프레젠테이션 레이어의 코드는 보통 재사용될 수 없음 -> 의존성 역전 적용했을 때 이점이 작음
빈약한 도메인
- 아키텍처의 효과가 없다면 도메인이 빈약한게 없을까. 빈약한 도메인이 성공할 수 있을까