Quels sont les inconvénients du modèle ActiveRecord?

30

Je suis curieux de savoir quels sont les inconvénients de l'utilisation du modèle ActiveRecord pour l'accès aux données / les objets métier. La seule à laquelle je peux penser du haut de ma tête est qu'elle viole le principe de responsabilité unique, mais le modèle de RA est assez commun pour que cette raison seule ne semble pas "assez bonne" pour justifier de ne pas l'utiliser (bien sûr, mon La vue peut être biaisée car souvent aucun du code avec lequel je travaille ne suit les principes SOLID).

Personnellement, je ne suis pas un fan d'ActiveRecord (à l'exception de l'écriture d'une application Ruby on Rails, où AR semble "naturel"), car on a l'impression que la classe en fait trop, et l'accès aux données ne devrait pas être réservé à la classe elle-même gérer. Je préfère utiliser des référentiels qui renvoient des objets métier. La plupart du code avec lequel je travaille a tendance à utiliser une variante d'ActiveRecord, sous la forme (je ne sais pas pourquoi la méthode est booléenne):

public class Foo
{
    // properties...

    public Foo(int fooID)
    {
        this.fooID = fooID;
    }

    public bool Load()
    {
        // DB stuff here...
        // map DataReader to properties...

        bool returnCode = false;
        if (dr.HasRows)
            returnCode = true;

        return returnCode;
    }
}

ou parfois la manière la plus "traditionnelle" d'avoir une public static Foo FindFooByID(int fooID)méthode pour les chercheurs et quelque chose dans le sens de la public void Save()sauvegarde / mise à jour.

J'obtiens qu'ActiveRecord est généralement beaucoup plus simple à implémenter et à utiliser, mais cela semble un peu trop simple pour les applications complexes et vous pourriez avoir une architecture plus robuste en encapsulant votre logique d'accès aux données dans un référentiel (sans parler d'avoir plus facile à échanger) stratégies d'accès aux données, par exemple, vous utilisez peut-être des processus stockés + des ensembles de données et souhaitez passer à LINQ ou quelque chose du genre)

Quels sont donc les autres inconvénients de ce modèle qui doivent être pris en compte pour décider si ActiveRecord est le meilleur candidat pour le travail?

Wayne Molina
la source

Réponses:

28

Le principal inconvénient est que vos «entités» sont conscientes de leur propre persistance, ce qui conduit à de nombreuses autres mauvaises décisions de conception.

Les autres problèmes sont que la plupart des boîtes à outils d'enregistrement actives mappent essentiellement 1 à 1 à des champs de table avec zéro couche d'indirection. Cela fonctionne à petite échelle mais s'effondre lorsque vous avez des problèmes plus délicats à résoudre.


Eh bien, avoir vos objets au courant de leur persistance signifie que vous devez faire des choses comme:

  • avoir facilement des connexions à la base de données disponibles partout. Cela conduit généralement à un codage en dur désagréable ou à une sorte de connexion statique qui est touchée de partout.
  • vos objets ont tendance à ressembler davantage à SQL qu'à des objets.
  • difficile de faire quoi que ce soit dans l'application déconnectée car la base de données est tellement enracinée.

Il y a finalement toute une série d'autres mauvaises décisions.

Wyatt Barnett
la source
2
pouvez-vous élaborer sur "d'autres mauvaises décisions de conception"?
kevin cline
2
Merci. Je n'ai pas trouvé ces problèmes comme un problème dans le développement de Ruby on Rails. Il est toujours possible de tester séparément le comportement et la persistance. L'OMI séparant la persistance du comportement a peu de valeur pratique.
kevin cline
@kevin: ces choses sont moins d'un inconvénient avec des fonctionnalités ruby ​​comme les mixins et la frappe de canard. Avec des langages statiques - comme C # qui est ce que l'OP a utilisé dans sa question - il est un peu plus difficile de séparer les deux.
Wyatt Barnett
@Wayne: pour moi, les classes ne sont que des boîtes pour insérer des méthodes - je peux séparer la logique métier de la persistance en les mettant dans des classes séparées, ou je peux simplement les séparer conceptuellement en m'assurant que les méthodes métier ne font pas I / O. Dans les langues avec un support de délégation médiocre (par exemple Java), cela enregistre une énorme quantité de code. OTOH, je viens d'entrer dans Hibernate pour que je puisse me tromper complètement.
Kevin Cline
4
J'ajouterais deux choses; 1. Le couplage avec le mécanisme de persistance rend le code difficile, voire impossible, à tester correctement. 2. Active Record colle votre code aux relations dans un mécanisme de persistance centralisé, ce qui rend très difficile la séparation de votre monolithe si vous le décidez.
istepaniuk
15

