함수형 프로그래밍의 맥락에서 빈혈 모델에 대해 말하는 것이 여전히 유효합니까?


38

대부분의 DDD 전술 디자인 패턴은 객체 지향 패러다임에 속하며 빈혈 모델은 모든 비즈니스 로직이 객체가 아닌 서비스에 배치되어 상황을 일종의 DTO로 만드는 상황을 설명합니다. 즉, 빈혈 모델은 절차 적 스타일의 동의어이며 복잡한 모델에는 권장되지 않습니다.

순수한 함수형 프로그래밍에 익숙하지는 않지만 DDD가 FP 패러다임에 어떻게 적용되는지, 그리고 그 경우 '애 네임 모델'이라는 용어가 여전히 존재하는지 알고 싶습니다.

업데이트 : Recentlry 가 주제에 관한 비디오 를 출판했습니다 .


1
여기에서 내가 생각하는 바를 말하는 경우 DTO는 빈혈이지만 DDD의 최고급 개체이며 DTO와이를 처리하는 서비스간에 자연스럽게 분리되어 있습니다. 원칙적으로 동의합니다. 이 블로그 게시물도 마찬가지 입니다.
Robert Harvey


1
"이러한 경우 '무기 모델'이라는 용어가 여전히 존재하는지 여부"짧은 대답으로, 빈곤 모델 용어는 OO와 관련하여 만들어졌습니다. FP의 맥락에서 빈혈 모델을 말하는 것은 전혀 의미가 없습니다. 관용적 FP가 무엇인지 설명하는 의미에서 동등 할 수 있지만 빈혈 모델과는 아무런 관련이 없습니다.
plalx

5
에릭 에반스는 자신의 책에서 묘사하는 것이 좋은 객체 지향 디자인이라고 비난하는 사람들에게 한 번 물었다. 몇 가지 레시피와 패턴을 내려 놓고 이름을 붙여서 더 쉽게 따라하고 이야기 할 수 있습니다. 따라서 DDD가 OOD와 연결되어 있다는 것은 놀라운 일이 아닙니다. 더 큰 문제는 OOD와 FPD의 교차점과 차이점이 무엇인지에 대한 것이지만 "기능적 프로그래밍"의 의미를 먼저 정의해야합니다.
Jörg W Mittag

2
@ JörgWMittag : 당신은 일반적인 정의 이외의 의미입니까? 하스켈이 가장 명백한 예시 플랫폼이 많이 있습니다.
Robert Harvey

답변:


22

"무기 모델"문제가 설명 된 방식은 그대로 FP로 잘 변환되지 않습니다. 먼저 적절하게 일반화해야합니다. 핵심적으로, 빈혈 모델은 모델 자체에 의해 캡슐화되지 않은 모델을 올바르게 사용하는 방법에 대한 지식이 포함 된 모델입니다. 대신, 그 지식은 관련 서비스 더미에 퍼져 있습니다. 이러한 서비스는 모델의 고객 이어야 하지만 빈혈로 인해 해당 모델에 대한 책임 이 있습니다. 예를 들어, Account클래스를 통해 처리되지 않으면 계정을 활성화 또는 비활성화하는 데 사용할 수없는 클래스 나 계정에 대한 정보를 검색 할 수 있는 클래스를 생각해보십시오 AccountManager. 계정은 외부 관리자 클래스가 아닌 기본 작업을 담당해야합니다.

함수형 프로그래밍에서 데이터 유형이 모델링 할 대상을 정확하게 나타내지 않을 때 비슷한 문제가 발생합니다. 사용자 ID를 나타내는 유형을 정의해야한다고 가정하십시오. "무의미한"정의는 사용자 ID가 문자열임을 나타냅니다. 기술적으로는 가능하지만 사용자 ID 임의의 문자열처럼 사용 되지 않기 때문에 큰 문제가 발생 합니다. 그것들을 연결하거나 부분 문자열을 분리하는 것은 의미가 없으며, 유니 코드는 중요하지 않으며 엄격한 문자 및 형식 제한이있는 URL 및 기타 컨텍스트에 쉽게 포함 할 수 있어야합니다.

이 문제를 해결하는 것은 보통 몇 단계로 이루어집니다. 간단한 첫 번째 방법은 "글쎄, a UserID는 문자열과 동일하게 표현되지만 형식다르므로 다른 형식 을 사용할 수 없습니다."라고 말합니다. Haskell (및 기타 유형화 된 기능 언어)은 다음을 통해이 기능을 제공합니다 newtype.

newtype UserID = UserID String

