본문 바로가기
Architecture

Clean Architecture (1)

by Heesu.lee 2021. 8. 28.

최근 DDD 와 같은 Architecture 에 대해 관심을 갖게 되었고, 관련해서 영상 하나를 추천 받아 시청하게 되었다.

영상은 Clean Architecture 에 대해 소개하고 있으며, 어떤 이유에서 Architecture 가 필요한지 설명해주었다.

 

https://www.youtube.com/watch?v=cPH5AiqLQTo 

해당 게시글은 위 영상 내용을 토대로 작성되었으며, 좀 더 관심 있으신 분들은 해당 영상을 보시길 적극 권장드린다.

 

아키텍처란 무엇인가? - Talk about what Architecture actually is

첫 주제는 아키텍처가 무엇이며, 왜 우리들은 아키텍처를 고민하는지에 대해 설명해주고 있었다.

사실 나 역시 아키텍처에 대해 들어보았지만, 무엇이냐는 질문에 정확한 대답을 할 수 없었다.

하지만, 이 영상을 보면서 조금은 더 명확하게 아키텍처의 존재 이유에 대해서 알 수 있게 되었다.

 

먼저, 영상에선 아키텍처의 목적에 대해 설명해주었다.

아키텍처의 목적 - What are the goals of Architecture

강사분 질문에 청중 대부분들이 멋진 대답을 해주었지만, 강사분은 많은 사람들이 아키텍처의 목적에 대해 잘 못 알고 있는 경우가 많다고 말하고 있었다.

Not making software works 

많은 사람들은 아키텍처가 소프트웨어가 잘 구동할 수 있도록 해준다고 생각하지만, 이는 전혀 아니라고 설명하고 있다.

사실 이 부분에 대해서도 나 역시 오해하고 있었을지도 모른다는 생각이 들었다. 너무 넓은 개념이다 보니 이 부분 역시 아키텍처의 존재 이유이지 않을까 생각했었다. 

 

강사분은 정말 심각히 좋지 않은 아키텍처 역시 작동은 한다고 설명하고 있었다.

그렇다면 도대체 아키텍처의 존재 이유와 목적은 무엇일까?

 

Facilitating the whole develepment experience

  • Facilitating develepment
  • Facilitating deployment
  • Facilitating maintenance
facilitate: 용이하게 하다, 쉽게 만들다, 촉진 시키다.

강사분이 강조한 아키텍처의 목적은 개발 전체 프로세스를 쉽고 간단하게 만드는데 있다고 설명해주고 있었다.

여기서 개발 전체 프로세스는 개발, 배포, 유지 보수가 포함된 전체 프로세스라 생각하면 되겠다.

 

The goal of software architecture is to minimize the lifetime cost of the software

  • the development cost
  • the maintenance cost afterwards

 

아키텍처는 위의 모든 프로세스를 전반적으로 쉽고 용이하게 해준다.

특히, 아키텍처는 개발 이후 유지 보수 비용을 크게 절감시키는데 용이하다고 설명해주었다.

 

Architecture

  • Keep software really soft
  • Keep framework at arm's length 
  • Keep options open

결국 아키텍처의 핵심은 특정 기술, 프레임워크에 대한 의존성을 줄이며, 이를 통해 변경이 용이하고, 확장성이 높은 코드를 만들어 개발 프로세스의 비용을 줄이는 것이라 설명해주고 있었다.

 

그렇다면, 어떻게 아키텍처를 통해 확장성이 높은 안정적인 코드를 만들고 프로세스 비용을 줄일 수 있을까

이제부터 몇가지 예시를 통해 알아볼 수 있도록 해보자

 

아래 아키텍처는 어떤 문제점이 있을까?

가장 보편적으로 볼 수 있는 계층 구조이다. 하지만, 강사분은 위 계층 구조는 추가적인 제약사항이 없다면 문제점을 유발할 수 있다고 말하고 있다.

 

강사분은 위의 계층을 또 다른 DDD 로 설명하고 있었다. (Database Driven Design) 또 많은 프로젝트들이 위 처럼 작성되고 있음을 언급하였다.

Hard to change things due to coupling to the Databse

 

