많은 시스템에서 데이터베이스는 왕좌에 앉아 있다. 팀은 스키마부터 그린다. 도메인을 논의하기 전에 테이블을 논의하고, 업무 규칙을 짜기 전에 어떤 DBMS를 쓸지부터 고른다. 마틴이 이 장에서 하려는 일은 그 왕좌를 걷어차는 것이다 — 데이터베이스는 아키텍처의 중심이 아니다. 세부사항이다. 아키텍처의 중심에서 밀어내 경계 바깥으로 치워도 되는, 아니 반드시 치워야 하는 무엇이다.

이 주장은 도발적으로 들린다. 데이터야말로 시스템이 지키려는 자산이 아닌가. 그러나 마틴의 논점은 데이터가 하찮다는 게 아니다. 정반대다. 데이터는 대단히 중요하기 때문에, 그것을 담는 그릇(데이터베이스 시스템)과 데이터의 의미(모델)를 냉정하게 갈라야 한다는 것이다. 이 장의 전부는 그 둘을 가르는 칼금 하나에 달려 있다.

데이터베이스는 유틸리티다

먼저 용어를 정확히 못박아야 한다. 마틴이 “세부사항”이라 부르는 것은 데이터가 아니라 데이터베이스다. 정확히는 관계형 테이블, 파일, 그것을 다루는 소프트웨어 시스템 — 오라클이든 MySQL이든 몽고DB든 — 이다. 이들은 데이터를 접근 가능하게 만들어 주는 도구일 뿐이다. 마틴은 이것을 유틸리티라 부른다. 전기나 수도처럼, 있으면 편리하지만 그 자체가 집의 설계 목적은 아닌 것.

왜 유틸리티인가. 그 뿌리를 캐면 하나의 우연에 가 닿는다. 오늘날 우리가 데이터베이스라 부르는 것들은 대부분 회전하는 자기 디스크를 전제로 태어났다. 데이터가 원판 위에 자기(磁氣)로 새겨지고, 그 원판이 빙글빙글 돌고, 헤드가 특정 트랙과 섹터로 이동해 데이터를 읽어 온다. 이 물리적 사실이 모든 것을 규정했다. 디스크는 느리다. 한 바퀴 도는 데 밀리초가 걸리고, 헤드가 움직이는 데 또 시간이 든다. 그래서 우리는 데이터를 페이지 단위로 뭉쳐 읽고, 인덱스를 만들어 헤드의 이동을 줄이고, 쿼리 최적화기를 두어 디스크 접근을 최소화한다. 관계형 모델의 정규화도, 인덱스 설계도, SQL이라는 선언적 질의 언어도 — 상당 부분이 이 느린 회전 원판을 어떻게든 덜 건드리려는 몸부림에서 나왔다.

그런데 회전 자기 디스크는 물리학의 진리가 아니다. 그저 어느 시대의 저장 기술이었을 뿐이다. RAM은 점점 싸지고, SSD는 회전하지 않으며, 언젠가 디스크가 완전히 사라지는 날이 오면 지금 우리가 데이터를 이리저리 뒤섞어 테이블에 흩뿌려 저장하는 방식은 어처구니없어 보일 것이다. 데이터베이스라는 것의 형태 자체가 곧 사라질 기술적 우연의 산물이라는 얘기다. 그런 우연 위에 시스템의 아키텍처를 세울 수는 없다.

판단 기준: 어떤 구성요소가 “지금 시대의 하드웨어 제약을 우회하기 위해 존재하는가”를 물어라 — 그렇다면 그것은 유틸리티이고, 아키텍처의 중심이 아니라 갈아 끼울 수 있는 세부로 다뤄야 한다. 함정: 데이터베이스가 지금 시스템에서 가장 비싸고 가장 눈에 띄는 인프라라는 이유로 그것을 아키텍처의 심장이라 착각한다 — 비싼 것과 중심인 것은 다르다.

데이터와 데이터베이스를 가른다

