2023년 11월 28일 작성
Clean Architecture - 다양한 형태의 경계들
System Architecture의 경계를 다양한 방식으로 나누어, software component를 분리할 수 있습니다.
경계의 종류
- system architecture는 software component와 그 component를 분리하는 경계에 의해 정의되며, 경계는 다양한 형태로 나타납니다.
1. 단일체 경계
- source code 수준 분리 mode에서는 나름의 규칙에 따라 분리되어 있을 뿐, 물리적으로 엄격하게 분리되지는 않은 형태입니다.
- 함수와 data가 단일 processor에서 같은 주소 공간을 공유합니다.
- 배포 관점에서 봤을 때, source code 수준 분리 module은 단일체(monolith)입니다.
- monolith는 단일 실행 file을 의미합니다.
- 배포 시에는 이점이 없지만, 개발 시 이점이 있습니다.
- 단일체는 component 수준으로 분리되지 않기 때문에, 배포 시엔 경계가 드러나지 않습니다.
- 개별 component를 배포하는 대신 커다란 하나의 file을 배포하기 때문입니다.
- 배포할 때 경계가 드러나지 않아도, 단일체에는 source code 수준 분리를 통해 경계를 가지도록 하는 것이 좋습니다.
- 단일체 안에 포함된 다양한 component를 개발하고 binary로 만드는 과정을 독립적으로 수행할 수 있게 하기 때문입니다.
- 단일체는 component 수준으로 분리되지 않기 때문에, 배포 시엔 경계가 드러나지 않습니다.
-
단일체를 배포하는 일은 일반적으로 compile과 정적 link 작업을 수반하므로, 대체로 component는 source code 형태로 전달됩니다.
- 단일체 경계를 가로지는 통신은 빈번합니다.
- source 수준에서 결합이 분리된 단일체에서 component 간 통신은 빠르고 저렴하기 때문입니다.
- 단일체 경계를 가로지는 통신은 전형적인 함수 호출을 의미합니다.
2. 배포형 Component 경계
- architecture의 경계가 물리적으로 드러난 형태입니다.
- 예를 들어, .NET DLL, Java Jar file, Ruby Gem, Unix 공유 library 등의 동적 link library가 이에 속합니다.
- component를 이 형태로 배포하면 따로 compile하지 않고 바로 사용할 수 있습니다.
-
배포 수준 결합 분리 mode에서 component는 binary와 같이 배포 가능한 형태로 전달됩니다.
- 배포 과정에서만 차이가 날 뿐 단일체와 동일합니다.
- 모든 함수가 동일한 processor와 주소 공간에 위치합니다.
- component를 분리하거나 component 간 의존성을 관리하는 전략도 단일체와 동일합니다.
- 그러나 정적 다형성을 사용할 수는 없습니다.
- 함수 호출이 통신의 전부이기 때문에 값싸고 빈번합니다.
- 동적 link와 runtime loading으로 최초의 함수 호출은 오래 걸릴 수 있습니다.
3. Thread 경계
- 단일체와 배포형 component는 모두 thread를 활용할 수 있습니다.
- thread는 architecture 경계나 배포 단위가 아닙니다.
- thread는 실행 계획과 순서를 체계화하는 방법에 가깝습니다.
- 모든 thread가 하나의 component에 포함될 수도 있고, 많은 component에 걸쳐 분산될 수도 있습니다.
4. Local Process 경계
- local process는 주로 command line이나 system 호출을 통해 생성되며, 정적으로 link된 단일체 또는 동적으로 link된 여러 component로 구성된 process를 의미합니다.
- process 관점에서 local process는 일종의 최상위 component이며, component 간의 의존성을 동적 다형성(의존성 역전)을 통해 관리하는 저수준 component로 구성됩니다.
- local process가 정적으로 link된 단일체일 때는 여러 monolitic process가 같은 component들을 가지고 있을 수 있습니다.
- compile하고 정적 link하는 과정에서 각 component의 binary가 단일체에 물리적으로 복사되어 들어가기 때문입니다.
- local process가 동적으로 link된 여러 개의 component로 구성되었을 때는 동적으로 link된 배포형 component들을 서로 공유할 수 있습니다.
- local process들은 동일한 processor 또는 하나의 multi-core system에 속한 여러 processor에서 실행되지만, 각각 독립된 주소 공간에서 실행됩니다.
- 일반적으로 memory 보호를 통해 process들이 memory를 공유하지 못하게 합니다.
- 종종 공유 memory partition을 사용하기도 합니다.
- local process 간 분리 전략은 단일체나 binary component의 경우와 동일합니다.
- source code 의존성 방향은 저수준에서 고수준으로 향합니다.
- 따라서 local process는 고수준 process의 source code가 저수준 process의 이름, 물리 주소, registry 조회 key를 포행해서는 안 됩니다.
- architecture 관점의 목표는 저수준 process가 고수준 process의 plugin이 되도록 만드는 것이기 때문입니다.
- local process는 socket, mailbox, message queue 같은 운영 체제에서 제공하는 통신 기능을 이용하여 서로 통신합니다.
- 예를 들어, local process 경계를 지나는 통신에는 운영 체제 호출, data marshaling/unmarshaling, process 간 문맥 교환 등이 있습니다.
- local process 경계를 지나는 통신은 비싼 작업에 속하므로 너무 빈번하게 이뤄지지 않도록 신중하게 제한해야 합니다.
5. Service 경계
- 물리적인 형태를 띠는 가장 강력한 경계입니다.
-
service는 process이므로 일반적으로 command line 또는 그와 동등한 system 호출을 통해 구동됩니다.
- 통신을 제외하고, local process에 적용한 규칙이 그대로 적용됩니다.
- 저수준 service는 반드시 고수준 service에 plugin되어야 합니다.
- 고수준 service의 source code에는 저수준 service를 특정하는 물리적인 정보(예를 들어, URI)를 포함해서는 안 됩니다.
- service 경계를 지나는 통신은 함수 호출에 비해 매우 느립니다.
- 자신의 물리적 위치에 구애받지 않고 모든 통신은 network를 통해 이루어집니다.
- 가능하다면 빈번하게 통신하는 일은 류류해야 합니다.
- 지연(latency)에 따른 문제를 고수준에서 처리할 수 있어야 합니다.
Reference
- Clean Architecture (도서) - Robert C. Martin