다른 질문은 "데이터베이스에서 오는 데이터 유형을 내 REST 끝점에서 요청할 때 간단하고 원시로 유지해야합니까?"데이터베이스에서 검색된 데이터의 유효성을 얼마나 신뢰할 수 있습니까?
데이터베이스에 저장하려는이 사례 클래스를 상상해보십시오. 행으로 :
case class Product(id: UUID,name: String, price: BigInt)
그것은 분명히 아니고, name
및 price
의 유형 서명 거짓말 때문에 그것이 말하는해서는 안됩니다. 그래서 지금의 과정을 (간단히하기 위해 우리의 유일한 관심사는 price
데이터 형식 상상)
case class Price(value: BigInt) {
require(value > BigInt(0))
}
object Price {
def validate(amount: BigInt): Either[String,Price] =
Try(Price(amount)).toOption.toRight("invalid.price")
}
//As a result my Product class is now:
case class Product(id: UUID,name: String,price: Price)
:
그래서 우리가하는 일은 사용자 정의 데이터보다 같은 어떤 것을 표현 유형을 만들 수있다 제품 데이터에 대한 사용자 입력을 복용하는 것은 같을 것이다 : 트리플 물음표 (???
)에서
//this class would be parsed from i.e a form:
case class ProductInputData(name: String, price: BigInt)
def create(input: ProductInputData) = {
for {
validPrice <- Price.validate(input.price)
} yield productsRepo.insert(
Product(id = UUID.randomUUID,name = input.name,price = ???)
)
}
보세요. 이것이 전체 애플리케이션 아키텍처 측면에서 우려 할 사항입니다. 데이터베이스에 Price
이라는 열을 저장할 수있는 능력이 있다면 (예 : slick
은 이러한 사용자 지정 데이터 형식을 지원합니다) 그러면 가격을 price : BigInt = validPrice.value
또는 price: Price = validPrice
으로 저장할 수 있습니다.
나는이 결정들 모두에서 많은 찬반 양론들을 보았고 나는 결정할 수 없다. 간단한 데이터베이스 유형과 같은
저장 데이터 (즉, BigInt
) 이유는 다음과 같습니다 :
성능 :
Price
의 창조에x > 0
의 단순한 주장 는 여기에 내가 각 선택을 지원하는 참조 인수입니다 간단하지만 복잡한 정규 표현식을 사용하는 유형의 사용자 정의를 확인하려고한다고 가정 해보십시오. 그것은공차 부패에 대해 컬렉션의 검색시 유해 할 것 :
BigInt
가 음의 값으로 삽입되어있는 경우는 당신의 얼굴에서 응용 프로그램은 단순히 열을 읽고에 그것을 밖으로 던져하려고 할 때마다 폭발 would't 사용자 인터페이스 그러나 검색된 다음 구입과 같은 일부 도메인 계층 처리에 관여하면 문제가 발생할 수 있습니다.
데이터 저장이 때문에 도메인 풍부한 유형 (즉, Price
)입니다 같이
- 없음 암시 적 추론하지 않고 신뢰 : 다른 방법으로 가격을 필요가 시스템의 다른 어떤 장소를 유효한.예를 들면 :
//two terrible variations of a calculateDiscount method:
//this version simply trusts that price is already valid and came from db:
def calculateDiscount(price: BigInt): BigInt = {
//apply some positive coefficient to price and hopefully get a positive
//number from it and if it's not positive because price is not positive then
//it'll explode in your face.
}
//this version is even worse. It does retain function totality and purity
//but the unforgivable culture it encourages is the kind of defensive and
//pranoid programming that causes every developer to write some guard
//expressions performing duplicated validation All over!
def calculateDiscount(price: BigInt): Option[BigInt] = {
if (price <= BigInt(0))
None
else
Some{
//Do safe processing
}
}
//ideally you want it to look like this:
def calculateDiscount(price: Price): Price
- 정수를 단순한 형식으로 도메인 형식 변환 및 그 반대 : 표시, 저장 영역 층과 같은 대; 당신은 단순히 그것들 모두를 지배하는 하나의 표현을 시스템에 가지고 있습니다.
내가보기에이 모든 혼란의 근원은 데이터베이스입니다. 사용자가 데이터를 가져 오는 것이 쉬운 일일 경우 : 을 신뢰할 수 없도록하는 것입니다. 당신은 단순한 데이터 타입을 유효성 검사를 통해 도메인 타입으로 캐스팅하고 진행하도록 요청합니다. 하지만 DB는 아니야. 현대의 계층화 된 아키텍처가이 문제를 확실하게 또는 최소한 완화 방법으로 해결합니까?
사용자 입력에 대한 신뢰도를 말할 것입니다. 소스 데이터베이스의 이상 변이에 대한 변환 규칙을 가지고 있거나 원본 데이터베이스를 먼저 정규화 할 수 있습니다. –
@plalx @RobertUdah 그러면 도메인을 처리하기 위해 가져온 값을'''BigInt'''로 저장한다고 가정합니다. 나는'''Price.validate (fetchedBigInt)''''''Price.apply (fetchedBigint)''두 가지 옵션을 가지고 있습니다. 전자는 예외를 던지지 않을 것이지만 편집증 프로그래밍의 범주에 넣습니다. 검색시 그것을 검증함으로써 당신은 거기에 놓인'''productService class''를 신뢰하지 않는다고 말하고 있습니다. 후자의 문제는 아무도 요즘 좋아하지 않는 예외를 던질 수 있다는 것입니다. 어느 쪽을 선택 하시겠습니까? – shayan
@shayan DB의 값이 매핑하려고하는 유형에 맞지 않으면 스토리지 라이브러리가 항상 던져 버리지 않습니까? 아니면 놓친 것이 있습니까? – guillaume31