여러 조그만 정책들이 합쳐 시스템을 이루며 동일한 이유로 동일한 시점에 변경되는 정책은 동일 수준에 위치하며, 그 반대의 경우 다른 수준에 위치시켜야한다. 즉 다른 컴포넌트로 분리해야 한다. 또한 저수준의 컴포넌트가 고수준을 의존하도록 설계해야한다.
입력과 출력까지의 거리를 수준으로 정의할 수 있다고 하며, 입력과 출력에서 멀리 위치할수록 정책의 수준이 높아진다고 한다. 또한 입력과 출력에 가까울수록 덜 중요한 이유로 변경이 되며, 더욱 빈번히 변경되고 긴급성을 요한다. 다음은 입력과 출력간의 데이터 흐름도의 예시로 '번역' 컴포넌트가 가장 상위 수준의 정책을 가진다.
데이터 흐름과 의존성은 항상 같은 방향을 가리키지 않으니 조심해야하며, 자칫하면 아래와 같이 잘못된 아키텍처가 만들어지게 된다. 고수준의 encrypt 함수가 저수준인 readChar, writeChar 함수에 의존하는 모습을 볼 수 있다. 따라서 아래와 같이 의존성에 따라 경계로 수준을 나눠주는 작업이 필요하다. 이 덕분에 입력/출력에 변화가 생겨도 암호화 정책은 거의 영향을 받지 않는다.
업무 규칙은 시스템이 존재하는 이유이며 핵심적인 기능이다. 직접적으로 수익을 내고 비용을 줄이는 코드를 수반한다. 그렇기 때문에 사용자 인터페이스나 데이터베이스 같은 저수준의 관심사로 인해 오염되지 않아야하며 업무 규칙의 코드는 시스템의 심장부에 위치해야 한다.(덜 중요한 코드는 플러그인 형태로 결합) 시스템에서 업무규칙은 가장 독립적이며, 재사용할 수 있는 코드여야 한다.
대출에 N%의 이자를 부과한다.
는 핵심업무규칙이며 잔액,이자율, 지급일정
은 핵심 업무 데이터이다. 이 둘은 본질적으로 결합되어 있으며 이러한 유형을 엔티티라고 부른다. 엔티티는 업무의 대표자로서 데이터 베이스, UI, 서드파티 플러그인의 고려사항으로 인해 오염되지 않아야한다. 대출을 뜻하는 Loan 엔티티의 UML 클래스 예시는 다음과 같다.
모든 업무 규칙이 엔티티처럼 순수하지는 않으며, 자동화된 시스템이 동작하는 방법을 정의하고 제약해 수익을 얻거나 비용을 줄이는 업무 규칙도 존재한다. 이러한 규칙을 유스케이스라고 하며 자동화된 시스템의 요소로 존재해야만 의미가 있다. 엔티티와는 반대로 애플리케이션에 특화된 업무 규칙을 구현하는 함수를 제공한다. 또한 엔티티에 비해 저수준 정책을 가지는데 그 이유는 입/출력에 더 가깝기 때문이다.
엔티티는 수많은 다양한 애플리케이션에 사용될 수 있도록 일반화된 개념이고, 유스케이스는 엔티티에 의존하는 조금더 구체적인 업무 규칙이다. 유스케이스는 입력데이터를 받아서 출력데이터를 생성하지만 이 때 데이터를 주고받는 방식에 대해서는 알지 못하도록 해야한다. 결국 엔티티도 유스케이스도 의존성 최대한 제거하여 만드는 것이 중요하며, 업무 규칙의 코드는 시스템의 심장부에 위치해야한다.
거실, 주방, 창문/ 정문, 독서공간, 작은 회의실을 보면 집/도서관임을 알 수 있듯이 아키텍쳐도 어떤 시스템을 위한 것인지 스스로 소리치는 구조를 갖춰야한다. 또한 유스케이스가 중심이 되는 아키텍처를 만들고 싶다면 아키텍처를 프레임워크로부터 제공받는것이 아닌 스스로 설계하는 능력이 있어야한다.
프레임워크가 강력하고 상당히 유용한것은 분명하지만, 좋은 아키텍처라면 프레임워크나 도구, 환경에 구애받지 않고 유스케이스를 지원하는 구조를 아무 문제없이 기술할 수 있다.(스프링, 톰캣, MySQL에 구애X) 그 중 웹은 단순 전달 메커니즘(입출력 장치)이며 시스템 구조가 의존해서는 안된다. 웹처럼 실제 어플리케이션을 무엇(웹, 앱, ...)으로 전달할지는 미루어야할 결정사항이라고 한다.
아키텍처가 유스케이스를 최우선으로 두고 프레임워크와 적당한 거리를 둔다면, 프레임워크를 통하지 않고도 단위테스트가 가능해야 한다. 도구나 환경에 구애받지않고 유스케이스 전부를 엔티티를 통해 전부에 대해 단위 테스트를 수행할 수 있게 된다면, 프레임 워크같은 결정사항을 미뤄 더 좋은선택이 가능해진다.
결론은 아키텍처는 시스템을 이야기 해야하며, 시스템에 적용한 프레임워크에 대해 이야기해서는 안된다. 소스 저장소만 보고도 시스템의 모든 유스케이스를 이해할 수 있게 만들어야 한다.