이 장의 오해 대부분은 하나의 뒤섞음에서 온다 — 데이터데이터베이스를 같은 것으로 취급하는 것. 마틴은 이 둘을 칼같이 가른다.

데이터는 아키텍처적으로 결정적이다. 시스템이 다루는 개념들 — 고객, 주문, 계좌, 거래 — 이 어떤 속성을 갖고 서로 어떻게 관계 맺는지, 이 데이터 모델은 업무 규칙의 뼈대다. 엔티티가 무엇이고 그들 사이에 어떤 불변식이 성립하는지는 시스템의 본질에 속한다. 이것이 흔들리면 시스템 전체가 흔들린다. 그러니 데이터 모델은 아키텍처의 안쪽 원, 고수준 정책에 속한다.

반면 그 데이터를 어디에 어떻게 저장하느냐 — 관계형 테이블에 정규화해 넣느냐, 문서로 뭉쳐 넣느냐, 어떤 벤더의 제품을 쓰느냐, 스키마의 컬럼 타입을 무엇으로 잡느냐, 어떤 SQL 방언으로 질의하느냐 — 이것은 전부 세부사항이다. 저장 기술은 데이터의 의미에 대해 아무것도 결정하지 않는다. Customer가 이름과 신용 한도를 갖는다는 사실은 그것이 오라클 테이블의 한 행이든 JSON 문서든 메모리 위의 객체든 변하지 않는다. 저장 형식은 그 사실을 담는 그릇의 모양일 뿐이다.

그래서 마틴의 명제는 이렇게 정리된다. 데이터 모델은 중요하다. 데이터베이스 시스템은 세부사항이다. 전자는 안쪽에, 후자는 바깥쪽에 둔다. 이 경계선을 긋는 순간, “우리 시스템은 관계형인가 NoSQL인가”라는 질문은 아키텍처의 질문에서 배포 시점에 결정해도 되는 세부의 질문으로 강등된다.

판단 기준: 어떤 사실이 저장 기술을 바꿔도 그대로 참인가를 물어라 — 참이면 그것은 데이터 모델(안쪽)이고, 저장 기술을 바꾸면 사라지는 것(테이블 구조·인덱스·SQL)이면 세부(바깥쪽)다. 함정: 스키마를 데이터 모델과 동일시하는 것 — 스키마는 특정 DBMS가 데이터 모델을 물리적으로 구현한 한 가지 방식일 뿐, 모델 그 자체가 아니다.

업무 규칙은 데이터베이스를 몰라야 한다

경계를 그었으면 그 경계가 지켜지는지를 봐야 한다. 마틴이 가장 경계하는 것은 데이터베이스에 대한 앎이 안쪽 원으로 새어 드는 것이다.

핵심 업무 규칙은 데이터가 어떻게 저장되는지 알아서는 안 된다. 대출 이자를 계산하는 규칙은 계좌가 어떤 테이블의 어떤 컬럼에 담겨 있는지, 그것을 꺼내 오는 SQL이 무엇인지 알 필요가 없다. 규칙이 아는 것은 오직 Account라는 개념과 그 잔액, 이율뿐이어야 한다. 저장에서 꺼내 오는 일은 규칙 바깥의 누군가가 대신 해 주고, 규칙에게는 이미 살아 있는 객체 형태로 건네줘야 한다.

이 원칙이 무너지는 전형적 장면이 있다. 데이터베이스에서 행(row)을 꺼내 온 뒤, 그 행을 표현하는 자료구조를 시스템 전체가 그대로 들고 다니는 것이다. 컨트롤러도, 유스케이스도, 화면도 모두 “테이블의 한 행”이라는 형태에 맞춰 데이터를 주고받는다. 겉보기엔 편리하다. 매핑 코드를 안 짜도 되니까. 그러나 이 순간 관계형 스키마의 형태가 시스템 구석구석에 각인된다. 컬럼 하나를 바꾸거나 테이블을 쪼개는 순간, 그 여파가 업무 규칙과 화면까지 번진다. 데이터베이스라는 바깥쪽 세부가 안쪽 정책을 인질로 잡은 것이다.

