Clean Architecture : Part 1 — Database vs Domain

JustWrite
28 min readMay 15, 2018

--

이 문서는 “Clean Architecture : Part 1 — Database vs Domain” 을 번역한 문서입니다. 저자의 동의를 얻어 올립니다. 의역이 심할 수 있으니 주의를 ...

클린 아키텍처에 대해서 들어본적 있나요? 이 주제는 여전히 뜨거운 감자인 주제입니다(SW 아키텍처는 언제나 뜨거운 감자죠). 더욱이 Uncle Bob 의 클린 아키텍처란 책 때문에 그 관심은 더 증가할 것으로 생각되요 (2017년에 책이 나왔습니다. 아직 한서가 없지만 이미 Uncle Bob 이 다른 책이나 글로 얘기한 것들의 집대성한 정도라 생각하면 될 것 같습니다. 그렇지만 사서 읽는 것을 추천드립니다). 많은 프로그래밍 언어를 통해 클린 아키텍처 개념이 구현되어있습니다 (Clean Architecture 와 본인이 사용하는 언어를 함께 입력해서 구글링하면 예가 많이 나옵니다). 전 몇 년 전 개인적으로 프로젝트를 scaling-up 할 때 맞닥뜨린 문제를 경험하면서 클린 아키텍쳐에 관심을 가지기 시작했죠. 이걸 계기로 SW 아키텍처를 깨끗하게 하는 많은 것들을 공부도 하고 찾기도 했습니다. 클린 아키텍처에 대한 깊이를 더할 수록 이것이 얼마나 유용한지 그리고, 여러 프로젝트에 이를 적용하는 것이 얼마나 가치가 있었는지 알게 되었죠. 다만 클린 아키텍쳐에 대해 몇몇 가지는 동의하지 않는 부분도 있는데 이는 아마 제가 클린 아키텍처를 다 이해하지 않아서 그런 걸지도요.

몇해 전부터 이런 글을 쓰고 싶었지만 기회가 닿지않아 쓰질 못했죠. 이 글에서 설명하는 것을 무조건 따라야 하는 것은 아니구요. 단지 클린 아키텍쳐에 대한 나의 해석일 뿐 그 이상도 아니라는 점을 말하고 싶내요. 만약 다른 관점을 가지고 있다면 그 부분에 대해서 함께 논의하면 좋을 것 같아요. 그리고, 이 글은 How-To 를 전달하지는 않아요. 그저 당신이 SW 아키텍처를 생각하는 방식을 살짝 바꾸려는 것이 이 글의 의도입니다.

전 이 글을 통해 안드로이드 상에서의 클린 아키텍처 개념과 이를 적용한 경험을 공유하려고 해요. 이 주제는 여전히 논의가 활발히 되고 있지만 꽤 오래전에 제안이 되었어요.

Introduction

처음에 이 글을 안드로이드 클린 아키텍쳐라고 했었는데요. 생각해 보니 이 글에 설명한 대부분의 기술과 아이디어는 다른 언어 또는 프레임워크를 사용해 구현이 가능하기에 지금처럼 클린 아키텍쳐로 제목을 바꿨어요. 핵심은 가장 중요한 개념을 설명하는 거에요. 그래서 많은 코드 예제가 포함되지는 않을 거에요. 이 글은 이론적인 그리고 핵심 아이디어와 실천에 관련된 부분이 더 초점을 두고 있어요. 실제 프로젝트를 예로 들어 설명하는 건 다른 글에서 다루려고 해요. 링크를 이 글에 곧 추가할 수 있기를 바래요.

A little bit of Clean Architecture Hitory

그럼 우선 클린 아키텍처의 역사를 시작으로 여행을 떠나 보겠습니다~! 모든 것은 저명하신 Uncle Bob 이 쓴 글에서 시작했습니다. 이 분을 당연히 알거라 생각됩니다. 밥은 엄청난 책들을 많이 썼는데요. 애자일 프랙티스와 관련된 엄청난 책들을 썼구요. 클린 코드, 객체 지향 설계 그리고 다른 주제들에 대한 것들도 많이 출판했어요. 뭐 많은 논쟁거리가 있습니다만 모두다 읽어볼 가치는 충분히 있어요.

