Layer(계층형)를 이용하는 사고 방식은 우리에게 오랫동안 주입되어 왔다고 한다.
- 웹 : 요청을 받아. 도메인(비즈니스) 계층에 있는 서비스로 요청을 보낸다.
- 도메인(서비스 보유) : 서비스에서 필요한 비즈니스 로직을 수행한다.
- 엔티티의 현재 상태를 조회하거나 변경하기 위해 영속성 계층 컴포넌트를 호출한다.- 영속성(엔티티) : 상태를 저장 / 관리한다.
견고한 아키텍처 패턴이고, 선택의 폭을 넓히며 변화하는 요구사항과 외부 요인에 빠르게 적응할 수 있게 해준다고 한다.
하지만 코드에 나쁜 습관들이 스며들기 쉽게 만들고 시간이 지날수록 점점 더 변경하기 어렵게 만드는 약점을 노출한다고 한다.
그 이유는 전통적인 계층형 아키텍처의 토대가 데이터베이스 이기 때문인다.
-> 모든 것이 영속성 계층을 토대로 만들어진다.
ORM 프레임워크 사용을 계층형 아키텍처와 결합하는 것에서 비즈니스 규칙을 영속성 관점과 섞게 되고, 영속성 코드가 도메인 코드에 녹아들어가서 둘 중 하나만 바꾸는 것이 어려워진다. 즉 ,강한 결합이 생긴다.
계층형 아키텍처 자체는 다른 규칙을 강제하지 않아 지름길을 택하기 쉬워진다고 한다.
- 계층형 아키텍처에서는 같은 계층이나 아래 계층에만 접근이 가능하다.
- 개발을 하다보면 상위 계층에 위치한 컴포넌트에 접근해야할 때가 있다. 이때 접근할 컴포넌트를 계층 아래로 내려버린다.
- 한번이 어렵지 두번 세번은 쉽다.
- 심리학에서는 이러한 이론을 "깨진 창문 이론"이라고 부른다.
단일 책임 원칙(SRP) x : 하나의 객체는 오로지 한 가지일만 해야하고, 그것을 올바르게 수행해야 한다. ' 오로지 한 가지 일만 하는 것'
- 단일 변경 이유 원칙 O : 컴포넌트(객체?)를 변경하는 이유는 오직 하나뿐이어야 한다. 이것이 진정한 단일 책임 원칙의 의도이다.
컴포넌트를 변경할 이유가 오로지 한 가지라면 어떤 다른 이유로 소프트웨어를 변경하더라도 전혀 신경 쓸 필요가 없다. 여전히 기대한대로 동작할 것이기 때문이다. 변경할 이유라는 것은 의존성을 통해 너무도 쉽게 전파된다.
계층형 아키텍처에서 계층 간 의존성은 항상 다음 계층인 아래 방향을 가리킨다. 단일 책임 원칙을 고수준에서 적용할 때 상위 계층들이 하위 계층들에 비해 변경할 이유가 더 많다는 것을 알 수 있다.
-> 영속성 계층을 변경할 때마다 잠재적으로 도메인 계층도 변경해야 한다.
그러나 도메인 코드는 가장 중요한 코드다. 영속성 코드가 바뀐다고 해서 도메인 코드까지 바꾸고 싶지는 않다. 이 의존성을 어떻게 제거할 수 있을까?
의존성 역전 원칙(DIP) :코드 상의 어떤 의존성이든 그 방향을 바꿀 수(역전시킬 수) 있다.
여기에 들어가면 다이어그램을 직접 그려보면서 이해해볼 수 있다!
도메인 계층에 서비스가 있다. 서비스는 영속성 계층의 엔터티, 리포지토리와 상효작용한다.
엔티티는 도메인 객체를 표현하게 되고, 도메인 코드는 엔티티들의 상태를 변경한다.
영속성 계층의 리포지토리가 도메인 계층에 있는 엔티티에 의존한다.
도메인 계층에 리포지토리에 대한 인터페이스를 만들고, 실제 리포지토리는 영속성 계층에서 구현하게 한다.
영속성 코드에 숨막히는 의존성으로부터 도메인 로직을 해방시키는 방법이고,
다음 아키텍처 스타일의 핵심 기능이라고 한다.
- 의존성을 역전시켜 도메인 코드가 다른 바깥쪽 코드에 의존하지 않게 한다.
- 영속성과 특화된 모든 문제로부터 도메인 로직의 결합을 제거한다.
- 도메인 로직의 결합을 제거하고 코드를 변경할 이유를 줄일 수 있다.
- 변경할 이유가 적을 수록 유지보수성이 더 좋아진다고 한다.
- 도메인 코드는 비즈니스 문제에 딱 맞도록 자유롭게 모델링 될 수 있다.
- 영속성 코드와 UI 코드도 영속성 문제와 UI 문제에 맞게 자유롭게 모델링 될 수 있다고 한다.
public class Service {
private Repository repository;
private Entity entity;
public void setRepository(Repository repository) {
this.repository = new Repository();
}
public void setEntity(Entity entity) {
this.entity = new Entity();
}
}
public class Repository {
private Entity entity;
public void setEntity(Entity entity) {
this.entity = new Entity();
}
}
- 서비스는 영속성 계층의 엔터티, 리포지토리와 상효작용한다.
public interface Repository{
void manageEntityStatus();
}
public class RepositoryImpl implements Repository {
private PersistedEntity persistedEntity;
@Override
public void manageEntityStatus() {
this.persistedEntity = new PersistedEntity();
}
}
public class Service implements Repository{
private Entity entity;
@Override
public void manageEntityStatus() {
this.entity = new Entity();
}
}
어라.. 그러고보니 리토지토리가 도메인으로 올라온 엔티티를 의존해야 되네..? 인터페이스가 클래스를 의존하나..?
이건 또 왜 ㅎ..