[Clean Architecture] 3부. 설계 원칙

DEV-YONG·2021년 9월 25일
0

Clean Architecture

목록 보기
1/1

SOLID 원칙

  • 함수와 데이터를 클래스로 배치하는 방법과 이들 클래스들을 서로 결합하는 방법을 설명한다.
  • 중간 수준 의 구조를 아래와 같이 만들기 위한 목적을 지닌다.
    • 변경에 유연한
    • 이해하기 쉬운
    • 다양하게 쓰일 수 있는 컴포넌트의 기반이 되는

🤔 코드수준 , 중간수준 , 고수준 이란 무엇인가?
코드수준 : 실제 소스 코드

설계 원칙(SOLID)이 중간 수준의 소프트웨어 구조를 형성하는데 작용한다.

중간수준 : 모듈 수준, (컴포넌트 원칙)

고수준 : 아키텍처

🤔 클래스, 모듈의 개념적 차이는?

  • 동일한 개념을 뜻한다.
    • 클래스 : 단순히 함수와 데이터를 결합한 집합 (62p)
    • 모듈 : 단순히 함수와 데이터로 응집된 집합 (67p)

SRP: 단일 책임 원칙

오해

  • 모듈은 단 하나의 일만 해야한다.
  • 함수는 단 하나의 일만 해야한다. ✅
    • 이 원칙은 SOLID 원칙에 속하지 않는다.

정의

"A class should have only one reason to change"

  • SRP에서는 "변경의 이유(reason to change)"가 될 책임(responsibility)을 규정한다.
  • "변경의 이유"란 바로 Actor(변경을 요청하는 집단, 사용자와 이해관계자)를 지칭한다.
  • 따라서, 아래와 같이 기술될 수 있다.

하나의 클래스은 오직 하나의 Actor에 대해서만 책임을 갖는다.

  • 단일 Actor를 책임지는 코드는 응집성(Cohesion)으로 묶인다.
    • 서로 다른 Actor가 의존하는 코드가 있다면, 코드를 분리하도록 한다.

OCP: 개방-폐쇄 원칙

정의

"You should be able to extend a classes behavior, without modifying it."

클래스의 행위는 확장할 수 있어야 하지만, 이때 개체를 변경해서는 안 된다.

  • Open for Extension, 확장에는 열려있어야 하지만
  • Closed for Modification, 변경에는 닫혀있어야 한다.

변경을 최소화하려면?

  • 서로 다른 목적으로 변경되는 요소를 적절하게 분리한다. (SRP, 단일 책임 원칙)
    • 목적 : 변경의 이유, Actor의 요구사항
    • 요소 : 클래스의 내용
  • 변경되는 요소 사이의 의존성을 체계화한다. (DIP, 의존성 역전 원칙)

Best Practice

  • 변경을 최소화하기 위하여 책임을 분리(SRP)하고 행위가 확장될 때 변경이 발생하지 않도록(OCP) 처리 과정을 클래스 단위로 분할하고, 클래스는 컴포넌트 단위로 분리한다.
    • ﹦ (이중선) : 컴포넌트 단위
      • 화살표와 오직 한 방향으로만 교차한다. (컴포넌트 관계는 단방향으로만 이루어진다.)
    • → (열린 화살표) : 사용
      • A → B: A가 B를 호출한다.
    • ⇾ (닫힌 화살표) : 구현 혹은 상속
      • A ⇾ B: A가 B를 상속(혹은 인터페이스를 채택)하여 수직(혹은 수평)확장을 한다.

  • 화살표는 변경으로부터 보호하려는 컴포넌트를 향하도록 그려진다.
    • View에서 발생한 변경으로부터 Presenter를 보호하고자 한다.
    • Presenter에서 발생한 변경으로부터 Controller를 보호하고자 한다.
    • Interactor는 다른 모든 것에서 발생한 변경으로부터 보호하고자 한다.
  • 컴포넌트 계층구조를 이와 같이 조직화하면 저수준 컴포넌트에서 발생한 변경으로부터 고수준 컴포넌트를 보호할 수 있다.