“The Clean Architecture” 라는 이름의 글은 2012년 8월 13일에 세상에 나왔습니다. 네, 5년 전입니다. 글은 상대적으로 짧지만 SW 업계에 큰 파장을 불렀죠. 아래는 중요한 부분에 대한 요약입니다.

  • 프레임워크로 부터 독립해라! 아키텍쳐는 프레임워크에 의존하면 안된다! 최소한의 노력으로 새로운 프레임워크를 적용할 수 있어야 한다.
  • 세부 구현으로 부터 독립해라! SW 시스템의 핵심 모듈은 UI, 데이터베이스, 프레임워크, 라이브러리 등의 변경에 영향을 받지 않아야 한다.
  • 의존성 규칙! 내부 레이어는 상위/외부 레이어의 어떤 것도 알아서는 안된다. 그 결과로 모든 의존성은 안(내부 레이어)를 향해야 한다.
  • Entity 와 Use Case!! Entity 와 Use Case 가 어플리케이션의 핵심이다. 아키텍처에 대해서 얘기할 때 이 것들이 존재하는 레이어가 가장 중요하다. 그리고 이 레이어는 세부 (구현) 사항을 담고 있는 레이어의 변경에 영향을 받지 않아야 한다.
  • Adapter 와 Converter 의 필요성. Adapter 와 Converter 는 각각의 레이어가 협력을 할 때 레이어 내부의 세부 사항이 다른 레이어로 전파되지 않도록 하는 역할을 한다. 즉 각각의 레이어가 필요로 하는 그들만의 데이터 모델을 외부 레이어로 전달하거나 외부 레이어에서 받아 올 때 외부 또는 내부 레이어의 데이터 모델의 격리를 위해서는 외부 레이어의 데이터 모델을 내부 레이어의 데이어 모델로 변환하는 그리고, 내부 레이어의 데이터 모델을 외부 레이어의 데이터 모델로 변환하는 기능을 담당하는 녀석들이 필요한데 이 두 녀석이 Adapter 와 Converter 이다. 이를 통해 얻을 수 있는 것은 적절한 의존성이다. 이 둘이 레이어간 완충재 역할을 한다고 보면 되겠다. 그리고 이 녀석들을 통해 DDD에서 말하는 Bounded Context 가 좋은 형태로 구현된다고 볼 수도 있겠다.
  • 의존성 역전의 법칙!! 이 법칙은 고수준의 모듈이 저수준 모듈에 의존성을 가지면 안된다는 것을 말한다. 양쪽 모듈 모두 추상화에 의존해야 한다고 말하는 거다. 그리고 추상화 모듈 또한 세부 사항에 의존을 하면 안된다는 것을 말하거구. 즉, 세부 사항이 추상화된 모듈에 의존성을 가져야 한다는 거다. 이 법칙은 클린 아키텍쳐에서만 유별나게 중요한게 아니다. 이 법칙은 소프트웨어를 깨끗하게하는 일반적인 실천 방법 중의 하나이기에 그냥 중요함.
  • 경계(boundary) 간의 데이터 전달. 레이어간 객체를 전달하는 것에는 항상 주의를 기울여야 한다. 전달되는 객체는 항상 고립되어야 함. 즉, 언어의 기본 형태를 사용해야 하고 감춰진 의존성을 가지면 안됨. ORM (Object Relational Mapping) 라이브러리 사용해봤다면 알걸? 데이터 레이어에 정의된 ORM 객체가 전 레이어를 돌아다님. NSManagedObject 가 대표적임.

밥의 클린 아키텍처라는 글이 세상에 나온지 몇년 후 이 아이디어는 많은 논의와 글의 토대가 되었죠. 개인적으로 가장 가치있다고 생각되는 글은 Fernado Cejas 의 “Architecting Android…The clean way?” 라는 글인 것 같아요. 이 글은 밥이 설명한 클린 아키텍쳐를 안드로이드에 적용했어요. 이 글은 엄청난 github issue 들을 만들어 냈고 가치있는 정보들 또한 많이 만들어 냈어요. 읽어보는거 추천해요.

밥은 좋은 글을 쓰느라 시간을 많이 쓰지는 않았어요. 여기 “A Little Architecture” 라는 괜찮은 내용이 있는데 인터뷰 형식이내요. 이 글도 읽어보면 좋아요. 추천요~! 끝으로 밥은 “Clean Architecture: A Craftsman’s Guide to Software Structure and Design” 라는 책을 쓰고 있어요. 아마 이 책이 끝판 왕이 되지 않을까해요. (이미 출판 완료!)

구글링하면 클린 아키텍처에 대한 다른 좋은 글도 찾을 수 있어요. 클린 아키텍처에 대한 구현 예는 거의 모든 프로그래밍 언어로 있는거 같아요. iOS 는 VIPER 라는 아키텍쳐가 있는데 이게 좀 괜찮아요. MVC (Model View Controller 지만 여기서는 Massive View Controller ) 를 대체할 수 있어요. iOS 에서 개발을 한다면 “The Book of VIPER” 라는 글을 읽어보길 추천!!

Why should I care about an architecture?

이 질문을 본인에게 해본적이 있던가 아니면 동료에게 들었던 적이 있을 것같내요. 답을 찾기 위해서는 아래 질문에 답을 스스로 해볼 걸 추천해요.

  • 왜 이렇게나 많은 프로그래밍 언어와 프레임워크가 있는거지?
  • 누구에게 고수준 프로그래밍 언어가 필요하지?
  • 기계는 저수준 어셈블리 보다 고수준 언어를 선호하나?
  • 당신의 프로젝트에서 정말 작은 수정인데 두려움을 가졌던 적이 있는가?
  • 당신이 작성한 시간이 좀 지난 코드를 다시 이해하기위해 얼마나 많은 시간을 소비했는가?
  • 새로운 동료에게 당신의 프로젝트의 아키텍쳐를 설명하기 위해 얼마나 많은 시간이 필요한가?

