ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Clean Architecture
    Architecture + Design Pattern 2022. 9. 25. 22:27

     

     

    지난 몇년동안 시스템 아키텍쳐에 대한 많은 아이디어들이 있었다.

    이 아키텍쳐들은 모두 디테일한 점들에서는 다른점을 가지고 있지만 크게보면 매우 유사하다. 이것들 모두 '관심사의 분리' 라는 같은 목적을 가지고있었고, 이것들 모두 소프트웨어를 여러 층들로 나눔으로써 그 목적을 이룬다. 각각은 최소 하나의 business rules 층과, interfaces 층을 가지고있다.  

     

    architectures 는 다음과 같은 시스템을 만든다. 

     

    1. 프레임워크와 독립적. 아키텍쳐는 소프트웨어에 얹혀있는 기능을 위한 라이브러리의 존재와 독립적이다. 이렇게 함으로써 프레임워크를 단순히 툴로써 (프레임워크에 시스템을 어떻게든 욱여넣기보다) 이용할 수 있게 된다. 

     

    2. 테스트가 용이하다. business rules 는 UI, Database, Web Server 등의 외부 도움없이 테스트할 수 있다. 

     

    3. UI 와 독립적이다. UI 를 나머지 시스템의 변경 없이 쉽게 변경할 수 있다. 예를 들어 Web UI 를 콘솔 UI 로 비즈니스 rules 변경 없이 바꿀 수 있다.

     

    4. Database 와 독립적이다. Oracle SQL Server, Mongo, BigTable, CouchDB 등 어떤 것으로도 바꿀 수 있다. Business rules 가 데이터베이스에 의해 제한되지 않기 때문이다. 

     

    Business Rule
    사업 규칙
     또는 비즈니스 룰(business rule)은 특정한 사업적인 면을 정의하고 제한하는 규칙으로서 늘 참이나 거짓으로 결정된다. 사업 규칙들은 비즈니스 구조를 확고히 하고 비즈니스의 행위를 제어하거나 영향을 주기 위해 고안되었다. 사업 규칙들은 단체에 적용할 운영, 정의, 제약 조건을 기술한다. 사업 규칙들은 사람, 프로세스, 회사의 행위, 단체의 컴퓨팅 시스템에 적용할 수 있으며 조직이 목표를 수행할 수 있게 도와준다.
    더보기
    A business rule is a controllable point that defines or constrains some aspect of a technical process with your business process. Business rules are made up of one or more evaluations you can configure to ensure your business process is followed. A great application of business rules is within the access controls you use to personalize Community Hub. 

     

    When checking the business rule, each evaluation is made in the specified order, and if all resolve to True, the business rule resolves to True. If even one evaluation resolves to False, the business rule resolves to False. In other words, it uses AND logic between each evaluation. Also, only active evaluations are made; disabled evaluations are ignored when checking the business rule.

    5. 외부 agency 와 독립적이다. 사실, business rules 는 바깥 층에 대한 어떠한 정보에 대해 알 필요가 없다. 

     

    위 diagram 은 위에서 언급한 모든 architectures 를 하나의 idea 로 나타내기 위한 것이다. 

     

     

    The Dependency Rule

    각 원들은 소프트웨어의 서로 다른 영역들을 나타낸다. 일반적으로 원의 중심부로 갈수록 소프트웨어의 레벨은 높아진다. 

    소스코드 의존성은 오직 안쪽으로만 향한다. 다시 말하면, 안쪽에 있는 어떠한 원도 바깥쪽에 있는 원에 대해 알지 못한다. 즉, 바깥 원에 선언된 어떠한 변수도 안쪽 원에 있는 코드에서 언급되면 안된다. 이는 함수, 클래스, 변수 등 모든 software entity 에 해당한다. 같은 맥락에서 바깥쪽이 안쪽에 있는 것에 어떠한 영향을 주어서도 안된다. 

     

     

    Entities

    Entities 는 Enterprise business rules 를 캡슐화 시킨다. Entity 는 object (with methods) 또는 set of data structures (with functions) 가 될 수 있다. Entities 가 enterprise 내의 많은 다양한 applications 에 사용될 수 있다면 어떤 것이든 상관없다. 

    만약 enterprise 가 없고 단지 single application 을 작성하고 있는 것이라면, entities 는 application 의 business objects 이다. Entities는 가장 General 하고 높은 레벨의 규칙들을 캡슐화시킨다. 이것들은 외부 요인이 변경되었을 때 변화할 가능성이 가장 적다. 예를들어, '페이지 네비게이션'이나 '보안'에 의해 엔티티 오브젝트들이 영향을 받지는 않을 것이다. 어떠한 특정 어플리케이션의 변화도 엔티티 층에 영향을 주면 안된다. 

    캡슐화 (encapsulation): Data 를 properties, methods 을 이용하여 하나로 묶거나, 
    특정 properties, methods 에 access level 을 부여해 직접적 접근을 제한하는 것. 

     

    Use Cases

    해당 층의 소프트웨어는 적용 위주의 Business rules 이다. 여기서는 시스템의 'use cases' 를 캡슐화 및 구현한다.  이러한 use cases 는 entities 로 향하거나 entities 로부터의 data 흐름을 조직하고, entities 가 enterprise business rule 을 사용하도록 하여 use case 의 목적을 이루도록 관리한다.  

    use case 층에서의 변화는 entities 에 영향을 주면 안된다. 또한,  database, UI, 또는 다른 어떠한 common frameworks 에 의해서 이 층은 영향을 받으면 안된다. 다른 말로 하면, 다른 concerns 로부터는 고립된 상태이어야한다. (위에 언급된 Dependency Rule) 

    그러나, application 동작에서의 변화는 use-cases 에 영향을 주고, 따라서 여기 층의 소프트웨어에도 영향을 준다.  만약 use-case 에 작은 변화가 생긴다면, 여기 층의 여러 코드도 당연히 영향을 받을것이다. 

    소프트웨어: 컴퓨터에게 동작 방법을 지시하는 명령어 집합의 모임

     

    Interface Adapters

    이 층의 software 는 use cases 와 entities 에서 쓰기 편한 Data 를 ->  Database 나 Web 과 같은 외부 agency 가 쓰기 편한 Data 로 변환해주는 adapters 의 모음이다. 예를들어, GUI 의 MVC architecture 만을 갖는 것이 Interface Adapters 가 될 수 있다. Presenters, Views, and Controllers 가 모두 이곳에 속한다. Models 는 Controllers 로부터 user cases 로, 다시 use cases 로부터 presenters 와 views 로 전달되는 data structures 일 것이다. 

    이와 유사하게, 이 층에서 data 는 entities 와 use caes에게 편리한 format 에서 Database 와 같은 persistence framework 에게 편리한 format 으로 변환도 된다. 이 층 (circle) 내부의 어떠한 code 도 database 에 대해 알면 안된다. 만약 database 가 SQL 일 경우, 모든 SQL 는 이 층, 여기 중에서도 일부 데이터베이스 관련된 곳에서만 알려져 있어야한다. (다른 곳에서는 알면 안됨.)

    또한, 모든 adapter 외부 form 을  내부 form 에 맞는(use cases, entities 에 사용될) data 로 바꿀 필요가 있는) 는 이 층에 존재하여야 한다. 

     

    Frameworks and Drivers.

    가장 바깥쪽 층은 일반적으로 frameworks 나 tools (Database, Web Framework 등) 으로 구성된다. 보통 이 층에서는 내부쪽으로 communicate 시켜주는 코드 이외에는 많이 작성하지 않는다. 이 층은 모든 상세한 것들이 위치하는 곳이다. (Web, database, ...) 우리는 이것들이 내부에 피해를 거의 주지 못하게 하기 위해 바깥쪽에 두도록 한다. 

     

    Only Four Circles?

    저기 원들은 단지 개략적으로 나타낸 것이다. 아마 네개 층보다 더 필요할 수 있을 것이다. 항상 이 네개만을 사용해야 한다는 rule 은 없다. 그러나, Dependency Rule 은 항상 적용된다. Source code 의 의존도는 항상 안쪽으로만 향해야한다. 안쪽으로 갈수록 추상화 정도는 높아진다. 따라서 가장 바깥쪽 원은 낮은 level 의 'Concrete detail' 이다.  내부로 이동할수록 소프트웨어는 더 추상적이고, 높은 level policies 를 캡슐화한다. 가장 안쪽 circle 이 따라서 가장 'general' 하다. (추상적)

     

    Crossing boundaries.

    오른쪽 아래 그림은 원의 경계를 어떤 방식으로 넘는지를 나타낸 도표이다. Controllers 와 Presenters 가 Use Cases 와 communicate 하는 것을 보여준다. 흐름을 다시 눈여겨보자. Controller 에서 시작해서 use case 를 거쳐, presenter 를 실행한다.  Source code Dependencies 도 다시 한번 상기해보자. 각각은 use cases 쪽으로 향한다. 

     우리는 보통 이런 명백한 모순을 Dependency Inversion Principle 을 통해 해결한다. 예를들어 Java 언어에서는 Interfaces 와 Inheritance 의 관계를 정리해서 source code dependencies 가 원래 흐름 방향과 반대로 가게 만든다.

    예를 들어, use case 가 presenter 를 호출해야 하는 경우를 생각해보자. 그러나, 이것은 절대로 직접적인 호출이 되면 안된다. (dependency rule 위반:  바깥쪽 원에 있는 어떠한 이름도 내부 원에서 언급될 수 없다.) 따라서 이때 우리는 use case 가 원 안쪽 내에 있는 interface  (Use Case Output Port) 를 호출하게 만들고, 바깥쪽의 presenter 에서는 interface 의 requirements 를 구현하도록 한다. 

    같은 테크닉이 architectures 의 모든 경계선을 지날 때 사용된다. Dynamic polymorphism 의 이점을 활용하여 반대방향으로 향하는 source code dependencies 를 만듦으로서, 어떤 방향으로 control 이 이동하든지 Dependency Rule 여전히 만족시킬 수 있다. 

     

    What data crosses the boundaries.

    보통 경계를 지나는 데이터는 단순한 'Data 구조'이다. 원하면 간단한 structs 또는 단순한 Data Transfer objects 를 사용할 수 있다. 함수 호출에 사용되는 단순한 arguments 도 될 수 있다. 또는 hasmap 을 통해 감싸거나, object 로 구성할 수도 있다. 중요한 점은 단순하고 고립된 data structures 가 경계를 넘을 수 있다는 것이다. Entities 나 Database rows 를 넘기면 안된다. 또한, 'The Dependency Rule' 을 어기도록 의존성을 설정해서도 안된다. 

    예를 들어, 많은 데이터베이스 frameworks 는 query 에 대한 응답으로 편리한 형태의 data 를 반환한다. (RowStructure 라고 부르자. ) 

    우리는 row structure 가 경계를 넘어 안쪽으로 오는 것은 원하지 않는다. ( 내부 원이 바깥 원에 대한 어떤 정보를 알도록 하기 때문에 Dependency Rule 위반. ) 따라서 data 가 경계를 넘을 때는, inner circle 에게 편리한 form 이어야 한다. (inner circle 이 알고있는 data structure) 

     

    Conclusion

    이 단순한 규칙들을 모두 지키는 것은 어렵지 않고, 많은 머리아픈 상황들을 해결해줄 것이다. Software 를 layers 로 나눔으로써, 그리고 Dependency Rule 을 지킴으로써 본질적으로 테스트 가능하고, 언급한 다른 모든 이점들이 있는 시스템을 만들게 될 것이다. 어떤 외부의 system(database, web framework 등) 이 쓸모없게 되면 해당 부분을 최소한의 노력으로 바꿀 수 있게 된다. 

     

     

    출처: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

    참고할 곳: https://eunjin3786.tistory.com/207

     

    'Architecture + Design Pattern' 카테고리의 다른 글

    ReactorKit  (0) 2022.10.12
    Clean Architecture + MVVM  (0) 2022.10.03
    MVVM  (0) 2022.09.26
    Delegate & Protocol  (0) 2022.03.28
    Architecture Patterns ( MVC, MVP,MVVM ) ( with iOS, Swift)  (0) 2021.10.21
Designed by Tistory.