Question

J'ai lu certaines des questions concernant les modèles de domaine anémique et la séparation des préoccupations. Quelles sont les meilleures techniques pour exécuter / attacher une logique de domaine sur des objets de domaine anémiques? Dans mon travail, nous avons un modèle assez anémique et nous utilisons actuellement "helper". classes pour effectuer la logique de base de données / métier sur les objets du domaine. Par exemple:

public class Customer
{
    public string Name {get;set;}
    public string Address {get;set;}
}

public class Product
{
    public string Name {get;set;}
    public decimal Price {get;set;}
}

public class StoreHelper
{
    public void PurchaseProduct(Customer c, Product p)
    {
         // Lookup Customer and Product in db
         // Create records for purchase
         // etc.
    }
}

Lorsque l'application doit effectuer un achat, elle crée StoreHelper et appelle la méthode sur les objets de domaine. Pour moi, il serait logique que le client / produit sache comment s'enregistrer lui-même dans un référentiel, mais vous ne voudriez probablement pas de méthodes Save () sur les objets de domaine. Cela aurait également un sens pour une méthode comme Customer.Purchase (Product), mais il s’agit de placer la logique de domaine sur l’entité.

Voici quelques techniques que j'ai rencontrées, mais je ne sais pas quelles sont les bonnes / mauvaises:

  1. Le client et le produit héritent d'une " Entity " class, qui fournit les opérations de base CRUD de manière générique (en utilisant éventuellement un ORM).
    • Avantages: chaque objet de données obtient automatiquement les opérations CRUD, mais est ensuite lié à la base de données / ORM
    • Inconvénients: cela ne résout pas le problème des opérations commerciales sur les objets, mais lie également tous les objets de domaine à une entité de base qui pourrait ne pas être appropriée
  2. Utiliser des classes auxiliaires pour gérer les opérations CRUD et la logique métier
    • Est-il judicieux d’avoir des DAO pour la "base de données pure"? opérations, et des assistants distincts pour les opérations plus spécifiques?
    • Est-il préférable d'utiliser des classes d'assistance non statiques ou statiques pour cela?
    • Avantages: les objets de domaine ne sont liés à aucune base de données / logique métier (complètement anémique)
    • Inconvénients: pas très OO, pas très naturel d’utiliser des helpers dans le code de l’application (qui ressemble au code C)
  3. Utilisez la technique Double Dispatch où l'entité dispose de méthodes pour enregistrer dans un référentiel arbitraire
    • Avantages: meilleure séparation des préoccupations
    • Inconvénients: les entités ont une logique supplémentaire attachée (bien qu'elle soit découplée)
  4. En C # 3.0, vous pouvez utiliser des méthodes d'extension pour attacher les méthodes CRUD / business à un objet de domaine sans le toucher.
    • Est-ce une approche valable? Quels sont les avantages / inconvénients?
  5. Autres techniques?

Quelles sont les meilleures techniques pour gérer cela? Je suis assez nouveau pour DDD (je lis le livre Evans - alors peut-être que cela ouvrira les yeux)

Était-ce utile?

La solution

Martin Fowler a beaucoup écrit sur les modèles de domaine, y compris les modèles de domaine anemic . Il a également une brève description (et des diagrammes de classes UML) de nombreux modèles de conception pour les modèles de domaine et les bases de données qui pourraient être utiles: Catalogue de " ; Patterns of Enterprise Application Architecture " .

Je suggérerais de consulter les enregistrement actif et mappeur de données . Dans la description de votre problème, il semble que vos classes d’aide contiennent les détails de l’implémentation de la base de données et du domaine / règles de gestion.

L'enregistrement actif déplacerait la logique de domaine et le code de la base de données de l'assistant dans les autres objets de domaine (comme votre classe de base Entity ). Le mappeur de données déplacerait la logique de domaine de l'aide dans les objets de domaine et le code de la base de données dans un objet de carte séparé. L'une ou l'autre approche serait plus orientée objet que les classes auxiliaires de type procédural.

Eric Evans '& l'; Domain Driven Design " le livre est excellent. Ça devient un peu sec, mais ça vaut vraiment le coup. InfoQ dispose d'un " Conception pilotée par le domaine rapidement " mini-livre qui constitue une bonne introduction au livre d’Evans. Plus " Conception axée sur le domaine rapidement " est disponible au format PDF gratuit.

Autres conseils

Pour éviter le modèle anémique, modifiez vos classes d’aide:

Une logique comme:
"Customer.PurchaseProduct (produit, paiement, paiement)",

"Client.KillClient (tueur de personne, arme)"

doit exister directement dans " Client " objet de domaine.

Une logique comme:
"Client.IsCustomerAlive ()"
"Client.IsCustomerHappy ()"
devrait aller aux spécifications.

Une logique comme:
"Customer.Create ()",

"Customer.Update ()"
devrait évidemment aller aux référentiels.

Une logique comme:
"Customer.SerializeInXml ()"

"Customer.GetSerializedCustomerSizeInBytes ()"

devrait aller aux services.

Les constructeurs complexes devraient aller dans les usines.

C'est comme ça que je le vois. Je serais heureux si quelqu'un pouvait commenter ma compréhension de l'approche DDD.

Modifier:

Parfois - modèle de domaine anémique ne devrait pas être évité .

J'ai modifié ma réponse pour ajouter que DDD ne consiste pas à ramasser et à supprimer des modèles.
DDD est une façon de penser.

J'ai toujours considéré le modèle du domaine anémique comme un anti-modèle. Il est clair qu'un client achètera des produits, cette capacité peut être générée par une implémentation d'interface

Interface IPurchase
      Purchase(Product);

, afin que n'importe lequel de vos objets de domaine puisse ensuite l'implémenter si nécessaire. De cette façon, vous pouvez introduire des fonctionnalités dans vos objets de domaine, exactement là où elles devraient être.

Une approche que vous n'avez pas mentionnée consiste à utiliser AOP pour gérer votre accès aux données. Un exemple de mon utilisation récente de cette approche (bien que largement simplifiée aux fins de la publication) est que j'avais une entité de domaine Compte qui utilisait une méthode débit , encapsulant la logique métier requise. afin de pouvoir débiter correctement le compte.

N.B. Tout le code est en Java avec la notation AspectJ AOP ...

public boolean debit(int amount) {
    if (balance - amount >= 0) {
        balance = balance - amount;
        return true;
    }
    return false;
}

Avec le référentiel approprié injecté dans mon aspect, j'ai ensuite utilisé une coupure de point pour intercepter les appels de cette méthode ...

pointcut debit(Account account,int amount) :
    execution(boolean Account.debit(int)) &&
    args(amount) &&
    target(account);

... et appliqué quelques conseils:

after(Account account, int amount) returning (boolean result)  : debit(account,amount) {
    if (result) getAccountRepository().debit(account, amount);
}

À mon avis, cela permet de bien dissocier les préoccupations et permet à vos entités de domaine de se concentrer entièrement sur la logique métier de votre application.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top