이 질문을 통해 전달하고 싶은 내용은 개발자인 우리에게만 고수준 언어, 프레임워크, 문법적 설탕 (syntactic sugar), 한줄로 된 해결책 (one-line solutions) 이 필요하다는 거에요. 그리고, 이 것들은 모두 언어의 구조를 기억하기 쉽고 편하게 하기 위한 거구요. 기계는 사람이 읽기 편한 코드와는 아무런 관련이 없어요. 그런데 왜 우리는 이런 것들을 가지고 있음에도 제대로 사용하지도 못하고, 소위 스파게티 코드나 큰 진흙 덩이 (밀가루 반죽이 더 나아보이는데 ㅎ) 같은 코드를 만들어 내는 걸까요?

때때로 우리들은 정말로 요구사항을 클리어하고 데드라인을 맞출 필요가 있어요. 그런데 이런 경우에도 우리는 프로젝트가 적어도 좋은 구조, 읽기 쉽고 확장하기 쉬운 형태의 코드를 가질 수 있도록 노력해야 해요. 다만 아키텍처 개발, 도메인 탐색, 큰 도메인을 작은 컨텍스트나 레이어로 분리하는 것들에 시간을 소비하는 것이 좋지 않은 아래와 같은 경우들이 있어요.

  • 뚜렷한 아키텍쳐나 도메인이 없는 경우. CRUD (Create, Read, Update, Delete) 정도만 필요한 응용과 같이 SW가 간단/단순한 경우거나 정말로 요구사항 자체가 적거나 한 경우에요.
  • 스크립트 또는 일련의 명령어 꾸러미로 충분한 경우. 간단한 스크립트, 배쉬(bash) 명령어 꾸러미 등과 같은 거요.
  • 소스 코드가 어플리케이션 크기가 제약인 경우. 관심의 분리 (Separation of Concerns), DDD, SOLID, 추상화 같은 것들이 모두 코드 크기를 증가시키는 오버헤드로 작용하는 경우에요. 이런 경우는 쉽게 만날 수 없어요. 모두다 시스템 제약을 많이 가지는 임베디드 시스템이거든요. 이들 시스템은 성능과 강건성을 최우선으로 치죠. 때문에 고수준의 무언가가 필요가 없는 경우죠.
  • 최초 시도인 경우. 프로젝트에 강력한 시간 제약이 있는 경우 “클린 아키텍쳐” 는 끔찍한 상황을 초례할 수 있어요. 기능적 요구사항의 개발도 진행이 잘안되는데 다들 더 나은 구조는 무엇인지에 대해서 시간을 소비하고 있다고 생각해 보세요. 끔찍하지 않겠어요?

그러나 대부분의 아키텍처적인 원칙과 좋은 실천방법들 이해하고 적용하려는 노력은 우리가 더 나은 어플리케이션을 만들 수 있게 도와줄뿐만 아니라 우리들 스스로를 더 가치있게 보이게 만들어요. 그리고 다른 사람들의 다양한 접근법을 살펴보는 것은 우리가 SW의 컨텍스트를 더 잘 이해할 수 있도록 돕고 보다 중요한 점은 아키텍쳐를 특정 요구사항에 맞게 조정할 수 있게 돕는 거에요. 그리고 그 결과로 우리들은 중요한 SW 아키텍쳐 결정을 할 수 있게되죠.

클린 아키텍처를 사용하면서 얻을 수 있는 모든 이점과 시험성(testability), 확장성(scalabilit) 등의 관련 원칙을 나열하지는 않을 거에요. 다만 견고한 기반과 같은 좋은 SW 아키텍처는 어플리케이션을 확장하기 쉽고, 테스트하기 쉽게 할 뿐만 아니라 전체 시스템 손상 없이 시스템 장애 및 개발자의 실수를 줄여주죠.

Architecture Evolution

왜 클린 아키텍쳐가 기존의 아키텍쳐보다 좋은지 (또는 나쁜지) 를 이해하기 위해서는 다양한 SW 아키텍처가 어떻게 진화했는지 살펴볼 필요가 있어요. 가장 유명한 아키텍쳐 그룹 두개 정도를 살펴볼게요.

잘 알려진 아키텍처를 두 개 그룹으로 나누면 아래와 같아요.

  • 데이터베이스 중심 아키텍처
  • 도메인 중심 아키텍처

각 그룹를 한번 살펴보자구요. 그런데 “데이터베이스 중심” 이 “데이터 중심” 또는 “데이터 주도 (data driven)” 과는 다르다는 걸 분명히 하고 싶내요. 데이터베이스 중심은 모든 것들이 데이터베이스를 중심으로 그 주변에 있다는 거에요. 그런데 데이터 중심 또는 데이터 주도는 모든 유즈 케이스와 상호 작용이 데이터에 의해 유도된다는 것을 말해요. 말그대로 내가 풀고자 하는 문제의 영역에 존재하는 데이터를 중심으로 관련된 모든 것을 유도한다는 거죠. 예외처리와 같은 부분을 제외하면 SW 는 모두 어떤 종류의 데이터와 함께 동작하는 하니까요. 그래서 우리가 일하는 필드를 IT (Information Technology) 라고 부르는 거구요.

Database-centric Architecture

첫 번째 그룹은 데이터베이스 중심 아키텍처에요. 이런 유형의 아키텍처가 최초의 SW 아키텍처이지 않을까 해요.