Le plus grand inconvénient de l'enregistrement actif est que votre domaine est généralement étroitement lié à un mécanisme de persistance particulier. Si ce mécanisme nécessite un changement global, peut-être d'une persistance basée sur des fichiers à une persistance basée sur une base de données, ou entre des infrastructures d'accès aux données, CHAQUE classe qui implémente ce modèle peut changer. Selon le langage, le cadre et la conception, même quelque chose d'aussi simple que de changer l'emplacement de la base de données ou à qui "appartient", il peut nécessiter de parcourir chaque objet pour mettre à jour les méthodes d'accès aux données (ce qui est rare dans la plupart des langues qui offrent un accès facile pour configurer des fichiers avec des chaînes de connexion).

Cela vous oblige également généralement à vous répéter. La plupart des mécanismes de persistance ont beaucoup de code commun, pour se connecter à une base de données et démarrer une transaction. DRY (Don't Repeat Yourself) vous dirait en tant que codeur de centraliser une telle logique.

Cela rend également les opérations atomiques délicates. Si un groupe d'objets doit être enregistré de manière tout ou rien (comme une facture et ses lignes de facture et / ou entrées client et / ou GL), l'un ou l'autre objet doit connaître tous ces autres objets et contrôler leur persistance ( qui étend la portée de l'objet de contrôle; les grands enregistrements interconnectés peuvent facilement devenir des "objets divins" qui savent tout de leurs dépendances), ou le contrôle de la transaction entière doit être géré de l'extérieur du domaine (et dans ce cas, pourquoi utilisez-vous AR?)

C'est également "faux" d'un point de vue orienté objet. Dans le monde réel, une facture ne sait pas comment se déposer, alors pourquoi un objet de code facture sait-il comment se sauvegarder dans la base de données? Bien sûr, une adhésion trop religieuse aux «objets ne devrait modéliser que ce que leurs homologues du monde réel peuvent faire» conduirait à des modèles de domaine anémique (une facture ne sait pas non plus comment calculer son propre total, mais divise le calcul de ce total en un autre objet est généralement considéré comme une mauvaise idée).

KeithS
la source
Dans Ruby, où la persistance peut être définie ou abstraite derrière un mot-clé ou une commande très simple, c'est probablement moins un problème. Dans .NET, qui nécessite plus de LoC pour configurer divers mécanismes de persistance, il est généralement redondant d'avoir une connexion SQL pour chaque objet, surtout si plusieurs objets doivent être enregistrés dans une transaction atomique. La connexion à la base de données est identique, que vous enregistriez une facture ou un client. Vous devez soit résumer les éléments communs dans une classe de base commune à tous les objets ActiveRecord dans votre base de code, soit les extraire dans sa propre classe (un référentiel)
KeithS
pouvez-vous fournir des exemples d'utilisation de Ruby ActiveRecord qui illustrent vos points? Je n'ai pas rencontré ces problèmes, mais mon application était assez petite. Je pouvais écrire des migrations dans Ruby et les déployer sur différentes implémentations DB. J'ai trouvé que Ruby ActiveRecord était très utile pour éliminer les répétitions, donc je ne vois pas pourquoi vous affirmeriez que l'utilisation d'ActiveRecord aurait l'effet inverse. Je n'ai pas eu de problème avec l'enregistrement: je viens de modifier le modèle objet et j'ai laissé AR mettre à jour la base de données pour refléter le modèle objet.
kevin cline
2

L'inconvénient de base est qu'il rend votre modèle de domaine complexe car il contient non seulement la logique métier mais également des informations de persistance.

La solution consiste donc à utiliser l' implémentation Data Mapper d'ORM. Cela sépare la couche de persistance et nous sommes désormais plus centrés sur la logique métier de l'entité. La doctrine est Data Mapper ORM.

Mais cette approche présente également une certaine complexité. Pour les requêtes, vous dépendez maintenant trop de Data Mapper, ce qui rend l'environnement orienté requêtes. Pour le simplifier, une autre couche est introduite entre le modèle de domaine et le mappeur de données est appelé référentiel .

Le référentiel résume la couche de persistance. Cela donne l'impression d'une programmation orientée objet dans le sens, c'est une collection de tous les objets de même type (comme toutes les entités stockées dans la table de base de données) et vous pouvez effectuer des opérations sur eux comme opération de collecte, ajouter , supprimer . contient etc.

Par exemple, pour l'entité utilisateur , il y aura UserRepository qui représente la collection du même type d'objets utilisateur (celui stocké dans la table des utilisateurs) sur lequel vous pouvez effectuer l'opération. Pour interroger la table des utilisateurs, il utilise le mappeur de données utilisateur, mais il a été extrait du modèle de domaine Utilisateur .

Le modèle de référentiel est le type de couche d'accès aux données , un autre est les différences d' objet d'accès aux données uniquement Le référentiel a une fonction racine agrégée

palash140
la source
Le référentiel et les modèles DAO diffèrent, DAO est un accès général aux données, le référentiel est pour la persistance de la collecte de tous les objets de même type. C'est-à-dire que tous les référentiels doivent avoir la même interface (comme un tableau ou une liste). L'autre roi des méthodes n'appartient pas au référentiel. DAO est de niveau inférieur, le référentiel peut utiliser DAO. Souvent, les programmeurs (en particulier PHP) utilisent le référentiel comme DAO, mais ce n'est pas correct.
xmedeko