마틴의 해법은 방향성 있는 의존이다. 업무 규칙은 저장에 대한 어떤 지식도 갖지 않고, 저장 계층이 규칙이 요구하는 형태(단순한 엔티티, 도메인 객체)로 데이터를 만들어 건넨다. 의존성은 언제나 세부(데이터베이스)에서 정책(업무 규칙)을 향하지, 그 반대가 아니다. 화면과 업무 규칙 사이에 추상이 놓이듯, 저장과 업무 규칙 사이에도 추상이 놓인다.

판단 기준: 업무 규칙 코드 안에서 SQL·테이블명·컬럼명·ORM 애너테이션이 눈에 띄면 경계가 이미 뚫린 것이다 — 그 지식은 저장 계층 안에 갇혀 있어야 한다. 함정: “행을 그대로 들고 다니면 매핑이 없어 편하다”는 유혹 — 그 편함은 스키마의 형태를 시스템 전역에 새기는 대가로 산 것이고, 청구서는 스키마가 처음 바뀌는 날 도착한다.

그들이 데이터베이스를 원하는 진짜 이유

그렇다면 왜 이렇게 많은 팀이 데이터베이스를 중심에 두는가. 마틴은 이 현상을 사람의 오류가 아니라 이해관계의 결과로 읽는다.

데이터베이스 벤더에게 데이터베이스가 시스템의 중심이 되는 것은 사업 그 자체다. 그들은 자기 제품이 아키텍처의 심장에 박히기를 원한다. 한번 스키마와 저장 프로시저와 벤더 고유 기능에 깊이 얽히고 나면 빠져나오기 어렵고, 그 얽힘이 곧 그들의 매출을 지킨다. 그래서 그들은 데이터베이스를 중심에 두라고 설득한다 — 온갖 편의 기능과 프레임워크 연동으로.

한편 데이터를 파일 시스템이 아니라 데이터베이스에 넣는 데는 정당한 실용적 이유도 있다. 데이터베이스는 여러 형태로 데이터에 접근하게 해 주고(인덱스·질의), 트랜잭션으로 일관성을 지켜 주고, 백업과 복구를 제공한다. 이것들은 진짜 가치다. 그러나 마틴이 짚는 것은 이 가치들이 전부 저수준의 관심사라는 점이다. 데이터를 빠르게 찾고, 일관되게 쓰고, 안전하게 보관하는 일은 필요하지만, 그중 어느 것도 “이 시스템이 무엇을 하는가”라는 고수준 정책을 결정하지 않는다. 유용한 유틸리티라는 사실과 아키텍처의 중심이라는 지위는 별개다.

판단 기준: 어떤 기술을 중심에 두자는 압력이 그 기술이 주는 실제 가치에서 오는지, 아니면 그 기술에 얽매이는 데서 이득을 보는 쪽의 이해에서 오는지를 분리해 물어라. 함정: 벤더가 제공하는 편의 기능(저장 프로시저·전용 확장)에 업무 로직을 심는 것 — 편해 보이지만 그것은 업무 규칙을 데이터베이스 안으로 이주시키는 일이며, 정책을 갈아 끼울 수 없는 세부에 못박는 것이다.

성능은 아키텍처가 아니라 캡슐화의 문제다

데이터베이스를 중심에 두는 가장 그럴듯한 논거는 성능이다. “데이터가 이렇게 많고 트래픽이 이렇게 높은데, 저장 방식을 세부로 미룬다는 게 말이 되는가. 성능은 처음부터 아키텍처로 설계해야 하지 않는가.”

마틴은 성능이 중요하지 않다고 말하지 않는다. 다만 성능이 어느 수준의 관심사인가를 다시 놓는다. 성능은 시스템의 고수준 정책이 아니다. 그것은 저수준에서 다뤄야 할, 그리고 저수준에 캡슐화할 수 있는 관심사다. 데이터를 얼마나 빠르게 넣고 빼느냐는 저장 계층 안쪽의 문제이며, 잘 그어진 경계 뒤에 가둘 수 있다.

