2017-11-06 6 views
0

私の質問をする他の方法は次のようなものです。「データベースからのデータ型を、RESTエンドポイントから簡単なものとして取得する必要がありますか」データベースから取得したデータの有効性をどの程度信用するべきですか?

データベースに保存したいこのクラスを想像してみてください行として:

case class Product(id: UUID,name: String, price: BigInt) 

それは明確ではありません、それはnamepriceの型シグネチャが嘘であるため、それは言うべきではありません。

ので、私たちがやっていることは、より良いようなものが何であるかを表すカスタムデータ型を作成されています。今では(単純化のために私たちの唯一の懸念は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つの表現があります。

私が見るこの混乱の原因は、データベースです。ユーザーからのデータが届いていれば、それは簡単でしょう:を信用することは決してありません。単純なデータ型を検証してドメイン型にキャストしてから処理を進めてください。しかし、データベースではありません。現代の階層化されたアーキテクチャは、この問題にいくつかの決定的な、あるいは少なくとも緩和的な方法で対処していますか?

+0

私はあなたのユーザー入力と同じくらい信頼しています。 –

+0

@plalx @RobertUdahそうですね、私が価格を '' 'BigInt'''として保存していると仮定すると、ドメイン内で処理するためにそれを取得するとき'' 'Price.validate(fetchedBigInt)' '' '' 'Price.apply(fetchedBigint)' 'の2つのオプションがあります。前者は例外をスローしませんが、私はそれを編集的プログラミングのカテゴリに入れます。取得時にそれを検証することによって、そこに置かれた '' 'productService class''を信頼していないと言っています。後者の問題は、誰も最近好きではない例外をスローする可能性があるということです。どちらを選ぶでしょうか? – shayan

+0

@shayan DBの値がマップしようとしているタイプに合わない場合は、ストレージライブラリは常にスローされませんか? – guillaume31

答えて

2
  1. データベースの整合性を保護します。オブジェクトの内部状態の完全性を保護するのと同じように。
  2. データベースを信頼してください。すでにチェックインされているものをチェックして再チェックするのは意味がありません。
  3. できるだけ長くドメインオブジェクトを使用してください。それらを与える最後の瞬間まで(生のJDBCコードまたはデータがレンダリングされる直前まで)待ってください。
  4. 破損したデータを許容しないでください。データが破損している場合、アプリケーションはクラッシュするはずです。それ以外の場合は、より壊れたデータが生成される可能性があります。

DBからの取得時のオーバーヘッドは無視されます。実際にそれが問題だと思われる場合は、2つのコンストラクタを用意します.1つはユーザーからのデータ(検証を実行する)と、データが良好である(データベースコードによって使用されることを前提とする)コンストラクタです。

私はバグを指しているときに例外が大好きです(途中で検証が不十分なためにデータが破損しています)。

私は定期的にrequiresをコードに残して、より複雑な検証(複数のテーブルからのデータが不正な方法で結合されている可能性がある)でバグを見つけやすくします。システムはまだクラッシュする必要がありますが、より良いエラーメッセージが表示されます。

+0

私は特にすべての点が好きです#4 – shayan

関連する問題