เลเยอร์โดเมนคือเลเยอร์ที่ไม่บังคับที่อยู่ระหว่างเลเยอร์ UI และ ของ Google Analytics
![เมื่อรวมไว้แล้ว เลเยอร์โดเมนที่ไม่บังคับจะให้ทรัพยากร Dependency ไปยัง
เลเยอร์ UI และขึ้นอยู่กับชั้นข้อมูลด้วย](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-domain-overview.png?authuser=19&hl=th)
เลเยอร์โดเมนมีหน้าที่ห่อหุ้มตรรกะทางธุรกิจที่ซับซ้อน หรือ ตรรกะทางธุรกิจอย่างง่ายที่นำโดย ViewModels หลายรายการไปใช้ซ้ำ เลเยอร์นี้ เป็นตัวเลือกเนื่องจากแอปบางแอปอาจไม่มีข้อกำหนดเหล่านี้ คุณควรใช้ เมื่อจําเป็น เช่น เพื่อจัดการความซับซ้อนหรือต้องการให้นํากลับมาใช้ใหม่ได้
เลเยอร์โดเมนมีประโยชน์ดังต่อไปนี้
- เพื่อหลีกเลี่ยงการทำซ้ำโค้ด
- ช่วยให้อ่านในชั้นเรียนที่ใช้คลาสเลเยอร์โดเมนได้ง่ายขึ้น
- ช่วยเพิ่มความสามารถในการทดสอบของแอป
- ช่วยหลีกเลี่ยงชั้นเรียนขนาดใหญ่โดยให้คุณแบ่งหน้าที่ความรับผิดชอบได้
เพื่อให้คลาสเหล่านี้เรียบง่ายและใช้ทรัพยากรน้อย Use Case แต่ละรายการควรมี มีหน้าที่เดียว และไม่ควรมีการเปลี่ยนแปลง คุณควรจัดการข้อมูลที่เปลี่ยนแปลงได้ใน UI หรือชั้นข้อมูลแทน
รูปแบบการตั้งชื่อในคู่มือนี้
ในคู่มือนี้ กรณีการใช้งานจะตั้งชื่อตามการดำเนินการรายการเดียวที่บุคคลนั้นรับผิดชอบ สำหรับ ซึ่งมีรายละเอียดดังนี้
กริยา ณ ปัจจุบัน + คำนาม/อะไร (ไม่บังคับ) + UseCase
เช่น FormatDateUseCase
, LogOutUserUseCase
GetLatestNewsWithAuthorsUseCase
หรือ MakeLoginRequestUseCase
การขึ้นต่อกัน
ในสถาปัตยกรรมแอปทั่วไป คลาส Use Case ที่เหมาะสมระหว่าง ViewModels จาก เลเยอร์ UI และที่เก็บจากชั้นข้อมูล ซึ่งหมายความว่าคลาส Use Case มักจะขึ้นอยู่กับคลาสที่เก็บ และแท็กจะสื่อสารกับเลเยอร์ UI เช่นเดียวกับที่เก็บ โดยใช้ Callback (สำหรับ Java) หรือ Coroutine (สำหรับ Kotlin) หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับเรื่องนี้ โปรดดูที่ชั้นข้อมูล
เช่น ในแอป คุณอาจมีคลาสกรณีการใช้งานที่ดึงข้อมูลจาก ที่เก็บข้อมูลข่าวและที่เก็บผู้แต่ง แล้วรวมเข้าด้วยกัน:
class GetLatestNewsWithAuthorsUseCase(
private val newsRepository: NewsRepository,
private val authorsRepository: AuthorsRepository
) { /* ... */ }
เนื่องจากกรณีการใช้งานมีตรรกะที่ใช้ซ้ำได้ จึงนำไปใช้งานในรูปแบบอื่นๆ ได้ด้วย
กรณี การมีกรณีการใช้งานหลายระดับในเลเยอร์โดเมนนั้นถือเป็นเรื่องปกติ สำหรับ
เช่น Use Case ที่กำหนดไว้ในตัวอย่างด้านล่างสามารถใช้ประโยชน์จาก
FormatDateUseCase
Use Case หากหลายคลาสจากเลเยอร์ UI ต้องใช้เวลา
เพื่อแสดงข้อความที่เหมาะสมบนหน้าจอ:
class GetLatestNewsWithAuthorsUseCase(
private val newsRepository: NewsRepository,
private val authorsRepository: AuthorsRepository,
private val formatDateUseCase: FormatDateUseCase
) { /* ... */ }
![GetLatestNewsWithAuthorsUseCase ขึ้นอยู่กับคลาสที่เก็บจาก
แต่ก็ยังขึ้นอยู่กับ FormatDataUseCase ซึ่งเป็นคลาส Use Case อีกแบบหนึ่งด้วย
ที่อยู่ในเลเยอร์โดเมนด้วย](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-domain-usecase-deps.png?authuser=19&hl=th)
กรณีการใช้งานการโทรใน Kotlin
ใน Kotlin คุณสามารถทำให้อินสแตนซ์คลาส Use Case เรียกใช้เป็นฟังก์ชันได้โดย
การกำหนดฟังก์ชัน invoke()
ด้วยแป้นกดร่วม operator
โปรดดูข้อมูลต่อไปนี้
ตัวอย่าง:
class FormatDateUseCase(userRepository: UserRepository) {
private val formatter = SimpleDateFormat(
userRepository.getPreferredDateFormat(),
userRepository.getPreferredLocale()
)
operator fun invoke(date: Date): String {
return formatter.format(date)
}
}
ในตัวอย่างนี้ เมธอด invoke()
ใน FormatDateUseCase
ช่วยให้คุณดำเนินการต่อไปนี้ได้
การเรียกใช้อินสแตนซ์ของคลาสราวกับว่าเป็นฟังก์ชัน เมธอด invoke()
คือ
ไม่จำกัดอยู่แค่ลายเซ็นเฉพาะ ซึ่งจะใช้พารามิเตอร์กี่รายการก็ได้
และแสดงผลประเภทใดก็ได้ คุณยังใช้งาน invoke()
มากเกินไปด้วยลายเซ็นที่ต่างกันได้ด้วย
ในชั้นเรียน คุณจะเรียก Use Case จากตัวอย่างด้านบนดังนี้
class MyViewModel(formatDateUseCase: FormatDateUseCase) : ViewModel() {
init {
val today = Calendar.getInstance()
val todaysDate = formatDateUseCase(today)
/* ... */
}
}
โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับโอเปอเรเตอร์ invoke()
ที่ Kotlin
เอกสาร
วงจร
กรณีการใช้งานไม่มีวงจรของลูกค้าเอง แต่จะกำหนดขอบเขตไว้ที่ชั้นเรียนแทน
ที่ใช้โค้ดเหล่านั้น ซึ่งหมายความว่าคุณจะเรียกใช้ Use Case จากคลาสใน UI ได้
จากบริการ หรือจากคลาส Application
เอง เนื่องจากกรณีการใช้งาน
ไม่ควรมีข้อมูลที่เปลี่ยนแปลงได้ คุณควรสร้างอินสแตนซ์ใหม่ของ Use Case
ทุกครั้งที่สอบผ่าน
ด้ายกำจัดขน
กรณีการใช้งานจากเลเยอร์โดเมนต้องปลอดภัยหลัก กล่าวอีกนัยหนึ่งคือ ต้อง สามารถเรียกจากเทรดหลักได้อย่างปลอดภัย กรณีที่คลาส Use Case ดำเนินการเป็นเวลานาน ในการดำเนินการบล็อก ผู้ดูแลระบบจะรับผิดชอบในการย้ายตรรกะนั้นไปยัง ชุดข้อความที่เหมาะสม แต่ก่อนที่จะดำเนินการดังกล่าว ให้ตรวจสอบว่าการบล็อก ควรวางไว้ในชั้นอื่นๆ ของลำดับชั้นมากกว่า โดยทั่วไป การประมวลผลที่ซับซ้อนเกิดขึ้นในชั้นข้อมูลเพื่อส่งเสริม ความสามารถในการนำกลับมาใช้ซ้ำหรือ การแคช เช่น การดำเนินการที่ใช้ทรัพยากรจำนวนมากในรายการขนาดใหญ่จะดีกว่า ไว้ในชั้นข้อมูลมากกว่าในเลเยอร์โดเมน หากผลลัพธ์จะต้อง เพื่อนำไปใช้ซ้ำบนหลายหน้าจอของแอป
ตัวอย่างต่อไปนี้แสดงกรณีการใช้งานที่ทํางานในเบื้องหลัง ชุดข้อความ:
class MyUseCase(
private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
) {
suspend operator fun invoke(...) = withContext(defaultDispatcher) {
// Long-running blocking operations happen on a background thread.
}
}
งานทั่วไป
หัวข้อนี้จะอธิบายวิธีทำงานทั่วไปของเลเยอร์โดเมน
ตรรกะทางธุรกิจที่เรียบง่ายซึ่งนำมาใช้ใหม่ได้
คุณควรห่อหุ้มตรรกะทางธุรกิจที่ทำซ้ำได้ซึ่งแสดงอยู่ในเลเยอร์ UI ใน คลาสของกรณีการใช้งาน วิธีนี้ทำให้นำการเปลี่ยนแปลงไปใช้กับทุกที่ในตรรกะได้ง่ายขึ้น และยังช่วยให้ทดสอบตรรกะแยกกันได้ด้วย
ลองดูตัวอย่าง FormatDateUseCase
ที่อธิบายไว้ก่อนหน้านี้ หากธุรกิจของคุณ
เกี่ยวกับการเปลี่ยนแปลงการจัดรูปแบบวันที่ในอนาคต คุณเพียงต้อง
เปลี่ยนแปลงโค้ดได้แบบรวมศูนย์ในที่เดียว
รวมที่เก็บ
ในแอปข่าวสาร คุณอาจมีNewsRepository
และAuthorsRepository
ชั้นเรียน
ที่จัดการปฏิบัติการด้านข้อมูลข่าวและผู้แต่งตามลำดับ ชั้นเรียน Article
ที่ NewsRepository
แสดงจะมีเฉพาะชื่อของผู้เขียนเท่านั้น แต่คุณต้องการ
เพื่อแสดงข้อมูลเพิ่มเติมเกี่ยวกับผู้แต่งบนหน้าจอ ข้อมูลผู้เขียน
จะรับค่าได้จาก AuthorsRepository
![GetLatestNewsWithAuthorsUseCase ขึ้นอยู่กับที่เก็บ 2 รายการที่แตกต่างกัน
คลาสจากชั้นข้อมูล: NewsRepository และ AuthorsRepository](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-domain-multiple-repos.png?authuser=19&hl=th)
เนื่องจากตรรกะเกี่ยวข้องกับที่เก็บหลายแหล่ง และอาจมีความซับซ้อนได้
สร้างคลาส GetLatestNewsWithAuthorsUseCase
เพื่อนำตรรกะออกจาก
ViewModel และทำให้อ่านง่ายขึ้น และวิธีนี้ทำให้ตรรกะ
โดยแยกออกมาต่างหากและใช้ซ้ำในส่วนต่างๆ ของแอป
/**
* This use case fetches the latest news and the associated author.
*/
class GetLatestNewsWithAuthorsUseCase(
private val newsRepository: NewsRepository,
private val authorsRepository: AuthorsRepository,
private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
) {
suspend operator fun invoke(): List<ArticleWithAuthor> =
withContext(defaultDispatcher) {
val news = newsRepository.fetchLatestNews()
val result: MutableList<ArticleWithAuthor> = mutableListOf()
// This is not parallelized, the use case is linearly slow.
for (article in news) {
// The repository exposes suspend functions
val author = authorsRepository.getAuthor(article.authorId)
result.add(ArticleWithAuthor(article, author))
}
result
}
}
ตรรกะจะแมปรายการทั้งหมดในรายการ news
ดังนั้นแม้ว่าชั้นข้อมูล
ความปลอดภัยหลัก งานนี้ไม่ควรบล็อกเทรดหลักเนื่องจากคุณไม่ทราบว่าต้องทำอย่างไร
หลายรายการที่จะประมวลผล ดังนั้นกรณีการใช้งานจึงย้ายงานไปยังพื้นหลัง
ชุดข้อความโดยใช้ผู้มอบหมายงานเริ่มต้น
ผู้บริโภคอื่นๆ
นอกเหนือจากเลเยอร์ UI แล้ว เลเยอร์โดเมนนั้นยังสามารถใช้ซ้ำได้โดยคลาสอื่นๆ เช่น
และคลาส Application
นอกจากนี้ หากแพลตฟอร์มอื่นๆ เช่น ทีวี
หรือ Wear แชร์ฐานของโค้ดกับแอปบนอุปกรณ์เคลื่อนที่ เลเยอร์ UI ของพวกเขายังนำมาใช้ซ้ำได้
เพื่อรับประโยชน์ที่กล่าวไว้ข้างต้นทั้งหมดของเลเยอร์โดเมน
การจำกัดการเข้าถึงชั้นข้อมูล
ข้อควรคำนึงอีกประการหนึ่งเมื่อใช้เลเยอร์โดเมนคือ คุณควร ยังคงอนุญาตการเข้าถึงชั้นข้อมูลโดยตรงจากเลเยอร์ UI หรือบังคับ ผ่านทางเลเยอร์โดเมน
![เลเยอร์ UI ไม่สามารถเข้าถึงชั้นข้อมูลโดยตรงได้ เนื่องจากจะต้องผ่านเลเยอร์โดเมน](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-domain-data-access-restriction.png?authuser=19&hl=th)
ข้อดีของการสร้างข้อจำกัดนี้คือ ทำให้ UI หยุดข้ามได้ ตรรกะเลเยอร์โดเมน เช่น ถ้าคุณกำลังบันทึกการวิเคราะห์ในแต่ละ คำขอเข้าถึงชั้นข้อมูล
อย่างไรก็ตาม ข้อเสียที่อาจเกิดขึ้นอย่างมากก็คือการบังคับให้คุณ เพิ่มกรณีการใช้งานได้แม้ว่าจะเป็นการเรียกใช้ฟังก์ชันอย่างง่ายในชั้นข้อมูลก็ตาม ซึ่งอาจเพิ่มความซับซ้อน และมีประโยชน์เพียงเล็กน้อย
วิธีที่ดีคือเพิ่ม Use Case เฉพาะเมื่อจำเป็นเท่านั้น หากคุณพบว่า UI ของคุณ เข้าถึงข้อมูลผ่าน Use Case เพียงอย่างเดียวได้เกือบทั้งหมด สมควรที่จะเข้าถึงข้อมูลด้วยวิธีนี้เท่านั้น
ท้ายที่สุดแล้ว การตัดสินใจที่จะจำกัดการเข้าถึงชั้นข้อมูล ฐานของโค้ดเฉพาะ และกำหนดว่าคุณต้องการกฎที่เข้มงวดหรือยืดหยุ่นกว่า ของเรา
การทดสอบ
ใช้หลักเกณฑ์การทดสอบทั่วไปเมื่อทดสอบโดเมน สำหรับการทดสอบ UI อื่นๆ นักพัฒนาซอฟต์แวร์มักจะใช้ที่เก็บปลอม คุณควรใช้ที่เก็บปลอมเมื่อทดสอบเลเยอร์โดเมนด้วย
ตัวอย่าง
ตัวอย่าง Google ต่อไปนี้สาธิตการใช้เลเยอร์โดเมน ศึกษาคู่มือเพื่อดูคำแนะนำนี้ในสถานการณ์จริง
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- ชั้นข้อมูล
- การใช้งานสถานะ UI