การตรวจสอบบริบทในการออกแบบที่ขับเคลื่อนด้วยโดเมน
เผยแพร่แล้ว: 2022-03-11การออกแบบที่ขับเคลื่อนด้วยโดเมน (เรียกสั้นๆ ว่า DDD) ไม่ใช่เทคโนโลยีหรือวิธีการ DDD จัดเตรียมโครงสร้างของแนวทางปฏิบัติและคำศัพท์สำหรับการตัดสินใจออกแบบที่มุ่งเน้นและเร่งโครงการซอฟต์แวร์ที่เกี่ยวข้องกับโดเมนที่ซับซ้อน ตามที่ Eric Evans และ Martin Fowler อธิบายไว้ ออบเจ็กต์โดเมนเป็นที่สำหรับวางกฎการตรวจสอบความถูกต้องและตรรกะทางธุรกิจ
เอริค อีแวนส์:
Domain Layer (หรือ Model Layer): รับผิดชอบในการนำเสนอแนวคิดของธุรกิจ ข้อมูลเกี่ยวกับสถานการณ์ทางธุรกิจ และกฎเกณฑ์ทางธุรกิจ สถานะที่สะท้อนถึงสถานการณ์ทางธุรกิจถูกควบคุมและใช้งานที่นี่ แม้ว่ารายละเอียดทางเทคนิคของการจัดเก็บจะถูกมอบหมายให้กับโครงสร้างพื้นฐานก็ตาม เลเยอร์นี้เป็นหัวใจสำคัญของซอฟต์แวร์ธุรกิจ
มาร์ติน ฟาวเลอร์:
ตรรกะที่ควรอยู่ในวัตถุโดเมนคือตรรกะของโดเมน - การตรวจสอบ การคำนวณ กฎเกณฑ์ทางธุรกิจ - อะไรก็ได้ที่คุณต้องการเรียก
การตรวจสอบความถูกต้องทั้งหมดในวัตถุโดเมนส่งผลให้วัตถุโดเมนขนาดใหญ่และซับซ้อนใช้งานได้ โดยส่วนตัวแล้ว ฉันชอบแนวคิดของการแยกส่วนการตรวจสอบความถูกต้องของโดเมนออกเป็นส่วนประกอบตัวตรวจสอบความถูกต้องที่แยกจากกัน ซึ่งสามารถนำมาใช้ซ้ำได้ทุกเมื่อ และจะขึ้นอยู่กับบริบทและการดำเนินการของผู้ใช้
ดังที่ Martin Fowler เขียนไว้ในบทความที่ยอดเยี่ยม: ContextualValidation
สิ่งหนึ่งที่ฉันเห็นผู้คนทั่วไปทำคือการพัฒนาขั้นตอนการตรวจสอบความถูกต้องสำหรับออบเจกต์ กิจวัตรเหล่านี้มีหลายวิธี อาจอยู่ในวัตถุหรือภายนอก อาจส่งคืนบูลีน หรือส่งข้อยกเว้นเพื่อบ่งชี้ความล้มเหลว สิ่งหนึ่งที่ฉันคิดว่าทำให้ผู้คนสะดุดอยู่ตลอดเวลาคือเมื่อพวกเขาคิดถึงความถูกต้องของวัตถุในวิธีที่ไม่ขึ้นกับบริบท เช่น วิธี isValid บอกเป็นนัย […] ฉันคิดว่ามันมีประโยชน์มากกว่าที่จะคิดว่าการตรวจสอบความถูกต้องเป็นสิ่งที่เชื่อมโยงกับบริบท ซึ่งโดยทั่วไปแล้วเป็นการกระทำที่คุณต้องการทำ เช่น การถามว่าคำสั่งซื้อนี้ถูกต้องหรือไม่ หรือลูกค้ารายนี้มีสิทธิ์เช็คอินที่โรงแรม ดังนั้นแทนที่จะมีวิธีเช่น isValid ให้มีวิธีเช่น isValidForCheckIn
ข้อเสนอการตรวจสอบความถูกต้องของการดำเนินการ
ในบทความนี้ เราจะใช้อินเทอร์เฟซอย่างง่าย ItemValidator ซึ่งคุณต้องใช้วิธีการ ตรวจสอบความถูกต้อง ด้วยประเภทการส่งคืน ValidationResult ValidationResult เป็นอ็อบเจ็กต์ที่มีรายการที่ได้รับการตรวจสอบแล้วและยังมีอ็อบเจ็กต์ Messages ส่วนหลังมีข้อผิดพลาด คำเตือน และสถานะการตรวจสอบข้อมูล (ข้อความ) ที่สะสมอยู่ซึ่งขึ้นอยู่กับบริบทการดำเนินการ
ตัวตรวจสอบความถูกต้องเป็นส่วนประกอบที่แยกส่วนซึ่งสามารถนำมาใช้ซ้ำได้ง่ายทุกที่ที่ต้องการ ด้วยวิธีการนี้ การอ้างอิงทั้งหมดที่จำเป็นสำหรับการตรวจสอบความถูกต้อง จึงสามารถฉีดเข้าไปได้อย่างง่ายดาย ตัวอย่างเช่น ในการตรวจสอบฐานข้อมูลว่ามีผู้ใช้รายใดที่ใช้อีเมลที่ระบุเฉพาะ UserDomainService เท่านั้น
การแยกตัวตรวจสอบความถูกต้องจะเป็นไปตามบริบท (การดำเนินการ) ดังนั้นหากการกระทำ UserCreate และการกระทำ UserUpdate จะมีส่วนประกอบที่แยกจากกันหรือการกระทำอื่นใด (UserActivate, UserDelete, AdCampaignLaunch เป็นต้น) การตรวจสอบความถูกต้องก็จะเติบโตอย่างรวดเร็ว
ตัวตรวจสอบการดำเนินการแต่ละคนควรมีรูปแบบการดำเนินการที่สอดคล้องกันซึ่งจะมีเฉพาะช่องการดำเนินการที่อนุญาตเท่านั้น สำหรับการสร้างผู้ใช้ จำเป็นต้องมีฟิลด์ต่อไปนี้:
ผู้ใช้สร้างโมเดล:
{ "firstName": "John", "lastName": "Doe", "email": "[email protected]", "password": "MTIzNDU=" }
และในการอัปเดตผู้ใช้ ต่อไปนี้จะได้รับอนุญาต externalId , firstName และ LastName externalId ใช้สำหรับระบุผู้ใช้และ อนุญาตให้เปลี่ยนชื่อและนามสกุล เท่านั้น
ผู้ใช้อัปเดตรุ่น:
{ "externalId": "a55ccd60-9d82-11e5-9f52-0002a5d5c51b", "firstName": "John Updated", "lastName": "Doe Updated" }
สามารถใช้การตรวจสอบความถูกต้องของฟิลด์ร่วมกันได้ ความยาวสูงสุดของชื่อคือ 255 อักขระเสมอ
ในระหว่างการตรวจสอบ ขอแนะนำให้ไม่เพียงแต่ได้รับข้อผิดพลาดแรกที่เกิดขึ้นเท่านั้น แต่ยังรวมถึงรายการปัญหาทั้งหมดที่พบด้วย ตัวอย่างเช่น ปัญหา 3 ประการต่อไปนี้อาจเกิดขึ้นพร้อมกันและสามารถรายงานได้ในระหว่างการดำเนินการ:
- รูปแบบที่อยู่ไม่ถูกต้อง [ข้อผิดพลาด]
- อีเมลต้องไม่ซ้ำกันระหว่างผู้ใช้ [ข้อผิดพลาด]
- รหัสผ่านสั้นเกินไป [ข้อผิดพลาด]
เพื่อให้บรรลุการตรวจสอบดังกล่าว จำเป็นต้องมีบางอย่าง เช่น ตัวสร้างสถานะการตรวจสอบ และเพื่อจุดประสงค์ดังกล่าว จึงมีการแนะนำ ข้อความ ข้อความ เป็นแนวคิดที่ฉันได้ยินจากที่ปรึกษาที่ยอดเยี่ยมเมื่อหลายปีก่อน เมื่อเขาแนะนำข้อความนี้เพื่อสนับสนุนการตรวจสอบความถูกต้องและสำหรับสิ่งอื่น ๆ ที่สามารถทำได้ เนื่องจากข้อความไม่ได้มีไว้สำหรับการตรวจสอบเท่านั้น
โปรดทราบว่าในหัวข้อต่อไปนี้ เราจะใช้ Scala เพื่อแสดงตัวอย่างการใช้งาน ในกรณีที่คุณไม่ใช่ผู้เชี่ยวชาญของ Scala อย่ากลัวที่จะทำตามได้ง่าย
ข้อความในการตรวจสอบบริบท
ข้อความ เป็นวัตถุที่แสดงถึงตัวสร้างสถานะการตรวจสอบความถูกต้อง เป็นวิธีที่ง่ายในการรวบรวมข้อผิดพลาด คำเตือน และข้อความข้อมูลระหว่างการตรวจสอบ แต่ละอ็อบเจ็กต์ Messages มีคอลเลกชั่นภายในของออบเจกต์ Message และยังสามารถมีการอ้างอิงไปยังอ็อบเจ็กต์ parentMessages ได้อีกด้วย
ออบเจ็กต์ข้อความเป็นอ็อบเจ็กต์ที่สามารถมี type , messageText , คีย์ (ซึ่งเป็นทางเลือก และใช้เพื่อสนับสนุนการตรวจสอบความถูกต้องของอินพุตเฉพาะซึ่งระบุโดยตัวระบุ) และสุดท้าย childMessages ที่พิสูจน์วิธีที่ยอดเยี่ยมในการสร้างแผนผังข้อความที่เขียนได้
ข้อความสามารถเป็นประเภทใดประเภทหนึ่งต่อไปนี้:
- ข้อมูล
- คำเตือน
- ข้อผิดพลาด
ข้อความที่มีโครงสร้างแบบนี้ทำให้เราสร้างซ้ำได้และยังช่วยให้ตัดสินใจเกี่ยวกับการดำเนินการถัดไปตามสถานะของข้อความก่อนหน้าได้ ตัวอย่างเช่น การตรวจสอบความถูกต้องระหว่างการสร้างผู้ใช้:
@Component class UserCreateValidator @Autowired (private val entityDomainService: UserDomainService) extends ItemValidator[UserCreateEntity] { Asserts.argumentIsNotNull(entityDomainService) private val MAX_ALLOWED_LENGTH = 80 private val MAX_ALLOWED_CHARACTER_ERROR = s"must be less than or equal to $MAX_ALLOWED_LENGTH character" override def validate(item: UserCreateEntity): ValidationResult[UserCreateEntity] = { Asserts.argumentIsNotNull(item) val validationMessages = Messages.of validateFirstName (item, validationMessages) validateLastName (item, validationMessages) validateEmail (item, validationMessages) validateUserName (item, validationMessages) validatePassword (item, validationMessages) ValidationResult( validatedItem = item, messages = validationMessages ) } private def validateFirstName(item: UserCreateEntity, validationMessages: Messages) { val localMessages = Messages.of(validationMessages) val fieldValue = item.firstName ValidateUtils.validateLengthIsLessThanOrEqual( fieldValue, MAX_ALLOWED_LENGTH, localMessages, UserCreateEntity.FIRST_NAME_FORM_ID.value, MAX_ALLOWED_CHARACTER_ERROR ) } private def validateLastName(item: UserCreateEntity, validationMessages: Messages) { val localMessages = Messages.of(validationMessages) val fieldValue = item.lastName ValidateUtils.validateLengthIsLessThanOrEqual( fieldValue, MAX_ALLOWED_LENGTH, localMessages, UserCreateEntity.LAST_NAME_FORM_ID.value, MAX_ALLOWED_CHARACTER_ERROR ) } private def validateEmail(item: UserCreateEntity, validationMessages: Messages) { val localMessages = Messages.of(validationMessages) val fieldValue = item.email ValidateUtils.validateEmail( fieldValue, UserCreateEntity.EMAIL_FORM_ID, localMessages ) ValidateUtils.validateLengthIsLessThanOrEqual( fieldValue, MAX_ALLOWED_LENGTH, localMessages, UserCreateEntity.EMAIL_FORM_ID.value, MAX_ALLOWED_CHARACTER_ERROR ) if(!localMessages.hasErrors()) { val doesExistWithEmail = this.entityDomainService.doesExistByByEmail(fieldValue) ValidateUtils.isFalse( doesExistWithEmail, localMessages, UserCreateEntity.EMAIL_FORM_ID.value, "User already exists with this email" ) } } private def validateUserName(item: UserCreateEntity, validationMessages: Messages) { val localMessages = Messages.of(validationMessages) val fieldValue = item.username ValidateUtils.validateLengthIsLessThanOrEqual( fieldValue, MAX_ALLOWED_LENGTH, localMessages, UserCreateEntity.USERNAME_FORM_ID.value, MAX_ALLOWED_CHARACTER_ERROR ) if(!localMessages.hasErrors()) { val doesExistWithUsername = this.entityDomainService.doesExistByUsername(fieldValue) ValidateUtils.isFalse( doesExistWithUsername, localMessages, UserCreateEntity.USERNAME_FORM_ID.value, "User already exists with this username" ) } } private def validatePassword(item: UserCreateEntity, validationMessages: Messages) { val localMessages = Messages.of(validationMessages) val fieldValue = item.password ValidateUtils.validateLengthIsLessThanOrEqual( fieldValue, MAX_ALLOWED_LENGTH, localMessages, UserCreateEntity.PASSWORD_FORM_ID.value, MAX_ALLOWED_CHARACTER_ERROR ) } }
เมื่อดูโค้ดนี้ คุณจะเห็นการใช้ ValidateUtils ฟังก์ชันยูทิลิตี้เหล่านี้ใช้เพื่อเติมออบเจ็กต์ Messages ในกรณีที่กำหนดไว้ล่วงหน้า คุณสามารถตรวจสอบการใช้งาน ValidateUtils บนโค้ด Github ได้
ระหว่างการตรวจสอบอีเมล อันดับแรก จะมีการตรวจสอบว่าอีเมลถูกต้องหรือไม่โดยโทรไปที่ ValidateUtils.validateEmail(… และตรวจสอบว่าอีเมลมีความยาวที่ถูกต้องหรือไม่ด้วยการเรียก ValidateUtils.validateLengthIsLessThanOrEqual(… เมื่อตรวจสอบความถูกต้องทั้งสองเสร็จแล้ว ให้ตรวจสอบว่าอีเมล ถูกกำหนดให้กับ User บางรายแล้ว เฉพาะในกรณีที่ผ่านเงื่อนไขการตรวจสอบอีเมลก่อนหน้าและดำเนินการด้วย if(!localMessages.hasErrors()) { … วิธีนี้ทำให้สามารถหลีกเลี่ยงการเรียกฐานข้อมูลราคาแพงได้ นี่เป็นเพียงส่วนหนึ่งของ UserCreateValidator . สามารถดูซอร์สโค้ดที่สมบูรณ์ได้ที่นี่
ขอให้สังเกตว่าหนึ่งในพารามิเตอร์การตรวจสอบความถูกต้องโดดเด่น: UserCreateEntity.EMAIL_FORM_ID พารามิเตอร์นี้เชื่อมต่อสถานะการตรวจสอบกับ ID อินพุตเฉพาะ
ในตัวอย่างก่อนหน้านี้ การดำเนินการถัดไปจะตัดสินใจตามข้อเท็จจริงหากวัตถุ Messages มีข้อผิดพลาด (โดยใช้วิธี hasErrors) สามารถตรวจสอบได้อย่างง่ายดายว่ามีข้อความ "คำเตือน" หรือไม่และลองอีกครั้งหากจำเป็น

สิ่งหนึ่งที่สามารถสังเกตได้คือวิธีการใช้ localMessages ข้อความในเครื่องคือข้อความที่สร้างขึ้นเหมือนกับข้อความใดๆ แต่มีข้อความหลัก จากที่กล่าวมา เป้าหมายคือการมีการอ้างอิงถึงสถานะการตรวจสอบความถูกต้องในปัจจุบันเท่านั้น (ในตัวอย่างนี้ emailValidation) จึงสามารถเรียก localMessages.hasErrors ได้ ซึ่งจะตรวจสอบก็ต่อเมื่อบริบท emailValidation มีข้อผิดพลาดเท่านั้น นอกจากนี้ เมื่อมีการเพิ่มข้อความใน localMessages ก็จะถูกเพิ่มไปยัง parentMessages ด้วย ดังนั้นข้อความตรวจสอบความถูกต้องทั้งหมดจึงมีอยู่ในบริบทที่สูงขึ้นของ UserCreateValidation
ตอนนี้เราได้เห็นการใช้งาน Messages แล้ว ในบทต่อไป เราจะเน้นที่ ItemValidator
ItemValidator - ส่วนประกอบสำหรับตรวจสอบความถูกต้องที่ใช้ซ้ำได้
ItemValidator เป็นลักษณะธรรมดา (อินเทอร์เฟซ) ที่บังคับให้นักพัฒนาใช้วิธีการ ตรวจสอบ ซึ่งจำเป็นต้องส่งคืน ValidationResult
ตรวจสอบรายการ:
trait ItemValidator[T] { def validate(item:T) : ValidationResult[T] }
ผลการตรวจสอบ:
case class ValidationResult[T: Writes]( validatedItem : T, messages : Messages ) { Asserts.argumentIsNotNull(validatedItem) Asserts.argumentIsNotNull(messages) def isValid :Boolean = { !messages.hasErrors } def errorsRestResponse = { Asserts.argumentIsTrue(!this.isValid) ResponseTools.of( data = Some(this.validatedItem), messages = Some(messages) ) } }
เมื่อ ItemValidators เช่น UserCreateValidator ถูกนำไปใช้เป็นส่วนประกอบการฉีดขึ้นต่อกัน วัตถุ ItemValidator จะถูกฉีดและนำกลับมาใช้ใหม่ในวัตถุใดๆ ที่ต้องการการตรวจสอบการกระทำ UserCreate
หลังจากดำเนินการตรวจสอบความถูกต้องแล้ว จะมีการตรวจสอบว่าการตรวจสอบสำเร็จหรือไม่ หากเป็นเช่นนั้น ข้อมูลผู้ใช้จะยังคงอยู่ในฐานข้อมูล แต่ถ้าไม่ใช่การตอบสนอง API ที่มีข้อผิดพลาดในการตรวจสอบความถูกต้องจะถูกส่งกลับ
ในหัวข้อถัดไป เราจะมาดูกันว่าเราจะแสดงข้อผิดพลาดในการตรวจสอบความถูกต้องได้อย่างไรในการตอบสนอง RESTful API และวิธีสื่อสารกับผู้บริโภค API เกี่ยวกับสถานะการดำเนินการของการดำเนินการ
Unified API Response - การโต้ตอบกับผู้ใช้อย่างง่าย
หลังจากตรวจสอบความถูกต้องของการดำเนินการของผู้ใช้แล้ว ในกรณีของเรา การสร้างผู้ใช้ จะต้องแสดงผลลัพธ์การดำเนินการตรวจสอบต่อผู้บริโภค RESTful API วิธีที่ดีที่สุดคือการมีการตอบสนอง API แบบรวมศูนย์ซึ่งจะมีการเปลี่ยนเฉพาะบริบทเท่านั้น (ในแง่ของ JSON ค่าของ "ข้อมูล") ด้วยการตอบกลับแบบรวมศูนย์ ข้อผิดพลาดสามารถแสดงได้อย่างง่ายดายต่อผู้บริโภค RESTful API
โครงสร้างการตอบสนองแบบครบวงจร:
{ "messages" : { "global" : { "info": [], "warnings": [], "errors": [] }, "local" : [] }, "data":{} }
การตอบสนองแบบรวมเป็นหนึ่งมีโครงสร้างให้มีข้อความสองระดับ ส่วนกลางและภายใน ข้อความในเครื่องคือข้อความที่เชื่อมต่อกับอินพุตเฉพาะ เช่น “ชื่อผู้ใช้ยาวเกินไป อนุญาตให้ใช้อักขระได้ไม่เกิน 80 ตัว”_ ข้อความส่วนกลางคือข้อความที่สะท้อนถึงสถานะของข้อมูลทั้งหมดบนหน้า เช่น “ผู้ใช้จะไม่เปิดใช้งานจนกว่าจะได้รับการอนุมัติ” ข้อความท้องถิ่นและข้อความสากลมีสามระดับ - ข้อผิดพลาด คำเตือน และข้อมูล คุณค่าของ "ข้อมูล" มีความเฉพาะเจาะจงกับบริบท เมื่อสร้างผู้ใช้ ฟิลด์ข้อมูลจะมีข้อมูลผู้ใช้ แต่เมื่อได้รับรายชื่อผู้ใช้ ฟิลด์ข้อมูลจะเป็นอาร์เรย์ของผู้ใช้
ด้วยการตอบสนองที่มีโครงสร้างเช่นนี้ ตัวจัดการ UI ของไคลเอ็นต์จึงสร้างขึ้นได้อย่างง่ายดาย ซึ่งจะรับผิดชอบในการแสดงข้อผิดพลาด คำเตือน และข้อความข้อมูล ข้อความส่วนกลางจะแสดงที่ด้านบนของหน้า เนื่องจากเกี่ยวข้องกับสถานะการดำเนินการ API ทั่วโลก และสามารถแสดงข้อความในเครื่องใกล้กับอินพุตที่ระบุ (ฟิลด์) เนื่องจากมีความเกี่ยวข้องโดยตรงกับค่าของฟิลด์ ข้อความแสดงข้อผิดพลาดสามารถแสดงเป็นสีแดง ข้อความเตือนเป็นสีเหลือง และข้อมูลเป็นสีน้ำเงิน
ตัวอย่างเช่น ในแอปไคลเอนต์ที่ใช้ AngularJS เราสามารถมีคำสั่งสองคำสั่งที่รับผิดชอบในการจัดการข้อความตอบกลับในพื้นที่และทั่วโลก เพื่อให้มีเพียงตัวจัดการสองคนนี้เท่านั้นที่สามารถจัดการกับการตอบสนองทั้งหมดในลักษณะที่สอดคล้องกัน
คำสั่งสำหรับข้อความในเครื่องจะต้องนำไปใช้กับองค์ประกอบหลักกับองค์ประกอบจริงที่ถือข้อความทั้งหมด
localmessages.direct.js :
(function() { 'use strict'; angular .module('reactiveClient') .directive('localMessagesValidationDirective', localMessagesValidationDirective); /** @ngInject */ function localMessagesValidationDirective(_) { return { restrict: 'AE', transclude: true, scope: { binder: '=' }, template: '<div ng-transclude></div>', link: function (scope, element) { var messagesWatchCleanUp = scope.$watch('binder', messagesBinderWatchCallback); scope.$on('$destroy', function() { messagesWatchCleanUp(); }); function messagesBinderWatchCallback (messagesResponse) { if (messagesResponse != undefined && messagesResponse.messages != undefined) { if (messagesResponse.messages.local.length > 0) { element.find('.alert').remove(); _.forEach(messagesResponse.messages.local, function (localMsg) { var selector = element.find('[]').parent(); _.forEach(localMsg.info, function (msg) { var infoMsg = '<div class="form-control validation-alert alert alert-info alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + msg + '</div>'; selector.after(infoMsg); }); _.forEach(localMsg.warnings, function (msg) { var warningMsg = '<div class="form-control validation-alert alert alert-warning alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + msg + '</div>'; selector.after(warningMsg); }); _.forEach(localMsg.errors, function (msg) { var errorMsg = '<div class="form-control validation-alert alert alert-danger alert-dismissable"><button type="button" class="close" data-dismiss="alert" aria-hidden="true">×</button>' + msg + '</div>'; selector.after(errorMsg); }); }); } } } } } } })();
คำสั่งสำหรับข้อความส่วนกลางจะรวมอยู่ในเอกสารเค้าโครงรูท (index.html) และจะลงทะเบียนกับกิจกรรมเพื่อจัดการข้อความส่วนกลางทั้งหมด
globalmessages.directive.js :
(function() { 'use strict'; angular .module('reactiveClient') .directive('globalMessagesValidationDirective', globalMessagesValidationDirective); /** @ngInject */ function globalMessagesValidationDirective(_, toastr, $rootScope, $log) { return { restrict: 'AE', link: function (scope) { var cleanUpListener = $rootScope.$on('globalMessages', globalMessagesWatchCallback); scope.$on('$destroy', function() { cleanUpListener(); }); function globalMessagesWatchCallback (event, messagesResponse) { $log.log('received rootScope event: ' + event); if (messagesResponse != undefined && messagesResponse.messages != undefined) { if (messagesResponse.messages.global != undefined) { _.forEach(messagesResponse.messages.global.info, function (msg) { toastr.info(msg); }); _.forEach(messagesResponse.messages.global.warnings, function (msg) { toastr.warning(msg); }); _.forEach(messagesResponse.messages.global.errors, function (msg) { toastr.error(msg); }); } } } } } } })();
สำหรับตัวอย่างที่สมบูรณ์ยิ่งขึ้น ให้เราพิจารณาการตอบสนองต่อไปนี้ซึ่งมีข้อความในเครื่อง:
{ "messages" : { "global" : { "info": [], "warnings": [], "errors": [] }, "local" : [ { "inputId" : "email", "errors" : ["User already exists with this email"], "warnings" : [], "info" : [] } ] }, "data":{ "firstName": "John", "lastName": "Doe", "email": "[email protected]", "password": "MTIzNDU=" } }
คำตอบข้างต้นสามารถนำไปสู่บางสิ่งดังต่อไปนี้:
ในทางกลับกัน ด้วยข้อความตอบกลับทั่วโลก:
{ "messages" : { "global" : { "info": ["User successfully created."], "warnings": ["User will not be available for login until is activated"], "errors": [] }, "local" : [] }, "data":{ "externalId": "a55ccd60-9d82-11e5-9f52-0002a5d5c51b", "firstName": "John", "lastName": "Doe", "email": "[email protected]" } }
แอพไคลเอนต์สามารถแสดงข้อความที่มีความโดดเด่นมากขึ้น:
ในตัวอย่างข้างต้น จะเห็นได้ว่าโครงสร้างการตอบสนองแบบรวมศูนย์สามารถจัดการได้อย่างไรสำหรับคำขอใดๆ ที่มีตัวจัดการเดียวกัน
บทสรุป
การใช้การตรวจสอบความถูกต้องกับโปรเจ็กต์ขนาดใหญ่อาจสร้างความสับสน และกฎการตรวจสอบสามารถพบได้ทุกที่ในโค้ดของโปรเจ็กต์ การตรวจสอบความถูกต้องสม่ำเสมอและมีโครงสร้างที่ดีทำให้สิ่งต่างๆ ง่ายขึ้นและนำกลับมาใช้ใหม่ได้
คุณสามารถค้นหาแนวคิดเหล่านี้ในต้นแบบสองเวอร์ชันที่แตกต่างกันด้านล่าง:
- Standard Play 2.3, Scala 2.11.1, Slick 2.1, Postgres 9.1, Spring Dependency Injection
- ปฏิกิริยาไม่ปิดกั้น Play 2.4, Scala 2.11.7, Slick 3.0, Postgres 9.4, Guice Dependency Injection
ในบทความนี้ ฉันได้นำเสนอคำแนะนำของฉันเกี่ยวกับวิธีสนับสนุนการตรวจสอบบริบทเชิงลึกที่เขียนได้ซึ่งสามารถนำเสนอต่อผู้ใช้ได้อย่างง่ายดาย ฉันหวังว่าสิ่งนี้จะช่วยคุณแก้ปัญหาในการตรวจสอบความถูกต้องและการจัดการข้อผิดพลาดทุกครั้ง โปรดแสดงความคิดเห็นและแบ่งปันความคิดของคุณด้านล่าง