이것이 왜 중요한가. 성능을 저수준에 캡슐화해 두면, 성능 요구가 바뀔 때 업무 규칙을 건드리지 않고 저장 계층만 갈아 끼울 수 있다. 처음엔 단순한 관계형으로 시작했다가, 읽기 부하가 폭증하면 캐시를 앞에 두거나, 특정 데이터를 다른 저장소로 옮기거나, 아예 다른 DBMS로 바꾼다 — 이 모든 결정이 경계 뒤에서 이뤄지고 안쪽 정책은 그대로다. 반대로 성능을 아키텍처의 중심축으로 삼아 업무 규칙을 특정 저장 방식에 맞춰 짜 두면, 성능 전략을 바꾸는 순간 시스템 전체를 다시 짜야 한다. 성능이라는 저수준 관심사가 고수준 정책을 오염시킨 대가다.

판단 기준: 성능 요구가 바뀌었을 때 저장 계층만 교체하면 되는가, 아니면 업무 규칙까지 손대야 하는가를 그려 보라 — 후자라면 성능이 잘못된 수준에 배치된 것이다. 함정: “성능이 걸려 있으니 처음부터 저장 최적화에 맞춰 설계하자”는 조기 결합 — 대개 그 성능 병목은 아직 오지도 않았고, 미리 맞춰 둔 결합만이 확실히 남아 훗날 변경을 가로막는다.

무엇을 중심에 두고 무엇을 미룰 것인가

이 장을 한 문장으로 접으면 이렇다 — 데이터의 의미는 지금 결정하고, 데이터의 저장은 미룰 수 있는 만큼 미뤄라. 이것은 15장에서 본 “선택지를 열어 두라”는 아키텍트의 원칙이 저장이라는 축 위에서 되풀이되는 것이다. 저장 기술을 늦게 고를수록, 더 많이 알게 된 상태에서 더 나은 결정을 내릴 수 있다.

주의할 균형도 있다. 데이터베이스를 세부로 미룬다는 것이 저장을 소홀히 하라는 뜻은 아니다. 데이터 모델은 여전히 진지하게 설계해야 하고, 트랜잭션 경계와 일관성은 업무 규칙이 의존하는 성질이므로 아무렇게나 다뤄도 되는 게 아니다. 미루는 것은 어떤 제품·어떤 스키마·어떤 SQL로 구현하느냐이지, 데이터가 무엇이고 어떤 불변식을 지켜야 하느냐가 아니다. 앞의 것은 세부, 뒤의 것은 정책 — 이 경계를 흐리면 이 장의 교훈은 “저장을 대충 해도 된다”는 정반대의 오독으로 미끄러진다.

  • 데이터베이스(DBMS·스키마·SQL·저장 프로시저)는 회전 자기 디스크라는 기술적 우연 위에 세워진 유틸리티다. 유용하되 아키텍처의 중심이 아니다.
  • 아키텍처적으로 중요한 것은 데이터의 모델(엔티티와 그 관계·불변식)이지, 그것을 저장하는 방식이 아니다. 전자는 안쪽 정책, 후자는 바깥쪽 세부.
  • 업무 규칙은 저장에 대한 지식을 가져선 안 된다 — SQL도, 테이블 형태도 저장 계층 안에 갇혀야 하고, 의존성은 세부에서 정책을 향한다.
  • 성능은 고수준 정책이 아니라 저수준에 캡슐화할 관심사다. 경계 뒤에 가두면 성능 전략을 바꿔도 업무 규칙은 그대로다.

데이터베이스를 중심에서 밀어내면 시스템은 오히려 데이터를 더 잘 지킨다. 저장 기술이 바뀌어도, 성능 요구가 뒤집혀도, 벤더를 갈아 치워도, 데이터가 무엇이며 어떤 규칙이 그것을 다스리는지는 안쪽 원에 온전히 남기 때문이다. 다음 장은 같은 칼금을 또 하나의 왕좌에 들이댄다 — 이번엔 데이터베이스가 아니라 웹이다.

다음장으로 31장