데이터베이스 중심의 원칙은 어플리케이션과 시스템을 개발할 때 여전히 광범위하게 사용되요. 아직도 운영 중인 레거시 시스템이 많이 있고, 확장과 유지보수가 정말 어려워요. 그런데, 더 중요한 것은 3-레이어, N-티어와 같은 아키텍처를 여전히 대학교에서 가르치고 있고, 심지어 이 아키텍처가 SW 시스템을 개발할 때 사용할 수 있는 적용 가능한 유일한 아키텍처인 것 처럼 가르친다는 거에요.

티어 vs 레이어
같은 수준에서 얘기를 진행하려면 SW 개발에서의 레이어와 티어의 차이를 이해해야 해요.

레이어는 논리적인 관심의 분리에요. 우리는 코드의 책임(responsibility)에 따라 몇 개의 레이어로 나눌 수 있어요. 그리고, 각 레이어의 통신을 위해 프로토콜을 정의하죠. 그 결과로 전체 시스템에 영향을 끼치지 않으면서 특정 컴포넌트를 다른 녀석으로 교체할 수 있어요. 3-레이어 아키텍쳐는 보통 프리젠테이션, 비지니스 그리고 데이터 레이어로 보통 이루어져요. 이 세가지 레이어를 한 기계에서 돌릴 수도 있고, 아니면 프리젠테이션 레이어를 몇 개의 다른 물리적인 기계에서 돌릴 수도 있어요. 예를 들면 한 기계에서는 웹사이트를 다른 기계에서는 API 게이트웨이 같은 형태로 말이죠.

티어는 반면에 코드, 모듈 그리고 컴포넌트의 물리적인 구성에 대한 것이에요. 하나의 티어는 배치(depolyment) 유닛에 해당해요. 그런데, 요즘에는 N-티어 어플리케이션이 N 개의 서로 다른 물리적인 서버에서 실행된다는 것을 의미하지는 않아요. 고맙게도 가상화라는 기술이 하나의 기계에서 N-티어 어플리케이션의 각 티어를 서로 다른 가상의 환경에서 실행하는 것을 가능하게 해주거든요. Docker, KVM, LXC 같은 것들을 들어 봤을 거에요.

개인적으로는 3-레이어 아키텍쳐가 이미 증명이된 아키텍쳐고, 강건성과 확장성이 좋기 때문에 사용하는 것이 당연하다고 생각해 왔었어요. 프로젝트를 시작할 때 특정 도메인을 위한 Relation Modeling 을 가장 첫 번째로 할일로 등록하고 DBMS 를 사용해 테이블과 테이블간 관계(Relation)를 생성하고 트리거와 도메인의 비지니스 로직을 구현하는 스토어드 프로시져를 생성하고 했던 것이 기억나내요.

모든 것은 비교를 통해 판단이 되어야 하기에 3-레이어 아키텍쳐가 최악은 아니에요. 이건 SW 시스템의 유연성과 확장성을 얻기위한 관심을 분리하려는 시도였죠. 이상적으로 3-레이어 아키텍처의 각 레이어는 다음의 책임을 가져야해요.

  1. 프리젠테이션 레이어: 이 레이어의 주된 임무는 UI 를 통해 어플리케이션의 기능을 전달하는 것이에요. 웹 페이지 형태일 수도 있고, 네이티브 윈도우 시스템을 사용해서 구현될 수도 있어요. 이 레이어는 다른 레이어에 비해 복잡한 일은 하지 않아야해요.
  2. 비지니스 레이어: 이 레이어는 비지니스 로직과 어플리케이션 로직이 있는 곳이에요. 그리고, 3-레이어 아키텍처의 경우 이 레이어는 프리젠테이션 레이어에 독립적이어야 해요. 그런데 여전히 데이터 레이어에는 명시적인 종속성을 가져요.
  3. 데이터 레이어: 비지니스 레이어에서 발생하는 요청을 처리하는 가장 아래 레이어 (데이터 레이어) 는 일반적으로 이러한 요청을 하부의 데이터 스토리지에 대한 쿼리로 변환해요. 그리고, 이 레이어는 비지니스 룰과 어플리케이션 로직과 종속되지 않는 추상화된 데이터 리포지토리 인터페이스를 제공해야 하죠.

이 아키텍처는 데이터베이스에 가장 큰 노력을 들여요. 데이터 레이어가 어플리케이션의 가장 가치있는 부분으로 간주되죠. 그래서 종종 데이터 레이어와 비지니스 레이어가 강하게 결합해요. 이 부분 때문에 이 둘을 분리하는 것 보다 전체 어플리케이션을 다시 작성하는 것이 훨씬 쉬운 상황이 만들어지기도 해요. 여기에 그런 예가 있어요. 정말 끔찍해요.

이 코드가 가지고 있는 많은 잘못된 부분들이 보였으면 좋겠어요. 이 코드는 심지어 3-레이어 아키텍처를 만족하지도 않아요. 이 예는 데이터 레이어가 없어요. 그냥 바로 데이터에 접근해 버리죠. 이런 모든 문제에도 불구하고 종속성은 여전히 비지니스 레이어에서 데이터 레이어로 향하고 있어요.

