QoraKey
Vue d'ensemble
Chaque requête Qora est identifiée par une clé. Qora accepte deux formats équivalents :
// Liste brute : sans friction, pas d'import nécessaire
key: ['users', userId]
// Wrapper QoraKey : explicite, typé
key: QoraKey(['users', userId])
key: QoraKey.withId('users', userId)
Les deux sont normalisés vers la même représentation interne et comparés avec une égalité structurelle profonde. Ils sont entièrement interchangeables : on peut les mélanger entre appels vers la même requête logique.
Constructeurs
QoraKey(List<dynamic> parts)
Constructeur direct depuis une liste de parties.
QoraKey(['users', userId])
QoraKey(['posts', {'status': 'published', 'page': 1}])
QoraKey.single(String key)
Raccourci pour une clé à segment unique.
QoraKey.single('users') // équivalent à QoraKey(['users'])
QoraKey.withId(String entity, dynamic id)
Raccourci pour une clé entité + ID.
QoraKey.withId('users', userId) // ['users', userId]
QoraKey.withId('posts', postId) // ['posts', postId]
QoraKey.withFilter(String entity, Map<String, dynamic> filter)
Raccourci pour une clé entité + map de filtres.
QoraKey.withFilter('posts', {'status': 'published', 'page': page})
// ['posts', {'status': 'published', 'page': page}]
QoraKey.from(List<dynamic> parts)
Constructeur factory qui convertit une liste brute en QoraKey.
final key = QoraKey.from(['users', userId]);
Propriétés
parts
final List<dynamic> parts
Les segments bruts de la clé sous forme de liste non modifiable.
final key = QoraKey.withId('users', 42);
print(key.parts); // [users, 42]
Égalité
QoraKey utilise une égalité structurelle profonde : deux clés sont égales si leurs parties sont récursivement égales :
QoraKey(['users', 1]) == QoraKey(['users', 1]) // true
QoraKey(['users', 1]) == QoraKey(['users', 2]) // false
// Liste brute et QoraKey sont normalisés identiquement
['users', 1] == QoraKey(['users', 1]) // normalisé vers la même entrée de cache
Types supportés dans les parties de clé :
| Type | Support |
|---|---|
int, double, bool | Égalité structurelle complète |
String | Égalité structurelle complète |
null | Supporté comme partie de clé |
List | Comparaison récursive |
Map | Comparaison récursive indépendante de l'ordre |
| Objets personnalisés | Doivent surcharger operator == et hashCode |
UserFilter) dans les parties de clé sans surcharger operator == et hashCode crée une nouvelle entrée de cache à chaque appel. Utiliser des valeurs sérialisables en primitives ou implémenter l'égalité explicitement.normalizeKey
List<dynamic> normalizeKey(Object key)
Fonction de niveau supérieur qui normalise toute entrée de clé valide en une List<dynamic> non modifiable :
- Accepte
QoraKeyouList<dynamic> - Valide que les parties sont sérialisables
- Retourne une liste copiée en profondeur et non modifiable
Cela est appelé en interne par chaque méthode de QoraClient. L'appel direct est rarement nécessaire.
final parts = normalizeKey(['users', 1]); // [users, 1] (non modifiable)
final parts = normalizeKey(QoraKey.withId('users', 1)); // même résultat
Lève ArgumentError si la clé n'est pas une List ou un QoraKey.
Pattern Key Factories
Centraliser les définitions de clés pour éviter les fautes de frappe et simplifier l'invalidation :
abstract class Keys {
// Utilisateurs
static List<dynamic> users() => ['users'];
static List<dynamic> user(int id) => ['users', id];
static List<dynamic> userPosts(int userId) => ['users', userId, 'posts'];
// Posts
static List<dynamic> posts(Map<String, dynamic> filters) => ['posts', filters];
static List<dynamic> post(int id) => ['posts', id];
}
// Utilisation
await client.fetchQuery<User>(
key: Keys.user(userId),
fetcher: () => api.getUser(userId),
);
client.invalidateWhere((key) => equalsKey(key, Keys.users()));
Bonnes pratiques
A faire
// Structure cohérente : entité en premier, puis ID/filtres
['users', userId]
['users', userId, 'posts']
['posts', {'status': 'published'}]
// Utiliser des primitives : toujours comparables
['users', userId.toString()]
['posts', {'page': page, 'limit': 20}]
A eviter
// Ordre incohérent
['users', userId]
[userId, 'users'] // Entrée de cache différente
// Objets personnalisés sans égalité
['users', UserFilter(status: 'active')] // Égalité d'identité : toujours un miss
// Valeurs calculées variant à chaque appel
['posts', DateTime.now().toIso8601String()] // N'atteint jamais le cache