일반적인 해석 → “하나의 컴포넌트는 오로지 한 가지 일만 해야 하고, 그것을 올바르게 수행해야 한다.”
실제 정의 → “컴포넌트를 변경하는 이유는 오직 하나뿐이어야 한다.”
아키텍처에서 SRP의 의미
아쉽게도 변경할 이유(책임)라는 것은 컴포넌트 간의 의존성을 통해 너무도 쉽게 전파된다.
의존성 각각은 변경하는 이유 하나씩에 해당한다. 점선 화살표 같은 전이 의존성도 마찬가지이다.
컴포넌트 A의 경우 모든 컴포넌트에 의존하고 있다. 즉 다른 어떤 컴포넌트가 바뀌던지 영향을 받게 된다.
일반적으로 많은 코드는 SRP를 위반한다. 그리고 그로인해, 점점 변경하기가 더 어려워지고 변경 비용도 증가한다.
의존에 따라 변경 이유가 늘어나게 된다. 즉, 의존 관계가 있을 경우 엄격하게 따지면 SRP 위반이 맞다고 생각한다.
하지만 애플리케이션의 사이즈가 조금만 커져도 의존 관계를 이용하지 않고 작성하는 건 불가능하고, 가능하더라도 오히려 더 많은 문제를 야기한다.
그렇기에 의존으로 생기는 변경 이유는 제외하고 하나의 책임, 즉 하나의 변경 이유를 갖는 것이 현실적인 SRP의 실현이라고 생각한다. 하지만 그렇다고 하더라도 의존 관계가 지나치게 많은 것은 부적합하며 SRP를 어겼다고 판단해야한다.
요구사항의 변경 등으로 코드에 한 영역을 변경했다. → 예상하지 못한 곳에서 부수효과가 발생한다.
이 경우 클라이언트가 변경을 원하지만 부수효과를 우려해서, 올바른 방향의 변경으로 나아갈 수 없는 악순환이 반복될 수 있다. → SW 신뢰의 문제이기도 하다고 생각한다.
계층형 아키텍처에서 영속성 계층에 대한 도메인 계층의 의존성으로 인해, 영속성 계층을 변경할 때 마다 도메인 계층에 영향이 가게 된다.
그럼 어째?
DIP → “코드 상의 어떤 의존성이든 그 방향을 바꿀 수(역전시킬 수) 있다.”
도메인 코드와 영속성 코드 간의 의존성 역전 → 도메인 코드의 변경할 이유의 개수를 줄여보자.
엔티티는 도메인 객체를 포현하고 도메인 코드는 이 엔티티들의 상태를 변경하는 일을 중심으로 하기 때문에 엔티티가 도메인 계층에 위치
“도메인 → 영속성” 방향의 의존성을 “영속성 → 도메인” 방향으로 역전
로버트 C.마틴 → ‘클린 아키텍처’
이 아키텍처에서 가장 중요한 규칙은 의존성 규칙
Core에는 주변 유스케이스에서 접근하는 도메인 엔티티들이 존재
유스케이스 = 앞 쪽 설명의 서비스
도메인 코드에서는 어떤 영속성 프레임워크나 UI 프레임워크가 사용되는지 알 수 없기 없다!
다소 추상적이다.
애플리케이션의 엔티티에 대한 모델을 각 계층에서 유지보수해야한다..!
영속성 계층에서 ORM 프레임워크를 사용한다고 가정
근데 위와 같은 상황은 바람직하다!
동적으로 객체 생성 시 Reflection API를 활용하기 때문이다.
Reflection API → 구체적인 클래스 타입을 알지 못해도 ****그 클래스의 정보(메서드, 타입, 변수 등등)에 접근할 수 있게 해주는 자바 API
대부분 프레임워크나 라이브러리 같은 사용하는 사람이 어떤 클래스를 만들지 모르는 경우 리플렉션을 통해 동적으로 이 문제를 해결하기 위해 많이 사용한다.
Reflection API로 가져올 수 없는 정보 중 하나가 생성자의 인자 정보!
Ref
클린 아키텍처의 원칙들을 조금 더 구체적으로 만들어준다!
애플리케이션 코어가 각 어댑터와 상호작용하기 위해 특정 포트를 제공하기 때문에 “포트와 어댑터 아키텍처”이라고도 한다.
육각형 안에는 도메인 엔티티와 이와 상호작용하는 유스케이스 존재
육각형에서 외부로 향하는 의존성 존재 X
왼쪽에 있는 어댑터
오른쪽에 있는 어댑터
애플리케이션 코어 - 어댑터 통신을 위해 애플리케이션 코어가 각각의 포트를 제공해야한다.
헥사고날 아키텍처를 계층으로 구성해보면...
“의존성을 역전시켜 도메인 코드가 다른 바깥쪽 코드에 의존하지 않게 함으로써 영속성과 UI에 특화된 모든 문제로부터 도메인 로직의 결합을 제거하고 코드를 변경할 이유의 수를 줄일 수 있다.”
도메인 코드를 비즈니스 문제에 맞게 자유롭게 모델링 할 수 있다.
영속성과 UI코드도 각각의 문제에 맞게 자유롭게 모델링 할 수 있다.