이것은 UserID주어진 String구조 에 의해 유형 시스템에 의해 처리 되는 값을 제공 UserID하지만 여전히 String런타임에 있는 값을 생성 하는 함수를 정의 합니다. 이제 함수는 UserID문자열 대신에 필요한 것을 선언 할 수 있습니다 . UserID이전에 문자열을 사용했던 곳에서 s를 사용 하면 두 코드를 UserID함께 연결하는 코드로부터 보호 됩니다. 타입 시스템 테스트가 필요하지 않은 것을 보장 합니다.

여기에 약점이 코드는 여전히 임의 걸릴 수 있다는 것입니다 String같은를 "hello"하고를 구축 UserID그것에서. 추가 단계에는 문자열이 제공 될 때 일부 불변량을 검사 UserID하고 만족할 경우 에만 a를 반환하는 "스마트 생성자"함수 작성이 포함됩니다 . 그런 다음 "바보" UserID클라이언트가 원하는 경우 생성자는 개인 그래서를 만들어 UserID있어야합니다 함으로써 존재로 오는 잘못된 사용자 ID를 방지, 스마트 생성자를 사용합니다.

추가 단계는 단순히 정의에 의해 잘못되거나 "부적절한"것을 구성 UserID하는 것이 불가능한 방식으로 데이터 유형 을 정의합니다. 예를 들어 UserID숫자를 숫자 목록으로 정의하면 다음 과 같습니다.

data Digit = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine
data UserID = UserID [Digit]

UserID숫자 목록 을 구성하려면 숫자 목록을 제공해야합니다. 이러한 정의가 주어지면 UserIDURL로 표현할 수없는 존재가 불가능하다는 것을 보여주는 것은 사소한 일 입니다. Haskell에서 이와 같은 데이터 모델을 정의하는 것은 종종 데이터 시스템 및 GADT (Generalized Algebraic Data Types) 와 같은 고급 유형 시스템 기능 을 통해 지원되므로 유형 시스템이 코드에 대해 더 많은 불변을 정의하고 증명할 수 있습니다. 데이터와 동작이 분리되면 데이터 정의 만 동작을 강제해야합니다.


2
그리고 불변을 지키는 골재와 골재는 어떻습니까? 마지막으로도 개발자가 이해하고 쉽게 이해할 수 있습니까? 저에게 DDD의 가장 중요한 부분은 비즈니스 모델을 코드에 직접 매핑하는 것입니다. 그리고 당신은 그것에 대해 정확히 대답합니다.
Pavel Voronin

2
좋은 연설이지만 OP 질문에 대한 대답은 없습니다.
SerG September

10

대부분의 경우, 불변성은 OOP가 주장하는 것처럼 함수와 데이터를 밀접하게 연결할 필요가 없습니다. 원본 데이터 구조가 예기치 않게 변경 될 염려없이 원본 코드에서 멀리 떨어진 코드로 파생 데이터 구조를 만들더라도 원하는만큼 사본을 만들 수 있습니다.

그러나이 비교를 수행하는 더 좋은 방법은 모델 계층 에 어떤 기능을 서비스 계층에 할당 하는지를 보는 것입니다 . OOP와 동일하게 보이지는 않지만 FP에서 여러 레벨의 추상화가 필요한 기능을 하나의 함수로 작성하려고 시도하는 것은 FP에서 매우 일반적인 실수입니다.

내가 아는 한, OOP 용어이기 때문에 아무도 빈혈 모델이라고 부르지 않지만 그 효과는 동일합니다. 해당되는 경우 일반 기능을 재사용 할 수 있으며 재사용해야하지만,보다 복잡한 작업 또는 애플리케이션 별 작업에는 모델 작업을위한 다양한 기능을 제공해야합니다. 적절한 추상화 레이어를 만드는 것은 모든 패러다임에서 좋은 디자인입니다.


2
"불변성으로 인해 OOP가 주장하는 것처럼 데이터와 함수를 밀접하게 연결할 필요가 없습니다.": 데이터와 프로 시저를 결합하는 또 다른 이유는 동적 디스패치를 ​​통해 다형성을 구현하는 것입니다.
조르지오

2
DDD의 맥락에서 데이터와 동작을 결합하는 주요 이점은 의미있는 비즈니스 관련 인터페이스를 제공한다는 것입니다. 항상 손에 있습니다. 우리는 코드를 문서화하는 자연스러운 방법을 가지고 있으며 (적어도 내가 익숙한 것임) 이것이 비즈니스 전문가와의 성공적인 의사 소통의 열쇠입니다. 그러면 FP에서 어떻게 달성됩니까? 아마도 파이핑이 도움이 될 것입니다. FP의 일반적인 특성으로 인해 비즈니스 요구 사항이 코드에서 리버스 엔지니어링하기가 더 어려워지지 않습니까?
Pavel Voronin