모든 아키텍처에서 한가지 필수적인 것은 각 레이어가 다른 레이어를 어떻게 의존하는지를 보여주는 의존성의 방향에 대한 부분이에요. 이런 의존성 화살표가 실제 코드에서는 어떤 의미가 있는지 설명해볼게요. 프리젠테이션 레이어는 비지니스 레이어에 있는 클래스나 모듈을 “import” 해요. 그리고, 비지니스 레이어는 데이터 레이어에 의존성을 가져요. 프로그래밍 언어에서 의존성은 주로 using, import, include, require 와 같은 구문을 통해 나타나요.

더 나쁜 경우에는 프리젠테이션 레이어가 transitively 나 명시적으로 데이터 레이어에 종속성을 가져요. 예를 들면 이런 문제는 ORM 을 사용할 때 종종 발생하는데 동일한 모델 클래스를 다양한 레이어에서 재활욜 할 때 발생해요.

3 개의 레이어 말고도 비지니스 룰, 인프라 서비스 등을 포함하는 레이어가 더 있다고 생각해봐요. 불행하게도 이 레이어 역시 데이터 레이어를 가리킵니다. 다음 그림과 같이 표시할 수 있어요.

또한 앞서 언급했듯이 비지니스 레이어와 데이터베이스 레이어의 경계는 너무 얇기도 하고 깨지기도 쉽기에 비지니스 로직이 데이터베이스로 스멀스멀 녹아 들어 갈 수 있어요.

Domain-centric architectures

SW 아키테거의 차세대 진화 단계는 고전적인 SW 아키텍처 (예: 3-레이어) 의 가치를 바꾸는 것이었어요. 개발자의 초점은 데이터베이스 레이어에서 비지니스 레이어로 이동했고 일반적인 방법을 사용해 비지니스 문제를 해결하고 있어요. 왜 그럴까요? 이 질문에 대답하려면 다음의 질문 — 답변 리스트를 보는게 좋아요.

Changing priorities

Q: 왜 개발자들은 그들의 일을 가지고 있을까?
A: 왜냐하면 HW 에 의해 실행되는 SW는 일상적인 작업들을 자동화기 때문이죠.

Q: 프로세스 및 작업 자동화에 가장 관심이 많은 곳은 뭐지?
A: 비지니스

Q: 주된 목적은 뭐죠?
A: 비지니스 확장과 목표를 달성해서 경쟁력을 확보하고 돈을 버는 거죠.

Q: 어떻게 SW 가 이걸 도울 수 있죠?
A: SW는 특정 도메인의 비니지느 문제를 해결하고, 비지니스 흐름(flow)를 자동화하죠.

이 문답에 언급된 프레임워크나, DBMS, 툴을 찾아보겠어요? 오해하지는 말구요. 개발자로서 우리는 프로젝트를 완료하기 위해서는 편안하게 사용할 수 있는 많은 도구를 가지고 있어야해요. 그런데, 그 도구들은 명시된 일을 완료하는 게끔 도와주는 세부사항일 뿐이에요. (문답 어디에도 어떤 도구를 사용해야 한다는 내용은 나오지 않았어요.)

비지니스는 지속적으로 변화해요. 그리고 비지니스는 새로운 도전에 대한 빠른 응답과 비지니스 영역에서의 경쟁력을 계속 유지할 수 있는 능력을 필요로하죠. 그리고, 우리는 개발자로서 새로운 요구사항에 빠르게 대응할 수 있는 생산성이 필요해요. 이런 능력은 우리를 대체 불가한 전문가로 만들어 주죠.

그런데 이 경우를 생각해봐요. 당신은 모바일 개발자고 안드로이드 앱 개발을 하고 있어요. 보스가 갑자기 당신 방으로 들어와 가능한 빨리 iOS 앱을 개발해야 한다고 소리치고 있어요. 왜냐하면 경쟁자들이 막 iOS 용 클라이언트 어플리케이션을 출시 했거든요.

이젠 얼마나 빨리 새로운 요구사항에 대응하는지는 당신과 당신의 동료에게 달렸어요. 만약 당신이 iOS 앱을 개발하게 된다면, 중요한 질문은 iOS 앱에 모든 비지니스와 도메인 관련 사항을 개발하기 위해 얼마나 많은 시간이 필요한지에요. 개발자들은 iOS 앱을 개발하고, 코드와 전체적인 아키텍처를 이해하기 위해 시간을 소비하겠죠.

늘 그래왔듯 비지니스 로직은 어플리케이션의 모든 레이어에 걸쳐 존재하고, 네… 물론 레이어가 있다면요. 더 중요한 것은 UI 가 비지니스 룰로 가득 차있단는 사실 조차 이해하지 못할 수 있다는 거에요. 그래서 모든 비지니스 룰을 완전히 개발하기 위해서 일부 앱에 이미 구현된 것들을 퍼즐 조각을 맞추 듯 조립하고 도메인 핵심을 나타낼 수 있게 프레임워크, 데이터베이스 그리고 다른 세부사항들에 대한 부분을 걷어내죠. 그래서 왜 도메인 중심인가에 대한 대답은 이래요. 모든 도메인 룰을 한 곳에서 유지하면 모든 레이어가 비지니스 로직을 가지는 부담을 없앨 수 있고, 이로 인해 많은 시간을 절약할 수 있게되고 그 결과 생산성 향상이라는 결과를 만들 수 있다는 거죠.