위 계층은 특정 Database 에 종속된 코드를 만들어 변경을 취약하게 만들고 있다고 설명한다.

 

다음 계층 구조는 소히 여러 군데서 쓰이는 모든 클래스들을 가장 하위 레이어로 밀어넣는 구조를 가지고 있다.

 Certain classes that are used through our mutiple layers jus push them to the most bottom layer

가장 하위 레이어는 모든 계층이 해당 계층에 대한 의존성을 가지고 있기 때문에 여러 군데서 쓰이는 클래스들을 가장 밑단으로 밀어넣어 여러군데에서 가져다가 사용하는 모습을 일컫는다.

Helper means the other classes that are needed everywhere
Easily blurred Boundaries

 

이러한 계층 구조를 가지게 된다면 이는 아키텍처의 경계를 모호하게 만든다고 설명한다.

계층 구조를 통해 가독성과 사용성을 높이는 데 그 목적이 있지만, 위와 같이 경계가 모호하다면 이는 결코 이점을 가질 수 없다.

 

 

아래와 같이 Controller 에서 Repository 를 바로 접근 가능한 계층 구조는 어떠한가?

Repository can accessed from Controller

Hard to test due to many dependencies, Hard to test shortcuts

 

위와 같이 여러 의존성을 가지게 된다면, 이로 인한 너무 많은 Mocking 이 필요 해지며 결과적으로 독립적인 테스트를 저해하는 상황이 발생한다.

 

 

다음 아래 계층 구조와 같이 여러 곳에서 광범위하게 사용되는 서비스는 어떠한가?

강사분은 이러한 서비스를 Hidden Functionality 라고 설명한다.

Hidden Functionality - Broad Services that are spanned across multiple use cases
It makes Use Case hard to find in the code
It also makes Use Case hard to work in parallel on the same feature

 

이러한 서비스는 해당 어플리케이션이 어떤 기능 혹은 서비스를 제공하는지(Use Case) 알기 어렵게 만든다. (주문이라면 생성, 취소 등)

이 뿐만 아니라, 해당 서비스로 인하여 여러 사람과 병렬로 작업하는데도 이슈를 쉽게 발생시킬 수 있다. (머지 충돌 등)

 

그렇다면 앞서 살펴본 문제 사항들을 어떻게 해결할 수 있을까?

해결책을 살펴보기 앞서 우리 아는 SOLID 원칙에 대해 한번 살펴보도록 해보자

 

해당 게시글에선 아키텍처 스타일에 큰 영향을 미치는 DIP, SRP 원칙만을 다루도록 하겠다.

 

Dependency Inversion Principle

DIP means we can invert dependencies in some way

 

해당 영상에서 말하는 의존 관계 역전 원칙이란 바로 어떤 여러 방법으로 우리 스스로 의존 관계를 바꿀 수 있어야 한다고 말하고 있다.

 

다음 아래 그림은 의존 관계를 뒤바꾼 하나의 예시이다.

왼쪽 계층의 경우 Entity (Repository) 를 통해 Database 와 강하게 결합되어 있는 상태이다.

이는 곧 해당 애플케이션의 비즈니스(도메인) 혹은 서비스가 영속성 계층(Persistence Layer) 에 의존적이다고 말할 수 있다.

 

오른쪽의 경우 의존성을 역전시켜 비즈니스 계층이 더 이상 영속성 계층에 의존성을 갖지 않은 형태를 가지고 있다.

무엇이 다른 것이고 어떻게 의존성을 역전 시켰을까? (화살표 방향이 역전)

 

오른쪽은 왼쪽에 존재하던 Entity 를 도메인 계층으로 끌고와 영속성 계층에 대한 의존 관계를 바꿔버렸다.

여기서 중요한 점은 새롭게 정의된 도메인 Entity 는 Jpa Enity 가 아니라는 점이다. (Domain Entity ≠ Jpa Entity)

그리고 인터페이스를 두어 영속성 계층에서 해당 역할(인터페이스)를 구현하여 도메인이 더 이상 영속성 계층에 의존하지 않도록 변경하였다.

 

영속성 계층의 경우 어떤 기술을 사용하든지 해당 인터페이스를 구현하기만 하면 된다. 그리고 더 이상 도메인 영역은 특정 기술 (JPA 등)에 더 이상 영향을 받지 않게 되었다.

 

