私の質問をする他の方法は次のようなものです。「データベースからのデータ型を、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
のいずれかとするオプションがあることを意味します。
私はこれらの決定の両方で多くの賛否両論を見るので、私は決定できません。
パフォーマンス:
Price
の作成時にx > 0
の簡単なアサーションので、簡単なデータベースの種類(すなわちBigInt
)としてストアデータ:ここ は、私は、各選択肢をサポートする参照引数ですあなたは複雑な正規表現でカスタム
Email
型を検証したいと考えています。それは公差腐敗に対してコレクションの検索時に有害である:
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
- 定数を単純型のドメインタイプの変換およびその逆:表現、ストレージ、ドメイン層などのために、それらをすべて統治するためには、システム内に1つの表現があります。
私が見るこの混乱の原因は、データベースです。ユーザーからのデータが届いていれば、それは簡単でしょう:を信用することは決してありません。単純なデータ型を検証してドメイン型にキャストしてから処理を進めてください。しかし、データベースではありません。現代の階層化されたアーキテクチャは、この問題にいくつかの決定的な、あるいは少なくとも緩和的な方法で対処していますか?
私はあなたのユーザー入力と同じくらい信頼しています。 –
@plalx @RobertUdahそうですね、私が価格を '' 'BigInt'''として保存していると仮定すると、ドメイン内で処理するためにそれを取得するとき'' 'Price.validate(fetchedBigInt)' '' '' 'Price.apply(fetchedBigint)' 'の2つのオプションがあります。前者は例外をスローしませんが、私はそれを編集的プログラミングのカテゴリに入れます。取得時にそれを検証することによって、そこに置かれた '' 'productService class''を信頼していないと言っています。後者の問題は、誰も最近好きではない例外をスローする可能性があるということです。どちらを選ぶでしょうか? – shayan
@shayan DBの値がマップしようとしているタイプに合わない場合は、ストレージライブラリは常にスローされませんか? – guillaume31