사내에 개발 표준을 수립할 때 정리한 레이어드 아키텍처에 대해서 정리하고자 합니다.
그중 4계층형 아키텍처에 대해서 정리합니다.
1. 레이어드 (계층형) 아키텍처란?
시스템을 각 관심사를 기준으로 계층으로 분리하여, 각 계층의 특정한 책임을 가지고 상호 작용하도록 구성하는 방식
2. 레이어드 아키텍처의 특징
- 각 레이어는 독립적인 모듈로 구성합니다.
- 각 계층은 고유한 관심사를 가지고 있으며 시스템의 복잡성을 줄여 줄 수 있습니다.
- 하위 레이어는 상위 레이어를 의존하지 않습니다.
3. 4계층 아키텍처
User Interface Layer (UI Layer)
- 사용자와 시스템(애플리케이션)간의 상호작용 (연결)을 하기 위한 레이어
- EX) ApprovalController
// ApprovalResponse
@GetMapping("/{approvalId}")
@Operation(summary = "결재 문서 조회", description = "결재문서를 조회합니다.")
public ApprovalResponse getApproval(@NotNull @PathVariable Long approvalId) {
return approvalQueryService.findDocument(approvalId);
}
Application Layer
- 애플리케이션이 제공하는 주요 기능 및 비지니스 구현의 역할을 수행한다.
- Infrastructure Layer를 활용
- 트랜잭션 관리, DTO 변환, 도메인 객체 생성
- 도메인 서비스 역할을 수행한다.
- EX ) ApprovalService
/** Service */
@Service
public class ApprovalService {
@Transactional
public void approve(Long documentId, String memberId, ApprovalLineAgreeRequest request) {
ApprovalDocument approvalDocument = approvalDocumentRepository.findById(documentId)
.orElseThrow(() -> new NotFoundException("결재문서가 존재하지 않습니다."));
ApprovalLines approvalLines = approvalDocument.getApprovalLines();
approvalLines.approvalLineApprove(memberId, request.comment());
if (!approvalLines.isRemainApprove()) {
approvalDocument.complete();
}
}
}
Domain Layer
- 핵심 도메인 객체가 포함되어 있으며 핵심 도메인 행위와 상태를 담당하고 핵심 비즈니스 로직을 구현한다.
- EX ) ApprovalLine
public class ApprovalLine {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Min(1)
@Comment("결재순번")
private int approvalSeq;
@Comment("결재상태")
private ApprovalLineStatus status;
public void approve(String memberId, String comment) {
if (!approverId.equals(memberId)) {
throw new IllegalArgumentException("해당 결재자가 아니여서 승인할 수 없습니다.");
}
if (isApproved()) {
throw new IllegalArgumentException("이미 승인된 결재라인입니다.");
}
this.status = ApprovalLineStatus.APPROVED;
this.comment = comment;
}
}
}
Infrastructure Layer
- 다른 레이어들을 지탱해주며 외부의 의존 및 기술을 담당하는 역할을 수행한다
- DB연결, DB 적재, 외부 Rest API 등..
- EX) JpaRepositiory, Mybatis mapper 등
public interface JpaApprovalDocumentRepository extends ApprovalDocumentRepository
, ApprovalQueryRepository
, JpaRepository<ApprovalDocument, Long> {
}
패키지를 보자면
─ approval (도메인 명 : EX - 결재)
├─ application (Application Layer)
│ └─ ApprovalService.java
├─ domain (도메인. - 핵심 도메인)
│ ├─ ApprovalDocument.java
│ ├─ ApprovalDocumentRepository.java
│ ├─ ApprovalDocumentStatus.java
│ ├─ ApprovalLine.java
│ ├─ ApprovalLineStatus.java
│ ├─ ApprovalLines.java
├─ infrainsturcture (인프라 인스트럭처 계층)
│ └─ JpaApprovalDocumentRepository.java
└─ ui (표현 - UI 계층)
└─ ApprovalController.java
흐름
각 계층은 레이어간 닫혀있어 하위레이어에서는 상위레이어를 호출하면 안 됩니다.
싱크홀 패턴?
계층간 호출 별도 로직 없이 호출만 하는 경우
이런 경우에는 오버헤드만 증가한다. 해당 계층에 80% (파레토 법칙의 근거) 정도의 그러한 로직이라면 계층을 열어두는 게 낫다
싱크홀
// UI 계층
@GetMapping("/{approvalId}")
public ApprovalResponse getApproval(@NotNull @PathVariable Long approvalId) {
return approvalQueryService.findDocument(approvalId);
}
// Applcation 레이어
public ApprovalResponse getApproval(@NotNull @PathVariable Long approvalId) {
/** 별도 로직없이 계층을 통과하고 있다.*/
return approvalRepositroy.findDocument(approvalId);
}
싱크홀 방지 패턴
// UI 계층
@GetMapping("/{approvalId}")
public ApprovalResponse getApproval(@NotNull @PathVariable Long approvalId) {
return approvalRepositroy.findDocument(approvalId);
}
Application Layer를 열어준다!
생각거리
정리하면서 느낀거지만, 레이어드 아키텍처의 핵심은 일련의 관심사를 묶는데 초점과 계층 간의 의존성관리를 도와준다고 생각이 들며, 의존성을 강력하게 관리하려면 각 계층별로 모듈화를 진행해 봐도 좋을 것으로 생각이 들었습니다.
레이어드 아키텍처에서 4계층 아키텍처만 제시하는 것은 아니지만, 계층이 많아지면 관리하기 복잡할 거 같지만 세부 계층을 나눌 수 있다는 장점이 있을 거 같네요 이 부분은 상황에 맞게 적절하게 쓰면 좋다고 생각이 들었습니다.
참고자료
https://www.oreilly.com/library/view/software-architecture-patterns/9781491971437/ch01.htmlhttps://www.baeldung.com/cs/layered-architecture