아키텍처 왜 쓸까

김주형·2022년 11월 16일
0

자라기

목록 보기
13/22
post-thumbnail

🙇🏻‍♂️ Reference


목표

  • 왜 객체지향, 아키텍처 등에 집착하는 건지 궁금해하던 중
  • 슈퍼 앱을 위한 노력이라는 영상을 보고
  • 해결해야 하는 과제가 복잡하고 많아지는 것에 대응하기 위함이라고 생각했습니다.
  • 이를 본받고자 조금이나마 도메인과 클린 아키텍처에 대해 학습했습니다.

계층형 아키텍처의 문제

Layer(계층형)를 이용하는 사고 방식은 우리에게 오랫동안 주입되어 왔다고 한다.

  • 웹 : 요청을 받아. 도메인(비즈니스) 계층에 있는 서비스로 요청을 보낸다.
  • 도메인(서비스 보유) : 서비스에서 필요한 비즈니스 로직을 수행한다.
    - 엔티티의 현재 상태를 조회하거나 변경하기 위해 영속성 계층 컴포넌트를 호출한다.
  • 영속성(엔티티) : 상태를 저장 / 관리한다.

견고한 아키텍처 패턴이고, 선택의 폭을 넓히며 변화하는 요구사항과 외부 요인에 빠르게 적응할 수 있게 해준다고 한다.

하지만 코드에 나쁜 습관들이 스며들기 쉽게 만들고 시간이 지날수록 점점 더 변경하기 어렵게 만드는 약점을 노출한다고 한다.

그 이유는 전통적인 계층형 아키텍처의 토대가 데이터베이스 이기 때문인다.
-> 모든 것이 영속성 계층을 토대로 만들어진다.

  • 대부분 애플리케이션의 목적은 보통 비즈니스를 관장하는 규칙이나 정책을 반영한 모델을 만들어서 사용자가 이러한 규칙과 정책을 더욱 편리하게 활용할 수 있게 해주는 것
  • 상태가 아니라 행동을 중심으로 모델링하게 됨.(행동이 상태를 바꾸는 주체이기 때문)
  • 그런데 왜 도메인 로직이 아닌 데이터베이스를 토대로 아키텍처를 만드는 걸까?

ORM 프레임워크 사용을 계층형 아키텍처와 결합하는 것에서 비즈니스 규칙을 영속성 관점과 섞게 되고, 영속성 코드가 도메인 코드에 녹아들어가서 둘 중 하나만 바꾸는 것이 어려워진다. 즉 ,강한 결합이 생긴다.
계층형 아키텍처 자체는 다른 규칙을 강제하지 않아 지름길을 택하기 쉬워진다고 한다.

  • 계층형 아키텍처에서는 같은 계층이나 아래 계층에만 접근이 가능하다.
  • 개발을 하다보면 상위 계층에 위치한 컴포넌트에 접근해야할 때가 있다. 이때 접근할 컴포넌트를 계층 아래로 내려버린다.
  • 한번이 어렵지 두번 세번은 쉽다.
  • 심리학에서는 이러한 이론을 "깨진 창문 이론"이라고 부른다.

대안 : 의존성 역전하기

단일 변경 이유 원칙

단일 책임 원칙(SRP) x : 하나의 객체는 오로지 한 가지일만 해야하고, 그것을 올바르게 수행해야 한다. ' 오로지 한 가지 일만 하는 것'

  • 단일 변경 이유 원칙 O : 컴포넌트(객체?)를 변경하는 이유는 오직 하나뿐이어야 한다. 이것이 진정한 단일 책임 원칙의 의도이다.

컴포넌트를 변경할 이유가 오로지 한 가지라면 어떤 다른 이유로 소프트웨어를 변경하더라도 전혀 신경 쓸 필요가 없다. 여전히 기대한대로 동작할 것이기 때문이다. 변경할 이유라는 것은 의존성을 통해 너무도 쉽게 전파된다.

의존성 역전 원칙

계층형 아키텍처에서 계층 간 의존성은 항상 다음 계층인 아래 방향을 가리킨다. 단일 책임 원칙을 고수준에서 적용할 때 상위 계층들이 하위 계층들에 비해 변경할 이유가 더 많다는 것을 알 수 있다.

-> 영속성 계층을 변경할 때마다 잠재적으로 도메인 계층도 변경해야 한다.
그러나 도메인 코드는 가장 중요한 코드다. 영속성 코드가 바뀐다고 해서 도메인 코드까지 바꾸고 싶지는 않다. 이 의존성을 어떻게 제거할 수 있을까?

의존성 역전 원칙(DIP) :코드 상의 어떤 의존성이든 그 방향을 바꿀 수(역전시킬 수) 있다.

의존성 역전은 어떻게 동작할까?

여기에 들어가면 다이어그램을 직접 그려보면서 이해해볼 수 있다!

  1. 도메인 계층에 서비스가 있다. 서비스는 영속성 계층의 엔터티, 리포지토리와 상효작용한다.

  2. 엔티티는 도메인 객체를 표현하게 되고, 도메인 코드는 엔티티들의 상태를 변경한다.

    • 따라서 엔티티가 도메인 계층으로 올라오게 된다.
  3. 영속성 계층의 리포지토리가 도메인 계층에 있는 엔티티에 의존한다.

    • 두 계층 사이 순환 의존성이 발생한다.
    • 순환 의존성 : A가 B를 의존하는데 B가 A를 다시 의존하는 현상
  4. 도메인 계층에 리포지토리에 대한 인터페이스를 만들고, 실제 리포지토리는 영속성 계층에서 구현하게 한다.

영속성 코드에 숨막히는 의존성으로부터 도메인 로직을 해방시키는 방법이고,
다음 아키텍처 스타일의 핵심 기능이라고 한다.

클린 아키텍처

  • 도메인 코드가 바깥으로 향하는 어떤 의존성도 없어야 함. 대신, 의존성 역전 원칙 도움으로 모든 의존성이 도메인 코드로 향한다.
  • 모든 의존성은 도메인 로직을 향해 안쪽 방향으로 향한다!
  • 의존성을 역전시켜 도메인 코드가 다른 바깥쪽 코드에 의존하지 않게 한다.
  • 영속성과 특화된 모든 문제로부터 도메인 로직의 결합을 제거한다.
  • 도메인 로직의 결합을 제거하고 코드를 변경할 이유를 줄일 수 있다.
  • 변경할 이유가 적을 수록 유지보수성이 더 좋아진다고 한다.
  • 도메인 코드는 비즈니스 문제에 딱 맞도록 자유롭게 모델링 될 수 있다.
  • 영속성 코드와 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();
    }

}
  1. 서비스는 영속성 계층의 엔터티, 리포지토리와 상효작용한다.

  1. 엔티티를 도메인 레이어로 끌어올리고 조금 정리해보면 순환 참조를 확인할 수 있다.

  1. 도메인 계층에 레포지토리 인터페이스를 생성한다.
public interface Repository{

    void manageEntityStatus();

}
  1. 영속성 계층에서 영속성 엔티티를 따로 생성하고, 레포지토리 구현체가 사용하도록 코드를 변경해준다.
    • 다음 그림과 일치하면 검증 완료!
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();
    }
}

어라.. 그러고보니 리토지토리가 도메인으로 올라온 엔티티를 의존해야 되네..? 인터페이스가 클래스를 의존하나..?

이건 또 왜 ㅎ..

profile
왔을때보다좋은곳으로

0개의 댓글