더 많은 비지니스 룰이 추상화되고 세부 사항에서 분리될수록 기술적 관점에서 동일한 비지니스 룰을 가진 매우 다른 (더 나은) 어플리케이션을 더 쉽게 만들 수 있어요.

Converting a database-centric architecture into a domain-centric architecture

이 두 그룹의 아키텍처에 대한 차이점을 이해하기 위해서 얼마나 쉽게 데이터베이스 중심 아키텍처에서 도메인 중심 아키텍처로 변환할 수 있는지 보여줄게요.

물론 이 그림이 이전에 봤던 그림처럼 쉽지는 않아요. 하지만 여기서는 주요 아이디어만 강조하려고 해요. 아키텍처는 대개 표준 형식으로 사용되지 않고 특정 요구사항을 만족하도록 변경되기 때문에 훨씬 더 복잡해 질 수 있어요. 다만 주요 아이디어는 도메인 레이어가 외부의 세부사항을 향하는 그 어떤 종속성도 가지면 안된다는 거에요. 아래 두 가지 다른 코드 블록을 보면 좀더 명확하게 이해할 수 있을 거에요.

[Database-centric approach]

[Domain-centric approach]

PostsRepositoryContract 인터페이스는 도메인 레이어에 있어요. 그리고, 이 인터페이스를 구현하는 세부사항 (concrete class) 는 “@inject” 라는 annotation 을 통해 런타임에 주입되요. 안드로이드에선 사람들이 보통 Dagger 를 사용해서 Depdency Injection 을 해요. 이렇게 함으로써 도메인 레이어가 세부사항에 직접 접근하는 것이 아니라 세부사항이 도메인 레이어에 정의된 계약 (여기서는 인터페이스) 를 구현했다는 거에요. 결국 외부 레이어가 PostsRepositoryContract 를 사용하기 위해 도메인 레이어로 향하는 종속성을 가지게 되요.

import 구문을 유심히 보세요. 첫 번째 경우는 데이터 레이어에 있는 클래스를 포함했어요. 물론 이 클래스가 인터페이스면 더 좋아요. 하지만 이게 뭐 큰 차이를 주지는 않아요. 결국 도메인 레이어가 데이터 레이어로 향하는 직접적인 종속성을 가지는 것은 똑같으니까요. 그런데 도메인 중심 접근의 코드를 보면 다른 레이어로 향하는 종속성이 하나도 없어요. 대신 도메인 레이어가 게임의 룰을 명시하는 것이 큰 차이에요. 여기에서 게임의 룰은 PostsRepositoryContract 에요.

Domain Driven Design

도메인 주도 설계 원칙에 대해 정의한 최초의 아키텍트 중 한명은 에릭 에반스에요. 그가 쓴 책 (도메인 주도 설계) 는 세부 사항보다는 도메인 문제를 통해 유도되는 SW 를 설계하는 개념을 설명해요. 이것이 아키텍쳐는 아니지만 복잡한 도메인을 다루는 SW 시스템을 만들 때 고려해야 하는 충고와 원칙들을 설명해요.