인터페이스의 목적

방향성 제어 (의존성 역전)

  • FinancialReportGeneratorFinancialDataGateway<I>FinancialDataMapper

정보 은닉

  • FinancialReportRequester
    • Interactor 내부에 대해 너무 많이 알지 못하도록 막기 위해서 존재한다.
    • 없을 경우, Controller는 FinancialEntities에 대해 Transitive Dependency 를 가지게 된다.
    • Transitive Dependency을 가지게 되면, 소프트웨어 엔티티는 ‘자신이 직접 사용하지 않는 요소에는 절대로 의존해서는 안 된다’는 소프트웨어 원칙을 위반하게 된다.

LSP: 리스코프 치환 원칙

정의

"Derived classes must be substitutable for their base classes."

클래스는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 치환할 수 있어야 한다.

  • 사용자(Billing)의 행위가 사용하는 타입(License<Interface>)에 의존하지 않고 있어, 하위 타입들(PersonalLicense, Business License)로 치환할 수 있다.
    • 타입에 의존하게 된다면, 타입을 치환할 수 없게 된다.

ISP: 인터페이스 분리 원칙

정의

"Make fine grained interfaces that are client specific."

클라이언트별로 세분화된 인터페이스를 만들어라

  • 오퍼레이션을 인터페이스 단위로 나눠라.
  • 오퍼레이션을 인터페이스 단위로 나누지 않는다면,
    • 무관한 오퍼레이션을 변경하더라도 필요치 않은 배포가 필요로 하다.
    • 필요 이상으로 많은 것을 포함하는 모듈에 의존하는 것은 불필요한 컴파일·배포를 강제한다.

DIP: 의존성 역전 원칙

정의

"Depend on abstractions, not on concretions."

구체화(Concretion)에 의존하지말고, 추상화(Absraction)에 의존하라.

  • 추상화에 의존하며, 구체화에 의존하지 않는 시스템을 유연성이 극대화된 시스템이라고 한다.
    • 구체화에 의존하는 경우(String)가 존재하지만, 이는 변경되는 일이 거의 없고 엄격하게 통제된다.
    • 안정성이 보장되어진 환경에 대해서는 구체화에 대한 의존성을 용납한다.
    • 변동성이 큰 구체화에 대한 의존을 피하도록 한다.
    • 인터페이스는 구현체보다 변동성이 낮다.
  • 안정된 소프트웨어 아키텍처란 변동성이 큰 구현체에 의존하는 일은 지양하고, 안정된 추상 인터페이스를 선호하는 아키텍처이다.

실천법

  • 변동성이 큰 구현체를 참조하지 않는다.
    • 인터페이스를 참조하라.
    • 객체 생성을 Abstract Factory를 사용하도록 강제한다.
  • 변동성이 큰 구현체로부터 파생하지 말라.
    • 수직 확장을 지양하라.
    • 변경이 더욱 어려워진다.
  • 구체함수를 overried 하지 말라.
    • 소스코드 의존성을 제고할 수 없고, 상속하게 되버린다.

Abstract Factory

  • 객체를 생성하려면 해당 객체를 구체적으로 정의한 코드에 대하여 소스코드 의존성이 발생하니, 이때 사용할 수 있도록 한다.

  • ApplicationService 인터페이스를 통하여 ConcreteImpl를 사용한다.
    • ConcreteImpl의 인스턴스 생성이 필요하다.
    • ConcreteImpl에 대한 소스코드 의존성이 발생하는 것을 막기 위하여 ServiceFactory를 이용한다.
  • 소스코드 의존성은 곡선(아키텍처의 경계)과 교차할 때 모두 Absract Component로 향한다.
    • Abstract Component : 고수준의 비즈니스 로직
    • Concrete Component : 비즈니스 로직을 다루기 위한 세부사항들
  • 의존성은 제어흐름과 반대방향으로 역전되어 흐른다. 이를 Dependency Inversion(의존성 역전)이라 칭한다.
profile
🧑🏻‍💻 iOS Developer @TOSS

0개의 댓글