더 이상 의존적이지 않게 되었다는 뜻은 다시 말해 비즈니스 혹은 도메인 계층이 의존에 대한 방향성을 정할 수 있다는 뜻이다. 이는 앞서 설명한거와 같이 특정 기술에 의존적이지 않으며, Third-Party framework 혹은 라이브러리를 자유롭게 선택, 변경 혹은 추가해줄 수 있다는 것과 일맥상통 한다.

 

Dependency Inversion

앞서 설명한 바와 같이 DIP 의 핵심은 바로 애플리케이션의 핵심 로직 (비즈니스 혹은 도메인 계층)들이 아키텍처 중앙으로 올 수 있도록 변경하는 것이다.

 

이를 통해 도메인 계층은 더 이상 영속성(Database), UI, Web 과 같이 구체적인 사항들에 대해 의존적이지 않게 되면 이를 통해 안정적인 상태를 유지할 수 있게 된다.  즉, 다시 말해 어떠한 구체적인 기술 혹은 코드로 인한 핵심 로직의 변경을 최소한으로 줄이는 것을 의미한다.

 

Single Responsibility Principle

이 글을 읽고 계신 분들이 생각하는 SRP 단일 책임 원칙은 무엇이라 생각하나요?

아마 대부분의 사람들이 단일 책임 원칙을 말 그래도 객체 혹은 모듈은 하나의 책임만을 수행해야 한다라고 알고 계실 것 같다.

저 역시 해당 부분에 대해 처음 공부할 때부터 지금까지 하나의 책임에 초점을 두고 그 책임의 scope 에 대해서만 항상 고민해왔었다.

 

물론 해당 사항이 완벽히 틀린 것은 아니지만 강사분은 이러한 해석을 잘 못 이해한 것이라 설명하고 있다.

It's wrong that a module should only do one thing
Module(Class) should have only One Reason to change

 

강사분은 SRP 에 대해 해당 영상에서 이렇게 말하고 있었다. 

하나의 모듈 혹은 클래스는 하나의 책임, 역할, 행동만 있는 것이 아닌 단 하나만의 변경될 이유를 가져야 한다.

It's only going to be changed for this reason and this makes the code base more stable

 

이렇게 변경 포인트가 하나만 존재해야만 더 안정적인 코드를 작성할 수 있다고 말하고 있었다.

사실 굉장히 이 부분에 대해서 공감은 하지만 어떻게 해야 단 하나만의 변경 이유를 만들 수 있을지 명확한 방법에 대해선 잘 떠오르진 않았다.

 

결국 강사분이 말씀하시는 부분을 단 하나의 역할, 책임을 가지고 있더라도 여러 변경 이유를 가지게 된다면 이를 통해 잦은 코드 변화가 일어날 것이고 이런 코드는 안정적일 수 없다는 것으로 이해하였다.

 

What does mean SRP on Macro level or Architecture level

보다 넓은 거시적 관점에서 SRP 단일 책임 원칙은 어떤 의미를 가지는가

다음 위와 같은 계층 구조에서 비즈니스 계층 혹은 도메인 계층은 영속성 계층에 대해 의존관계를 가지고 있다.

이러한 상황에서 만약 영속성 계층에 변경이 발생한다면 필연적으로 도메인 계층에 대한 코드 변경이 필요할 수 밖에 없다.

Changes in persistence layer may affect business code

 

즉, 우리는 원하지 않은 변경 이유가 도메인 계층에 추가된 것이나 다름이 없다.

오로지 도메인 계층은 어떠한 요구사항 변경에 따라 변경이 일어나야 하는데 이런 구체적인 것에 의존하게 되면서 또 다른 변경 이유가 생겨버린 것이다. 이러한 도메인 계층은 안정적이지 않다고 볼 수 있다.

 

반대로 아래와 같은 Clean Architecture Circular Style 를 다시 살펴보자