7

OOP에서 DDD를 사용할 때 비즈니스 논리를 도메인 오브젝트 자체에 두어야하는 주된 이유 중 하나는 일반적으로 오브젝트의 상태를 변경하여 비즈니스 논리가 적용되기 때문입니다. 이것은 캡슐화와 관련이 있습니다. Employee.RaiseSalary아마도 인스턴스 의 salary필드를 변경시킬 Employee수 있으며 공개적으로 설정해서는 안됩니다.

FP에서는 돌연변이가 방지되므로 RaiseSalary기존 Employee인스턴스 를 가져 와서 새 급여와 함께 Employee 인스턴스를 반환하는 함수를 만들어이 동작을 구현해야 합니다 . 따라서 원래 개체에서 읽고 새 개체를 만드는 것만으로 돌연변이가 발생하지 않습니다. 이런 이유로, 그러한 RaiseSalary함수는 Employee클래스 의 메소드로 정의 될 필요는 없지만 어느 곳에서나 살 수 있습니다.

이 경우, 데이터를 동작과 분리하는 것이 자연스러워집니다. 하나의 구조는 Employee데이터 (완전 빈혈)를 나타내며, 하나 (또는 ​​여러) 모듈은 해당 데이터에서 작동하는 기능 (불변성을 보존)을 포함합니다.

DDD에서와 같이 데이터와 동작을 결합 할 때 일반적으로 단일 책임 원칙 (SRP)을 위반합니다 Employee. 급여 변경 규칙이 변경되면 변경해야 할 수도 있습니다. 그러나 EOY 보너스 계산 규칙이 변경되면 변경해야 할 수도 있습니다. 분리 된 접근 방식을 사용하는 경우에는 한 가지 책임이있는 여러 개의 모듈을 가질 수 있으므로 그렇지 않습니다.

따라서 평소와 같이 FP 방식은 더 큰 모듈성 / 구성 성을 제공합니다.


-1

문제의 본질은 모델에서 작동하는 서비스의 모든 도메인 로직을 갖춘 빈혈 모델이 기본적으로 절차 적 프로그래밍이라는 것입니다. 데이터가 아닌 "스마트 한"객체가있는 "실제"OO 프로그래밍과는 대조적입니다. 또한 데이터와 가장 밀접한 관련이있는 논리.

함수 프로그래밍과 동일한 대조가 존재합니다. "실제"FP는 함수를 일류 엔티티로 사용하는 것을 의미합니다.이 엔티티는 매개 변수로 전달되고 즉시 구성되어 리턴 값으로 리턴됩니다. 그러나 그 모든 힘을 사용하지 못하고 그 사이에 전달되는 데이터 구조에서 작동하는 기능 만 가지고 있으면 같은 위치에있게됩니다. 기본적으로 절차 프로그래밍을 수행하는 것입니다.


5
그렇습니다. 기본적으로 OP가 그의 질문에서 말하는 것입니다. 둘 다 여전히 기능적 구성
Robert Harvey

-3

DDD가 FP 패러다임에 어떻게 적합한 지 알고 싶습니다

필자는 그것이 불변의 Value Objects 사이의 전환에 대한 전술적 접근법이나 엔티티에서 메소드 를 트리거 하는 방법으로 생각 합니다 . (대부분의 논리가 여전히 엔티티에 존재하는 경우)

그 경우에 '무기 모델'이라는 용어가 여전히 존재하는지 여부.

"전통적인 OOP와 유사한 방식"을 의미한다면 일반적인 구현 세부 사항을 무시하고 기본 사항으로 돌아가는 데 도움이됩니다. 도메인 전문가는 어떤 언어를 사용합니까? 사용자의 의도 는 무엇입니까 ?

그들이 함께 프로세스와 기능을 체인에 대해 이야기 가정하면, 그것은 기능을 보인다 (또는 적어도 "할 - 어"객체)를 기본적으로 있는 도메인-객체는!

따라서이 시나리오에서 "함수"가 실제로 실행 가능 하지 않고 실제 작업을 수행하는 서비스에서 해석 하는 메타 데이터의 별자리 일 때 "무기 모델"이 발생할 수 있습니다 .


1
빈약 한 모델은 튜플, 레코드 또는 목록과 같은 추상 데이터 유형을 처리를 위해 다른 함수로 전달할 때 발생합니다. "실행하지 않는 함수"(그것이 무엇이든)만큼 이국적인 것은 필요하지 않습니다.
Robert Harvey

따라서 "기능"에 대한 인용 부호는 빈혈이있을 때 라벨이 얼마나 부적절하게되는지를 강조합니다.
Darien

아이러니하고 있다면 조금 미묘합니다.
Robert Harvey
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.