이런 원칙들을 자세히 다루지는 않을 거에요. 이미 많은 글과 책들이 이 주제에 대해서 잘 설명해주거든요. 여기선 원칙들의 중요한 포인트를 강조하고 싶어요.

  • 제한된 컨텍스트 (Bounded Contexts) 전체 어플리케이셔 도메인 레이어는 몇개의 작은 도메인으로 나뉠 수 있어요. 각각의 작은 컨텍스트는 모델을 가지고 이 모델은 그 컨텍스트 안에서 의미를 갖죠. 그리고, 이들은 그들만의 경계를 가져요. 이런 서브 컨텍스트를 “제한된 컨텍스트 (Bounded Context)” 라고 불러요. 어플리케이션 도메인을 제한된 컨텍스트를 분리하면 유지보수성이 높아져요. 즉, 느슨한 결합과 재사용이 좋아져요. 예를 들어 도메인 전체에 여러개의 Customer 모델이 있을 수 있지만 모델 각각은 특정한 제한된 컨텍스트에 속해요. 왜요? 혹시 속성이 10개 이상인 모델 클래스를 사용해 본적있어요? 보통 이런 녀석들을 보면 유사한 이름을 가지는 속성을 가지고 있는 경우가 많죠. 그리고 이런 속성들이 함께 사용되기 보다는 특정한 경우 (조건에) 특정한 속성이 사용될 가능성이 매우 커요. 왜그럴까요? 도메인 관점에서 완전히 다른 녀석들을 하나의 모델 클래스에 뭉쳐놨기 때문이죠. “Context” 라고 불리는 클래스를 만들어서 도메인 전체에 사용해 본적 있으시죠? 그게 딱 이 경우에 들어맞는 (나쁜) 예죠. ( 의역에 의역이지만 원본 글이 내 기준으로는 정말 이상해서 마음대로 해석함)
  • 보편 언어 (Ubiquitous Language) 특정 비지니스 도메인의 모든 용어를 이해하는 것은 매우 중요해요. 왜냐하면 사용되는 문맥에 따라 그 의미가 다른 경우가 있기 때문이에요. 이 언어는 비지니스 도메인을 기반으로 해야 해요. 보편 언어는 당신(개발자)와 도메인 전문가 / 이해 관계자 간의 연결고리에요. 이는 프로젝트 기간 동안 모순을 제거할 수 있게 도와줘요.
  • 엔티티와 값 객체 (Entities and Value Objects) 엔티티는 라이프 사이클 식별자 및 비지니스 가치를 가지는 비지니 모델이에요. 일반적으로 도메인 전문가는 엔티티 측면에서 작업을 해요. 엔티티는 상태를 가지고 변화할 수 있어요. 즉, “mutable” 한거죠. 더욱이 엔티티는 유일해야하고 따라서 식별자를 사용해 비교할 수 있어요. 예를 들면 같은 물건을 주문하는 두개의 주문 요청이 있다고 했을 때, 비지니스 관점에서 이 두 주문 요청은 완전히 별개에요. 값 객체는 일반적으로 속성으로 사용되는 불변 (immutable) 객체에요. 그래서 속성을 비교해 동일성 여부를 테스트할 수 있어야 해요. 값 객체는 (@) 기호를 가지는 모든 문자열이 유효한 메일 주소는 아니다와 같은 제약을 가질 수도 있어요. 예를 들면 날짜, 가격, 포인트, 가충치와 같은 것들을 값 객체로 처리할 수 있어요. 반면 우편, 주문, 고객은 엔티티의 예가 될 수 있어요.
  • 집합체와 집합체 루트 일반적으로 여러 오브젝트는 전체로 간주되는 단일한 유닛을 형성할 수 있어요. 이는 DDD 에서 집합체 (애그리거트, Aggregate) 라고 불려요. 시스템 프로세스 (예: 리눅스의 프로세스) 가 그 예가 될 수 있죠. 프로세스는 보통 복수의 쓰레드와, 파일 디스크립터, 환경 변수, 메모리 영역과 관계되요. 그러나 프로세스 없이 존재하는 각각은 의미가 거의 없어요. 집합체 루트는 외부와 내부 집합체의 소통을 위한 엔티티에요. 이전 프로세스 예를 따르면 단일 쓰레드는 보통 프로세스 컨텍스트 내에서만 접근 가능해요. 이 경우 프로세스가 집합체 루트 (애그리거트 루트) 가 되죠.

모든 주요 아이디어는 비지니스가 문제 도메인을 바라보는 시각에 최대한 가까운 SW 아키텍처를 만드는 거에요. 이런 부분은 도메인 전문가와 동일한 시각으로 문제를 바라보게 하는데 도움이 되지만 SW 시스템을 쉽게 유지 관리하고 프로젝트의 50%를 다시 작성하지 않고도 SW 시스템이 도메인 룰 변경에 용이하게 대응하도록 유지하는 것이 더 중요해요.

The Hexagonal architecture

도메인 레이어가 코어로서 존재하는 가장 첫 번째 아키텍처는 육각구조 아키텍처에요. 중요한 몇 가지 원칙을 통합했어요. 이 아키텍처의 아이디어는 정말 쉽게 이해할 수 있어요. 도메인 레이어는 외부 세상 (세부 사항, 프레임워크, UI) 으로 분리되어야 한다는 것이 본질이에요. 그리고 그 결과 이 아키텍처는 외부 영역과 내부 영역으로 이루어진 두 영역을 가지죠.

  • 안과 밖 (Inside and Outside) 이 아키텍처가 정의하는 두 가지 영역이 있어요. 내부 영역은 모든 비지니스 로직, 도메인 룰, 도메인 객체, 집합체 미 컨텍스트를 포함해요. 이와는 대조적으로 외부 영역은 내부 영역이 필요로하는 것을 제공해주는 세부 사항, 구성 요소, 툴, 프레임워크를 포함해요. 두 지역은 어댑터와 포트를 통해 연결이 되죠.
  • 포트와 어댑터 어플리케이션 코어가 세부 사항과 종속성을 가지지 않도록 포트와 어댑터가 두 세계의 중재자 역할을 해요. 중재자는 물리적인 장치에 설치되어 장치를 과전류로 부터 보호하는 퓨즈와 같아요. 동일한 원칙이 여기에도 적용되는 거죠. 어플리케이션 코어를 견고하게 유지해 비지니스 로직이 외부로 흘러가는 것을 막죠. 이 아키텍처에서 어댑터는 도메인 코어에서 필요한 서비스 간 통신 브리지 역할을 해요. 그리고 포트는 API 와 같으며 오직 특정 어플리케이션의 기능이 외부에 노출되도록 하죠 (최소 권한 및 인터페이스 분리 원칙).
  • 의존성 화살표는 내부로 도메인 중심 아키텍처의 주요 아이디어는 다음과 같아요. 모든 세부 사항은 도메인 레이어에 있는 인터페이스의 형태를 따르는 필수 기능 구현을 바탕으로 어플리케이션의 핵심을 채워요.

