‘만들면서 배우는 클린 아키텍처’ 기술 서적을 읽고 학습 내용을 정리하기 위한 목적의 TIL 포스팅입니다🙆♂️ 예제코드는 깃허브 레포지토리를 참고해주세요.
11장 - 의식적으로 지름길 사용하기
- 지름길 자체를 파악하면 우발적으로 사용되는 지름길을 인식하고 수정할 수 있다. 또는 정당한 지름길이라면 지름길의 효과를 의식적으로 택할 수도 있다.
왜 지름길은 깨진 창문 같을까?
- 1969년 심리학자 필립 짐바르도의 ‘깨진 창문 이론’이라는 실험의 내용과 유사하다.
- ‘깨진 창문 이론’은 어떤 것이 멈춘 것처럼 보이고, 망가져 보이거나 관리되지 않는다고 여겨지면 인간의 뇌는 이를 더 멈추고, 망가뜨려도 된다고 생각하게 된다는것과 비슷하다.
- 위 이론이 코드 작업에 적용될땐 다음의 의미를 갖는다.
- 품질이 떨어진 코드에서 작업할때 더 낮은 품질의 코드를 추가하기 쉽다.
- 코딩 규칙을 많이 어긴 코드에서 작업할때 또 다른 규칙을 어기기도 쉽다.
- 지름길을 많이 사용한 코드에서 작업할때 또 다른 지름길을 추가하기도 쉽다.
깨끗한 상태로 시작할 책임
- 가능한 지름길을 쓰지 않고 기술 부채를 지지 않은채 프로젝트를 깨끗하게 시작하는것이 중요하다. 지름길이 몰래 스며드는 순간 깨진 창문과 같아져 버려서 더 많은 지름길을 끌어들이기 때문이다.
- 새로운 프로젝트를 인계받는 입장에서는 이 코드가 연관성 전혀 없는 레거시이기에 깨진 창문을 만들어 내기가 더 쉽다.
- 때론 지름길이 더 실용적일때도 있다. 작업 중인 부분이 프로젝트 전체로 봤을땐 그리 중요치 않거나, 프로토타이핑 작업중이거나, 경제적인 이유로..
- 이러한 의도적인 지름길에 대해서는 세심하게 잘 기록해둬야 한다. 마이클 나이가드가 제안한 아키텍처 결정 기록의 형태도 괜찮다.
- 만약 팀원 모두가 이 문서에 대해 인지하고 있따면 지름길이 합리적인 이유에 의해 추가됐다는 사실을 알기에 깨진 창문 이론의 영향을더 줄일수 있을 것이다.
유스케이스간 모델 공유하기
출처: https://jandari91.tistory.com/61
- 유스케이스들이 기능적으로 묶여 있을때 유효하다. 즉, 특정 세부사항을 변경할때 실제로 두 유스케이스 모두에 영향을 주고 싶은 것이다.
- 만약 두 유스케이스가 독립적으로 진화해야 한다면 입출력 모델 공유는 지름길이 된다. 만약 독립적으로 진화해야 한다면 처음엔 똑같은 입출력 모델을 복붙하더라도 일단 분리해서 시작해야 한다.
- 그러므로 유스케이스가 독립적으로 진화할 필요가 있는지 주기적으로 질문해야 한다. 대답이 “예”가 되는 그때가 바로 입출력 모델을 분리할 시점이다.
도메인 엔티티를 입출력 모델로 사용하기
출처: https://jandari91.tistory.com/61
- 인커밍 포트는 도메인 엔티티에 의존성을 가지고 있다. 그 결과 Account 엔티티는 변경할 또 다른 이유가 생긴것이다.
- 간단한 생성이나 업데이트 유스케이스에서는 유스케이스 인터페이스에 도메인 엔티티가 있는것이 괜찮을지도 모른다. (DB에 저장해야 하는 바로 그상태 정보가 엔티티에 있으므로)
- 하지만 유스케이스가 단순히 데이터베이스 필드 몇 개를 업데이트하는 수준이 아니라 더 복잡한 도메인 로직을 구현해야 한다면(도메인 로직 일부를 풍분한 도메인 엔티티로 위임할 수도 있으니) 유스케이스 인터페이스에 대한 전용 입출력 모델을 만들어야 한다. 왜냐하면 유스케이스 변경이 도메인 엔티티까지 전파되길 바라진 않을것이기 때문이다.
- 이 지름길이 위험한 이유는 많은 유스케이스가 간단한 생성 또는 업데이트 유스케이스로 시작해서 시간이 지난면서 복잡한 도메인 로직 괴물이 되어간다는 사실 때문이다.
- 이는 최소 기능 제품(MVP)으로 시작해서 점점 복잡도를 높여가는 애자일 환경에서 특히 그렇다.
- 그러므로 처음에는 도메인 엔티티를 입력 모델로 사용했더라도 도메인 모델로부터 독립적인 전용 입력 모델로 교체해야 하는 시점을 잘 파악해야 한다.
인커밍 포트 건너뛰기
- 아웃고잉 포트는 애플리케이션 계층과 아웃고잉 어댑터 사이의 의존성을 역전시키기 위한 (의존성을 안쪽으로 가게하기 위한) 필수 요소인 반면 인커밍 포트는 의존성 역전에 필수적인 요소는 아니다.
- 인커밍 어댑터가 인커밍 포트 없이 애플리케이션 서비스에 직접 접근하도록 할 수 있다.
출처: https://jandari91.tistory.com/61
- 위 이미지를 보면 인커밍 어댑터와 애플리케이션 계층 사이의 추상화 계층을 줄였다.
- 하지만 인커밍 포트는 애플리케이션 중심에 접근하는 진입점을 정의한다. 이를 제거하면 특정 유스케이스를 구현하기 위해 어떤 서비스 메서드를 호출해야 할지 알아내기 위해 애플리케이션 내부 동작에 대해 더 잘 알아야 한다. 전용 인커밍 포트를 유지하면 한눈에 식별 가능하다. 이는 새로운 개발자가 코드를 파악시 특히 더 도움된다.
- 또 다른 이유는 아키텍처를 쉽게 가능할 수 있기 때문이다. 이전 장에서 소개한 아키텍처를 강제하는 옵션들을 이용하면 인커밍 어댑터가 애플리케이션 서비스가 아닌 인커밍 포트만 호출하게 할 수 있다. 그럼 애플리케이션 계층에 대한 모든 진입점을 정의하는것이 아주 의식적인 결정이 된다. 인커밍 어댑터에서 호출할 의도가 없던 서비스 메서드를 실수로 호출하는 일이 절대 발생할 수 없다.
- 애플리케이션 규모가 작거나 인커이 어댑터가 하나뿐이라 모든 제어 흐름을 인커밍 포트의 도움 없이 단순에 파악할 수 있다면 인커밍 포트가 없는것이 편하다.
- 그러나 애플리케이션 규모가 이후로도 계속 작게 유지되거나 인커밍 어댑터가 계속 하나밖에 없을것이라고는 확신할 수 없다.
애플리케이션 건너뛰기
출처: https://jandari91.tistory.com/61
- 아웃고잉 어댑터에 있는 AccountPersistenceAdapter 클래스는 직접 인커밍 포트를 구현해서 일반적으로 인커밍 포트를 구현하는 애플리케이션 서비스를 대체한다.
- 간단한 CRUD 유스케이스에선 보통 애플리케이션 서비스가 도메인 로직없이 생성, 업데이트, 삭제 요청을 그대로 영속성 어댑터에 전달하므로 구미가 당기는 방식이다. 그대로 전달하는 대신 영속성 어댑터가 직접 유스케이스를 구현하게 할 수 있다.
- 하지만 이 방법의 단점은 인커밍 어댑터와 아웃고잉 어댑터 사이에 모델을 공유해야 한다. 여기에선 도메인 모델을 입력 모델로 사용하는 케이스가 되버리게 되는것이다.
- 나아가 애플리케이션 코어에 유스케이스라고 할만한것이 없어진다. 만약 시간이 지남에 따라 CRUD 윳케이스가 점점 복잡해지면 도메인 로직을 그대로 아웃고잉 어댑터에 추가하고 싶은 생각이 들 수 있고, 그러면 도메인 로직이 흩어져서 추후 유지보수하기 어려워진다.
- 결국 단순히 전달만 하는 보일러 플레이트 코드가 가득한 서비스가 많아지는 것을 방지하기 위해 간단한 CRUD 유스케이스에선 애플리케이션 서비스를 건너뛰기로 결정할수도 있다. 하지만 유스케이스가 엔티티를 단순히 생성, 업데이트, 삭제하는 것보다 더 많은 일을 하게되면 애플리케이션 서비스를 만든다는 명확한 가이드라인을 팀에 정해둬야 한다.
유지보수 가능한 소프트웨어를 만드는데 어떻게 도움이 될까?
- 경제적인 관점에서 지름길이 합리적일때도 있다.
- 모든 애플리케이션은 처음엔 작게 시작하기에, 유스케이스가 단순한 CRUD 상태에서 벗어나는 시점이 언제인지에 대해 팀이 합의하는 것이 매우 중요하다. 합의를 이루고난 후에야 팀은 지름길을 장기적으로 더 유지보수하기 좋은 아키텍처로 대체할 수 있다.
- 단순 CRUD 상태에서 더이상 벗어나지 않은 유스케이스도 있다. 이러한 유스케이스는 유지보수 비용을 증가시키지 않기 때문에 지름길을 계속 그대로 유지하는게 더 경제적이다.
- 어떤 경우든 아키텍처에 대해, 그리고 왜 특정 지름길을 선택했는가에 대한 기록을 남겨서 나중에 우리 자신 또는 프로젝트를 인계받는 이들이 이 결정에 대해 다시 평가할 수 있게 하자.