본문 바로가기

SearchDeveloper/클린 아키텍처

[7] 20~23 업무규칙 & 아키텍처

23.09.23

[20] 업무 규칙

애플리케이션 = 업무 규칙 + 플러그인

  • 업무 규칙 = 엔티티 + 유스케이스

업무 규칙이란?

사업적으로 수익을 얻거나 비용을 줄일 수 있게 하는 규칙. 컴퓨터상으로 어떻게 구현하는지 또는 규칙을 자동화하는 시스템과는 상관없다.

ex. 대출에 N% 의 이자를 부과한다.

업무 규칙은 대출 잔액, 이자율, 지급 일정 같은 핵심 데이터와 본질적으로 결합되어 있기 때문에 엔티티라는 객체 형태로 관리하기 좋다.

엔티티

: 컴퓨터 시스템 내부의 객체로서, 핵심 업무 데이터 기반으로 동작하는 조그만 핵심 업무 규칙을 구체화한다.

-순전히 업무에 대한 것이어야 한다.

-이런 핵심적인 개념 구현하는 소프트웨어는 한 데 모으고, 세부 규칙(DB, 사용자 인터페이스, 서드 파티 프레임워크) 과는 독립적이어야 한다.

유스케이스

: 엔티티의 핵심 업무 규칙을 언제, 어떻게 호출할지 명시하는 규칙을 담는다.

-그렇다하더라도 입출력 인터페이스가 웹인지, 콘솔인지, API 인지에 대한 정의는 하지 않는다. 사용자와 엔티티 사이의 상호작용을 규정하는거지, 시스템에서 데이터가 들어오고 나오는 방식과는 무관하기 때문이다.

  • 요런 느낌

-엔티티는 고수준이고 유스케이스는 저수준이다. 유스케이스가 입출력과 더 가깝기 때문이다. 그래서 엔티티는 유스케이스에 알지 못하며, 유스케이스는 엔티티에 대해 알고 있다.

-요청, 응답 모델 또한 HttpRequest 같은 입출력 인터페이스와 종속되지 않는다. 만약 한데 모으면 후에는 결국 다른 이유로 인해 변경 될 것이고 공통 폐쇄 원칙, 단일 책임 원칙을 위배하기 때문이다.

업무 규칙을 표현하는 코드는 반드시 시스템의 심장부에 위치 해야 하며, 덜 중요한 코드는 심장부에 플러그인돼야 한다.
업무 규칙은 시스템에서 가장 독립적이며 가장 많이 재사용할 수 있는 코드여야 한다.

 

[21] 소리치는 아키텍처

상위 수준 디렉토리, 패키지 소스코드를 봤을 때 “헬스 케어 시스템이야", “재고 관리 시스템이야" 처럼 유스케이스를 먼저 파악할 수 있어야 한다. “스프링이야”, “레일스야” 같이 프레임워크나 세부사항이 아니라

  • 좋은 아키텍처는 유스케이스가 중심이 되어야 한다.
  • 프레임워크, DB, 웹서버는 사용하기 위한 도구일 뿐이므로 결정을 미룰 수 있어야한다. 웹도 입출력의 한 종류이므로 마찬가지
  • 테스트할 때도 웹서버나 여타 세부사항이 반드시 필요한 상황이 되면 안 된다.

 

[22] 클린 아키텍처

헥사고날 아키텍처, DCI, BCE 같은 아키텍처들의 목표는 모두 관심사의 분리이다.

최소한 업무규칙 하나와 사용자↔시스템 인터페이스 하나를 포함한다.

그리고 아래와 같은 특징을 갖는다.

  • 프레임워크 독립성: 아키텍처는 프레임워크 존재 여부에 의존하지 않는다.
  • 테스트 용이성: 업무 규칙은 UI, DB, 웹 서버 등 외부 요소가 없이도 테스트할 수 있다.
  • UI 독립성: 업무규칙에 영향 받지 않게 UI 를 변경할 수 있다.
  • DB 독립성: 업무규칙에 영향 받지 않게 DB 종류를 교체할 수 있다.
  • 모든 외부 에이전시에 대한 독립성: 업무규칙은 외부 세계 인터페이스에 대해 전혀 알지 못한다

 

아키텍처의 아키텍처 느낌. 아키텍처 전부를 이 다이어그램으로 설명하는 의도

가장 중요한 규칙: 의존성 규칙 (소스 코드 의존성은 반드시 안쪽으로, 고수준의 정책을향해야 한다.)

엔티티

전사적인 핵심 업무 규칙

-가장 고수준인 규칙을 캡슐화한다.

유스케이스

애플리케이션에 특화된 업무 규칙

-이 계층에서 발생한 변경이 엔티티에 영향 주면 안 된다.

-같은 맥락으로 프레임워크같은 외부 요소의 변경이 유스케이스에 영향을 주면 안 된다.