위와 같이 도메인 계층이 계층 중심으로 들어가 어떠한 외부 의존성을 가지지 않도록 DIP 가 적용된 아키텍처에선 도메인 계층은 안정적으로 운영된다. 해당 도메인 계층을 둘러싼 어떤 외부 계층에서 변화가 일어나더라도 도메인 계층이 변경되어야 할 이유는 존재하지 않게 된다. 이로 인하여 도메인 계층은 보다 더 안정적으로 운영될 수 있게 된다.

 

따라서, 위와 같은 아키텍처에서 도메인 계층에 대한 DIP 가 적용되었다면 외부 다른 요인에 의한 변경 이유를 없애고 보다 더 안정적으로 SRP 단일 책임 원칙을 지킬 수 있게 되는 것이다.

 

이렇게 SRP 원칙이 잘 적용이 된다면 구체적인 것들에 신경쓰지 않고(JPA - Lazy Loading 등) 더 자유롭게 도메인 계층을 작성할 수 있을 것이다.

Domain Centric Architecture which has the domain layer in the middle of it allows Domain Driven Desgin
It can make it to design freely without having to think about certian technology issues

 

위와 같이 도메인이 가장 중앙부에 위치시킨 계층이어야만 Domain-Driven Design 을 구현할 수 있다.

우리의 도메인은 그 어떤 특정 구체적인 기술이 아니다, 사용자의 요구사항 (기능)이며 이를 구체적인 의존성을 제거해야 진정한 의미의 DDD 를 도출할 수 있는 것이다.

 

What does mean SRP on Micro level or Class level

미시적 관점 혹은 클래스 레벨에서의 단일 책임 원칙은 어떤 것일까?

Service is reponsible for multiple actors or multiple use cases

다음 위와 같이 여러 사용자들로부터 사용되는 광범위한 서비스가 존재한다고 생각해보자 (Broad Service)

예) OrderService - 주문 관련된 모든 광범위한 서비스를 담당 (주문 생성, 취소 등)

 

각 사용자들은 서로 다른 서비스(Use Case)를 요청 중인 상태인데 (주문 생성, 취소, 확인 등) 만약 이러한 상황에서 특정 한 사용자에 대한 요구사항이 변경되어 바뀌게 된다면 어떻게 될 것인가?

 

위와 같이 특정 기능에 대한 요구사항이 달라져 변경하게 된다면 이는 다른 기능엔 별다른 요구사항 변경이 일어나지 않았음에도 영향이 미칠 수 있다.

 

어떻게 하면 이런 불필요한 변경 이유가 생기는 것을 막을 수 있을까?

강사분은 이것에 대한 해결책으로 서비스 분리를 말하고 있었다.

Split this broad service up into Use Cases
Service is responsible for one actor or Use Case

 

말 그래도 광범위한 서비스를 각 사용처(Use Case)에 맞도록 분리시키는 것이다.

기존 OrderService 를 예시로 들자면 CreateOrderService, UpdateOrderService, CancleOrderService 로 나눠버리는 것을 의미한다. 이 과정에서 강사분은 서비스 대신 UseCase 란 단어를 사용하였지만 그것은 중요하지 않다. 여기서 중요한 건 실제 사용되는 하나의 UseCase 별로 광범위한 서비스를 쪼개는 것이다.

 

클래스 레벨에서의 SRP 단일 책임 원칙이란 하나의 서비스 클래스가 모든 Use Case를 가지지 않도록 하여 자신과 상관없는 요구사항들로 인하여 변경될 여지를 남기지 않도록 하는 것을 의미한다. 다시말해 각 Use Case 별로 서비스 클래스를 쪼개어 사용하는 것을 의미한다.

 

Hexagonal Architecture

강사분은 위의 내용들을 총체적으로 담고 있는 Clean Architecutre 의 하나의 예시로 Hexagonal Architecture 를 보여주었다.

 

이 부분은 다음 게시글에서 다룰 수 있도록 하겠다.

 

참고

  • Clean Architecture with Spring by Tom Hombergs @Spring I/O 2019 - 링크

 

※ 주니어 개발자이기에 부족한 점이 많습니다. 틀린 부분이나 부족한 부분에 대해 자유롭게 글 남겨주시면 감사하겠습니다.

'Architecture' 카테고리의 다른 글

Clean Architecture (2)  (0) 2021.09.13

댓글