가장 안쪽의 레이어를 도메인 레이어라고 말해요. 그러나 어플리케이션 레이어와 같은 다른 이름으로 불릴 수도 있어요. 더욱이 이 아키텍처는 두 개 이상의 레이어로 나누어질 수도 있지만 본질은 여전히 같아요. 두 개의 서로 다른 영역을 사용해 세부 사항 및 프레임워크를 도메인 룰에서 분리하고 도메인 레이어는 어플리케이션 코어와 통신하는데 필요한 인터페이스만을 제공하는 거죠.

그리고 항상 기억해야해요. 포트와 어탭터를 올바른 방식으로 설계하는 것을요. 그 것들이 최소한 사용가능 해야겠죠.

The Onion architecture

다음이자 마지막 아키텍처는 Onion 아키텍처에요. 아마 어디에서 이 이름이 왔는지 추측할 수 있을 거에요. 이 아키텍처는 DDD 원칙과 의존성 역전의 법칙에서 영감을 얻어 탄생했어요. 아마 알아차렸을 수도 있겠는데 우리가 살펴볼 다음 아키텍쳐는 더 많은 레이어와 더 많은 세부사항을 가지고 있어요. 그렇지만 핵심은 여전히 같아요.

추가적인 레이어는 시스템의 요소들 간 더 나은 관심의 분리를 제공하기 위함이에요. 어플리케이션 레이어는 도메인 코어와 외부 세상의 중재자에요. 보통 전체 어플리케이션의 Use Case 를 포함해요. 그리고 보통 Use Case 는 조립 가능한 유닛 형태로 입력 데이터를 수집하고, 도메인 서비스에 전달해요 그리고 결과를 호출한 쪽에 전달해요. 어플리케이션의 규칙에 따라 여러 UseCase 가 복합적으로 사용될 수 있어요. 나중에 이 레이어를 어떻게 설계하고 구현하는지 살펴 볼 거에요.

How do the DDD principles relate to domain-centric architectures ?

이 질문을 스스로 했을 것 같아요. 도메인 주도 설계는 어플리케이션의 핵심 레이어를 모델링하는 일련의 원칙이에요. 그리고 이 것은 도메인 중심 아키텍처의 가장 중요한 레이어인 도메인 / 비지니스 레이어를 설계하기 위한 전략, 패턴 및 프랙티스에 관한 것이에요. 반면에 언급된 모든 아키텍처는 단지 전체 시스템의 아키텍처가 어떤 모습에 대해서만 설명을 해요. 그러면서 구현 세부사항에 대해서는 깊은 관심을 두지 않아요. 따라서 도메인 중심 아키텍처를 시스템의 기반으로 사용하기로 결정했다면, 요구사항에 가장 적합한 아키텍처를 찾은 다음 DDD 가이드라인을 고려해 도메인 레이어를 설계하는 것이 가장 좋아요.

What architecture to use ?

개인적으로는 여기에 언급된 그리고 앞으로 설명할 아키텍처의 순수한 형태를 그대로 사용하는 것은 추천하지 않아요. 좋은 아키텍트가 되려면 how-to 가이드를 따르는 것보다는 직접 결정을 내려야해요. 모든 프로젝트는 고유하며 나름의 접근 방식이 필요해요. 아키텍처의 차이를 알면 각각의 장점과 단점을 알 수 있고 특정 프로젝트에 맞는 아키텍처를 정의할 수 있다고 생각해요. 물론 관심의 분리, 제어 역전과 같은 프랙티스를 따르는 것을 잊지 말고 코드 베이스를 깨끗하고 유지 가능하게 만들어야해요.

더욱이 대부분의 아키텍쳐를 실제 코드 예제 없이 정의했는데 이건 좋은 것 같아요. 아키텍처 그 자체는 추상적인 것이에요. 개념, 룰, 프랙티스의 집합일 뿐이에요. 그러니 이 글과 다른 많은 글들의 주요 아이디어를 통해 주요한 개념을 이해하고, 실제 SW 시스템에 적용하면서 스스로 중요한 결정을 내릴 수 있게 하세요.

Conclusion

이 글을 통해 아키텍처를 설계하는 두 가지 서로 다른 방법을 보여주려고 했어요. SW 아키텍처에 대한 생각을 바꾸는 것도 포함해서요. 많은 도메인 중심 아키텍처가 있지만 그 것들은 대략적으로는 모두 같은 철학을 고수하고 있다는 점에서는 같아요. 더욱이 도메인 중심과 데이터 중심 아키텍처 모두 동일한 레이어를 가지고 있기 때문에 처음 볼 때는 매우 유사할 수 있고 어느면에서는 그래요. 이 둘의 가장 큰 차이는 종속성 규칙 (Depdency Rule) 을 통해 표시되는 종속성 화살표에요. 파트2에서는 클린 아키텍처라고 불리는 도메인 중심 아키텍처에 대해서 알아볼거에요.

--

--

JustWrite
JustWrite

Written by JustWrite

Just write and see and make it more readable.

Responses (1)