-운영 관점에서 애플리케이션이 변경되면 유스케이스가 영향을 받는다.

인터페이스 어댑터

업무규칙(엔티티, 유스케이스)에 가장 편리한 방식에서 웹 같은 외부 세계에 편리한 방식으로 변환

-업무규칙에 가장 편리한 형식에서 DB 한테 가장 편리한 방식으로 변환

  • 하지만 어떤 부분도 DB 에 대해 알아서는 안 된다.

-MVC 계층 포함. ex) 컨트롤러, 뷰, 프레젠터

  • 요청이 들어오면 컨트롤러 → 유스케이스 / 유스케이스 → 프레젠터, 뷰 로 이동

프레임워크 & 드라이버

DB 나 웹 프레임워크 같은 도구들로 구성

모든 세부사항이 위치하는 곳

의존성 규칙(저수준→고수준) 을 지키며 경계를 횡단하는 방법

내부 원은 외부 원에 대해 몰라야하므로,

내부가 외부를 사용하고 싶으면 아래와 같은 방법으로 한다.

  • 내부에서는 내부에서 만든 인터페이스 호출
  • 인터페이스 구현은 외부에서

시나리오 (웹 기반, DB 사용)

  1. 요청이 컨트롤러로 들어오면 pojo 객체를 Input Boundary 인터페이스 메소드 호출을 통해 Use Case Interactor 로 전달
  2. Use Case Interactor 가 Data Access 인터페이스 통해 DB 호출해서 Entity 랑 엮어서 Output Data 생성
  3. Presenter 가 Output Data 받아서 View 에 보여줄 수 있게 데이터 재가공하여 View Model 에 넣어줌

화살표는 저수준에서 고수준으로 항상 향하고 있다!

 

[23] 프레젠터와 험블 객체

※ humble: 겸손한, 보잘 것 없는, humble object: 대강 만든 객체

험블 객체 패턴

목표: 테스트를 쉽게 할 수 있도록 하자

어떻게:

  • 테스트하기 어려운 행위와 쉬운 행위를 분리한다.
  • 테스트하기 어려운 행위를 험블 객체로 옮긴다. 나머지 모듈엔 테스트 하기 쉬운 객체를 옮긴다.

GUI

  • GUI 에서 화면의 각 요소가 적절한 위치에 표시되었는지 테스트하는 건 어려움 → 뷰(험블 객체)
  • GUI 에서 수행하는 행위는 테스트하기 쉬움 → 프레젠터

로 분리. 서로 다른 클래스로 생성

-프레젠터: 앺에서 데이터 받아 화면에 표현할 수 있는 포맷으로 변경한다.

-뷰: 프레젠터에서 받은 데이터를 GUI 에 표현하지만 데이터 처리는 하지 않게 한다.

ex. 프론트 안에서 프레젠터와 뷰로 나누는 사례인듯. vue 에서 메소드는 프레젠터, 위에 뿌려주는곳이 뷰

(사례1) 날짜 표시하기

- 앺 → Date 객체 → Presenter

-Presenter → 적절한 포맷의 날짜 문자열 → View Model(데이터 구조)에 담기

-View ModelView 가 날짜 문자열 찾아서 뿌려줌

(사례2) 금액 표시하기

-앺 → Currency 객체 → Presenter

-Presenter → 소수점, 통화 표시된 금액 문자열 → View Model(데이터 구조)에 담기 (음수면 false 값도)

-View ModelView 가 날짜 문자열 찾아서 뿌려줌

(사례3) 버튼 이름, 비활성 여부 들도 모두 프레젠터에 의한 뷰 모델에서 찾고 뷰는 뿌려주기만 한다.

데이터베이스 게이트웨이

: DB가 수행하는 CRUD 메소드가 포함된 인터페이스 (유스케이스 인터렉터 ↔ DB Gateway ↔ DB)

유스케이스는 테스트 하기 쉽다. DB 게이트웨이를 스텁이나 테스트 더블로 교체해서 테스한다.

데이터 매퍼

DB 테이블에서 가져온 데이터를 데이터 구조에 담아주는 클래스

(ORM 으로 불리는. Object 는 데이터는 private 으로 볼 수 없으므로 객체가 아닌 데이터 구조(public 데이터 변수)집합이 맞다고 하며 데이터 매퍼라고 불리는 게 맞다고 저자는 본다.)

ORM 시스템은 DB 계층에 존재한다.

  • DB 게이트웨이 인터페이스 ↔ 데이터 매퍼(험블 객체) ↔ DB

서비스 리스너

애플리케이션이 다른 서비스와 통신해야할 때

서비스 인터페이스 ↔ 서비스 리스너가 수신하고 데이터 구조 포맷 변경

→ 이 데이터 구조가 험블 객체?

 

레퍼런스

클린 아키텍처 20~23장