Die Domainebene ist eine optionale Schicht, die sich zwischen der UI-Ebene und dem der Datenschicht.
<ph type="x-smartling-placeholder">![Wenn sie enthalten ist, stellt die optionale Domainebene Abhängigkeiten
der UI-Ebene und
hängt von der Datenschicht ab.](https://developer.android.google.cn/static/topic/libraries/architecture/images/mad-arch-domain-overview.png?authuser=8&hl=de)
Die Domainschicht ist für die Kapselung der komplexen Geschäftslogik verantwortlich. einfache Geschäftslogik, die von mehreren ViewModels wiederverwendet wird. Diese Ebene ist optional, da diese Anforderungen nicht für alle Apps gelten. Sie sollten nur wenn es erforderlich ist, z. B. um Komplexität zu bewältigen oder die Wiederverwendbarkeit zu bevorzugen.
Eine Domainebene bietet folgende Vorteile:
- Codeduplizierung wird vermieden.
- Sie verbessert die Lesbarkeit in Klassen, die Domainebenenklassen verwenden.
- Sie verbessert die Testbarkeit der App.
- Durch die Aufteilung der Verantwortlichkeiten werden große Klassen vermieden.
Um diese Klassen einfach und kompakt zu halten, sollte jeder Anwendungsfall nur eine für eine einzelne Funktion verantwortlich ist und keine änderbaren Elemente enthalten, Daten. Stattdessen sollten Sie veränderliche Daten in Ihrer Benutzeroberfläche oder in den Datenebenen verarbeiten.
Namenskonventionen in diesem Leitfaden
In diesem Leitfaden sind Anwendungsfälle nach der einzelnen Aktion benannt, für die sie verantwortlich sind für die Sie angegeben haben. Die Konvention lautet wie folgt:
Verb im Präsens + Substantiv/Was (optional) + Anwendungsfall.
Beispiele: FormatDateUseCase
, LogOutUserUseCase
,
GetLatestNewsWithAuthorsUseCase
oder MakeLoginRequestUseCase
.
Abhängigkeiten
In einer typischen App-Architektur passen die Anwendungsfallklassen zwischen ViewModels aus dem UI-Ebene und Repositories aus der Datenschicht Das bedeutet, dass Anwendungsfallklassen sind normalerweise von Repository-Klassen abhängig und kommunizieren mit der UI-Ebene, genau wie Repositories – mit Callbacks (für Java) oder Koroutinen (für Kotlin) Weitere Informationen hierzu finden Sie im Abschnitt zur Datenschicht. .
Vielleicht haben Sie in Ihrer App eine Anwendungsfallklasse, die Daten von einem Nachrichten- und einem Autorenverzeichnis und kombiniert diese:
class GetLatestNewsWithAuthorsUseCase(
private val newsRepository: NewsRepository,
private val authorsRepository: AuthorsRepository
) { /* ... */ }
Da Anwendungsfälle wiederverwendbare Logik enthalten, können sie auch für andere Zwecke genutzt werden.
Cases. Es ist normal, dass auf der Domainebene mehrere Anwendungsfälle vorhanden sind. Für
kann für den im Beispiel unten definierten Anwendungsfall der Parameter
Anwendungsfall FormatDateUseCase
, wenn mehrere Klassen aus der UI-Ebene von Zeit abhängig sind
um die richtige Nachricht auf dem Bildschirm anzuzeigen:
class GetLatestNewsWithAuthorsUseCase(
private val newsRepository: NewsRepository,
private val authorsRepository: AuthorsRepository,
private val formatDateUseCase: FormatDateUseCase
) { /* ... */ }
![GetRecentNewsWithAuthorsUseCase hängt von Repository-Klassen aus der
der Datenschicht, sondern auch von FormatDataUseCase,
einer weiteren Anwendungsfallklasse,
die sich auch auf der Domain-Ebene befindet.](https://developer.android.google.cn/static/topic/libraries/architecture/images/mad-arch-domain-usecase-deps.png?authuser=8&hl=de)
Anwendungsfälle in Kotlin aufrufen
In Kotlin können Sie Anwendungsfallklasseninstanzen wie Funktionen
Definieren der invoke()
-Funktion mit dem operator
-Modifikator Weitere Informationen:
Beispiel:
class FormatDateUseCase(userRepository: UserRepository) {
private val formatter = SimpleDateFormat(
userRepository.getPreferredDateFormat(),
userRepository.getPreferredLocale()
)
operator fun invoke(date: Date): String {
return formatter.format(date)
}
}
In diesem Beispiel können Sie mit der Methode invoke()
in FormatDateUseCase
Folgendes tun:
Instanzen der Klasse so aufrufen, als wären sie Funktionen. Die Methode invoke()
ist
nicht auf eine bestimmte Signatur beschränkt, sondern können eine beliebige Anzahl von Parametern annehmen.
und einen beliebigen Typ zurückgeben. Sie können invoke()
auch mit verschiedenen Signaturen überladen.
in Ihrem Kurs. In diesem Fall würden Sie den Anwendungsfall aus dem obigen Beispiel wie folgt aufrufen:
class MyViewModel(formatDateUseCase: FormatDateUseCase) : ViewModel() {
init {
val today = Calendar.getInstance()
val todaysDate = formatDateUseCase(today)
/* ... */
}
}
Weitere Informationen zum Operator invoke()
finden Sie in der Kotlin-Dokumentation.
Dokumentation.
Lebenszyklus
Anwendungsfälle haben keinen eigenen Lebenszyklus. Stattdessen beziehen sie sich auf den Kurs.
der sie verwendet. Das bedeutet, dass Sie Anwendungsfälle über Klassen in der UI aufrufen können.
aus Diensten oder der Klasse Application
selbst. Weil Anwendungsfälle
keine änderbaren Daten enthalten, sollten Sie eine neue Instanz eines Anwendungsfalls erstellen.
jedes Mal, wenn Sie
sie als Abhängigkeit übergeben.
Threading
Anwendungsfälle auf der Domainebene müssen hauptsicher sein. mit anderen Worten, sie müssen können Sie über den Hauptthread aufrufen. Anwendungsfallklassen mit langer Ausführungszeit blockieren, sind sie dafür verantwortlich, diese Logik in den entsprechenden Thread. Bevor Sie das tun, sollten Sie jedoch prüfen, Operationen besser in anderen Hierarchieebenen platziert werden. Normalerweise finden komplexe Berechnungen in der Datenschicht statt, um die Wiederverwendbarkeit oder Caching. Beispielsweise ist ein ressourcenintensiver Vorgang auf einer großen Liste besser und nicht in der Domain-Ebene, wenn das Ergebnis um sie auf verschiedenen Bildschirmen der App wiederzuverwenden.
Das folgende Beispiel zeigt einen Anwendungsfall, der seine Arbeit auf einem Hintergrund ausführt. Thread:
class MyUseCase(
private val defaultDispatcher: CoroutineDispatcher = Dispatchers.Default
) {
suspend operator fun invoke(...) = withContext(defaultDispatcher) {
// Long-running blocking operations happen on a background thread.
}
}
Allgemeine Aufgaben
In diesem Abschnitt wird beschrieben, wie Sie gängige Aufgaben der Domainebene ausführen.
Wiederverwendbare einfache Geschäftslogik
Sie sollten die auf der UI-Ebene vorhandene wiederholbare Geschäftslogik in einer Anwendungsfallklasse. Das macht es einfacher, Änderungen überall dort anzuwenden, wo die Logik verwendet wird. Außerdem können Sie die Logik isoliert testen.
Betrachten Sie das zuvor beschriebene FormatDateUseCase
-Beispiel. Wenn Ihr Unternehmen
Anforderungen an zukünftige Änderungen der Datumsformatierung, müssen Sie lediglich
an einem zentralen Ort zu ändern.
Repositories kombinieren
In einer Nachrichten-App hast du möglicherweise die Kurse NewsRepository
und AuthorsRepository
die Nachrichten- bzw. Autorendatenvorgänge abwickeln. Klasse Article
die NewsRepository
nur den Namen des Autors anzeigt, Sie möchten aber
um weitere Informationen über den Autor auf dem Bildschirm anzuzeigen. Informationen zum Autor
kann im AuthorsRepository
abgerufen werden.
![GetLatestNewsWithAuthorsUseCase ist von zwei verschiedenen Repositorys abhängig
Klassen aus der Datenschicht: NewsRepository und AuthorsRepository.](https://developer.android.google.cn/static/topic/libraries/architecture/images/mad-arch-domain-multiple-repos.png?authuser=8&hl=de)
Da die Logik mehrere Repositories umfasst und komplex werden kann,
erstellen Sie eine GetLatestNewsWithAuthorsUseCase
-Klasse, um die Logik aus
das ViewModel und
erleichtern die Lesbarkeit. Dadurch wird die Logik auch einfacher,
isoliert getestet und in verschiedenen Teilen der App
wiederverwendbar.
/**
* 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
}
}
Die Logik ordnet alle Elemente in der news
-Liste zu. Auch wenn die Datenschicht
Hauptthread nicht blockiert wird, da Sie nicht wissen,
Elemente verarbeitet werden. Deshalb verlagert der Anwendungsfall die Arbeit in den Hintergrund.
Standard-Dispatcher verwendet.
Andere Verbraucher
Abgesehen von der UI-Ebene kann die Domain-Ebene auch von anderen Klassen wiederverwendet werden, z. B.
und der Application
-Klasse. Wenn andere Plattformen wie das Fernsehen
oder Wear die Codebasis für die mobile App freigeben, kann deren UI-Ebene ebenfalls
um alle oben genannten Vorteile der Domain-Ebene zu nutzen.
Zugriffsbeschränkung für Datenschicht
Eine weitere Überlegung bei der Implementierung der Domainebene ist, ob Sie weiterhin direkten Zugriff auf die Datenschicht über die UI-Ebene ermöglichen oder über die Domain-Ebene hinweg.
<ph type="x-smartling-placeholder">![Die UI-Ebene kann nicht direkt auf die Datenschicht zugreifen. Sie muss die Domain-Ebene durchlaufen.](https://developer.android.google.cn/static/topic/libraries/architecture/images/mad-arch-domain-data-access-restriction.png?authuser=8&hl=de)
Diese Einschränkung hat den Vorteil, dass dadurch verhindert wird, dass Ihre Benutzeroberfläche das Domain-Layer-Logik. Wenn Sie beispielsweise Analytics-Logging auf jedem Zugriffsanforderung an die Datenschicht.
Der möglicherweise erhebliche Nachteil besteht jedoch darin, dass Sie dadurch gezwungen sind, Anwendungsfälle hinzufügen, auch wenn es sich nur um einfache Funktionsaufrufe an die Datenschicht handelt was die Komplexität erhöhen kann.
Es empfiehlt sich, Anwendungsfälle nur bei Bedarf hinzuzufügen. Wenn Sie feststellen, dass Ihre Benutzeroberfläche auf Daten fast ausschließlich über Anwendungsfälle zugreift, ist es sinnvoll, nur auf diese Weise auf Daten zuzugreifen.
Die Entscheidung, den Zugriff auf die Datenschicht einzuschränken, ist letztendlich individuelle Codebasis, ob Sie strenge Regeln oder eine flexiblere Ansatz.
Testen
Beim Testen der Domain gelten allgemeine Richtlinien für Tests. Ebene. Für andere UI-Tests verwenden Entwickler in der Regel fiktive Repositories. ist es empfehlenswert, beim Testen der Domainebene auch gefälschte Repositories zu verwenden.
Produktproben
In den folgenden Google-Beispielen wird die Verwendung der Domainebene veranschaulicht. Sehen Sie sich diese Tipps in der Praxis an:
Empfehlungen für dich
- Hinweis: Der Linktext wird angezeigt, wenn JavaScript deaktiviert ist.
- Datenschicht
- UI State-Produktion