책에서 소개된 아키텍처의 4개 차원(특성, 스타일, 논리적 컴포넌트, 결정)을 각자의 언어로 해석해 보았습니다.
아키텍처 특성은 성능, 확장성, 가용성, 신뢰성 등 시스템이 성공하기 위해 지원해야 하는 역량들입니다.
아키텍처 스타일은 마이크로서비스나 모듈형 모놀리스처럼 전체 시스템의 골격을 이루는 베스트 프랙티스 예시들입니다.
논리적 컴포넌트는 역할과 책임을 기준으로 나눈 구성 요소입니다.
아키텍처 결정은 시스템이 지켜야 할 '법'이나 '제약 사항'과 같습니다. 예를 들어 "프레젠테이션 레이어는 DB에 직접 접근하면 안 된다"는 규칙이 이에 해당합니다.
# 동변성(Connascence): 변화의 전파를 관리하는 법
이번 스터디에서 가장 뜨거웠던 주제 중 하나는 동변성(Connascence)이었습니다. "시스템의 한 요소가 변경될 때, 정상 동작을 위해 다른 요소도 함께 변경되어야 하는 관계"를 뜻합니다. 아키텍트는 이 동변성의 강도(Strength)를 분석하여, 결합도가 높은 코드를 더 관리하기 쉬운 형태로 리팩터링해야 합니다.
정적 동변성 (코드 수준에서 파악 가능)
이름(Name): 여러 컴포넌트가 엔티티의 이름에 합의해야 하는 가장 기초적인 결합입니다. 현대적인 IDE 도구로 가장 쉽게 리팩터링할 수 있어 가장 바람직한 유형으로 꼽힙니다.
타입(Type): 변수 타입에 대한 합의가 필요하며, 정적 타입 언어에서는 컴파일러가 이를 잘 잡아줍니다.
의미 동변성(Meaning): 특정 값의 의미에 합의해야 하는 경우로, '규약 동변성'이라고도 합니다. 상수 대신 하드코딩된 '매직 넘버'를 사용하는 것이 대표적인 사례입니다.
위치 동변성(Position): 인자의 순서 등 값들의 순서에 합의해야 합니다. 예를 들어 updateSeat("14D", "Ford")에서 인자 순서가 바뀌면 타입이 같더라도 논리적 오류가 발생합니다.
알고리즘(Algorithm): 여러 컴포넌트가 특정 알고리즘(예: 서버-클라이언트 간 보안 해싱)에 합의해야 하는 경우로, 정적 동변성 중 가장 강도가 높습니다.
동적 동변성 (런타임에 실행해봐야 파악 가능)
실행 순서(Execution): 여러 컴포넌트의 실행 순서가 중요한 경우입니다. 예를 들어 이메일 전송 전 반드시 발신자/수신자 속성을 먼저 설정해야 하는 규칙 등이 해당합니다.
타이밍(Timing): 실행 타이밍이 결과에 영향을 미치는 경우로, 분산 시스템의 경합 조건(Race Condition)이 대표적인 예입니다.
값 동변성(Values): 여러 값이 서로 의존하여 함께 변경되어야 무결성이 유지되는 관계입니다. 트랜잭션 내에서 여러 DB의 값을 일관되게 갱신해야 하는 상황을 떠올리면 쉽습니다.
신원 동변성(Identity): 여러 컴포넌트가 동일한 엔티티를 참조하고 공유해야 하는 경우입니다.
가급적 동적 동변성보다는 정적 동변성을 유지하되, 가급적 보다 덜 해로운 동변성으로 교환해나가는 것을 지향해야 합니다.
# 응집도와 결합도 : 좋은 코드의 척도
좋은 아키텍처를 판별하는 기준인 응집도와 결합도에 대해 깊이 있는 토론이 이어졌습니다.
응집도(Connascence)는 기능적 응집부터 우발적 응집까지 7가지 단계를 살펴보며, 변경의 이유가 동일한 것끼리 뭉쳐야 한다는 점을 확인했습니다.
결합도와 '주 시퀀스와의 거리': 추상도와 불안정도를 축으로 하는 그래프를 통해 '고통 영역(추상도는 낮지만 인기가 많아 고치기 힘든 코드)'과 '무용 영역'을 정의했습니다.