도메인 전용 언어 (domain-specific language, DSL)
- 특정 비즈니스 도메인의 문제를 해결하려고 만든 언어
- 특정 비스니스 도메인을 인터페이스로 만든 API
- 도메인을 표현할 수 있는 클래스와 메서드 집합이 필요하다
DSL의 장점
- 간결함 : API는 비즈니스 로직을 간편하게 캡슐화하므로 반복을 피할 수 있고 코드를 간결하게 만들 수 있다.
- 가독성 : 도메인 영역의 용어를 사용하므로 비 도메인 전문가도 코드를 쉽게 이해할 수 있다. 다양한 조직 구성원 간에 코드와 도메인 영역이 공유될 수 있다.
- 유지보수 : 잘 설계된 DSL로 구현한 코드는 쉽게 유지 보수하고 바꿀 수 있다.
- 높은 수준의 추상화 : DSL은 도메인과 같은 추상화 수준에서 동작하므로 도메인의 문제와 직접적으로 관련되지 않은 세부 사항을 숨긴다.
- 집중 : 비즈니스 도메인의 규칙을 표현할 목적으로 설계된 언어이므로 프로그래머가 특정 코드에 집중할 수 있다.
- 관심사 분리(SoC) : 지정된 언어로 비즈니스 로직을 표현함으로 애플리케이션의 인프라구조와 관련된 문제와 독립적으로 비즈니스 관련된 코드에서 집중하기가 용이하다.
DSL의 단점
- DSL 설계의 어려움 : 간결하게 제한적인 언어에 도메인 지식을 담는 것이 쉬운 작업은 아니다.
- 개발 비용 : 코드에 DSL을 추가하는 작업은 초기 프로젝트에 많은 비용과 시간이 소모된다. 또한 DSL 유지보수와 변경은 프로젝트에 부담을 주는 요소다.
- 추가 우회 계층 : DSL은 추가적인 계층으로 도메인 모델을 감싸며 이때 계층을 최대한 작게 만들어 성능 문제를 회피한다.
- 새로 배워야 하는 언어 : DSL을 프로젝트에 추가하면서 팀이 배워야 하는 언어가 한 개 더 늘어난다는 부담이 있다.
- 호스팅 언어 한계 : 일부 자바 같은 범용 프로그래밍 언어는 장황하고 엄격한 문법을 가졌다. 이런 언어로는 사용자 친화적 DSL을 만들기가 힘들다.
JVM에서 이용할 수 있는 다른 DSL 해결책
내부 DSL
내부 DSL이란 자바로 구현한 DSL을 의미한다. 람다 표현식이 등장하며 쉽고 간단하며 표현력 있는 DSL을 만들 수 있게 되었다. 익명 내무 클래스를 사용하는 것보다 람다를 사용하면 신호 대비 잡음 비율을 적정 수준으로 유지하는 DSL을 만들 수 있다.
- 외부 DSL에 비해 새로운 패턴과 기술을 배워 DSL을 구현할 필요가 없다
- 순수 자바로 DSL을 구현하면 나머지 코드와 함께 DSL을 컴파일할 수 있다.
- 개발팀이 새로운 언어를 배울 필요가 없다.
- 기존 자바 IDE를 통해 자동 완성, 자동 리팩터링 같은 기능을 그대로 사용할 수 있다.
다중 DSL
같은 자바 바이트코드를 사용하는 JVM 기반 프로그래밍 언어를 이용하여 DSL을 만들 수 있다.
- 문법적 잡음이 없으며 개발자가 아닌 사람도 코드를 쉽게 이해할 수 있다.
- 자바 언어가 가지는 한계를 넘을 수 있다 (스칼라는 꼬리 호출 최적화를 통해 함수 호출을 스택에 추가하지 않는다).
- 누군가가 해당 언어에 대해 고급 기술을 사용할 수 있을 정도의 충분한 지식을 가지고 있어야 한다.
- 두 개 이상의 언어가 혼재하므로 여러 컴파일러로 소스를 빌드하도록 빌드 과정을 개선해야 한다.
- 호환성 문제를 고려해야한다
외부 DSL
자신만의 문법과 구문으로 새 언어를 설계해야 한다.
최신 자바 API의 작은 DSL
- 스트림 API는 컬렉션을 조작하는 DSL
- 데이터를 수집하는 DSL인 Collectors
자바로 DSL을 만드는 패턴과 기법
메서드 체인
- 메서드 체인을 통해 객체를 만든다.
- 플루언트 API로 도메인 객체를 만드는 몇 개의 빌더를 구현해야 한다.
- 사용자가 미리 지정된 절차에 따라 설정하도록 한다.
- 사용한 파라미터가 빌더 내부로 국한된다.
- 정적 메서드 사용을 최소화하고 메서드 이름이 인수의 이름을 대신하도록 만들어 가독성이 좋다.
- 빌더를 구현하여야만 한다.
중첩된 함수 이용
- 다른 함수 안에 함수를 이용하여 도메인 모델을 만든다.
- DSL에 더 많은 괄호를 사용해야 한다
- 인수 목록을 정적 메서드에 넘겨주어야 한다
- 인수의 의미가 이름이 아니라 위치에 의해 정의된다
람다 표현식을 이용한 함수 시퀀싱
- 메서드 체인 패턴처럼 플루언트 방식으로 정의 가능하다
- 중첩 함수 형식처럼 람다 표현식의 중첩 수준과 비슷하게 도메인 객체의 계층 구조를 유지한다
- 많은 설정 코드가 필요하며, 람다 표현식 문법에 의한 잡음의 영향을 받는다.