Question Techniques de traitement du modèle de domaine anémique


J'ai lu certaines des questions concernant les modèles de domaines anémiques 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? À mon travail, nous avons un modèle assez anémique, et nous utilisons actuellement des classes "helper" pour exécuter 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 le StoreHelper et appelle la méthode sur les objets du domaine. Pour moi, il serait judicieux pour le client / produit de savoir s’enregistrer dans un référentiel, mais vous ne voudriez probablement pas utiliser les méthodes Save () sur les objets du domaine. Cela serait également logique pour une méthode comme Customer.Purchase (Product), mais cela place la logique de domaine sur l'entité.

Voici quelques techniques que je rencontre, mais je ne sais pas lesquelles sont bonnes / mauvaises:

  1. Customer et Product héritent d'une classe "Entity", qui fournit les opérations CRUD de base de manière générique (à l'aide d'un ORM peut-être).
    • Avantages: Chaque objet de données obtiendrait 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 et lie également tous les objets de domaine à une entité de base qui pourrait ne pas être appropriée
  2. Utiliser des classes d'assistance pour gérer les opérations CRUD et la logique métier
    • Est-il judicieux d'avoir des DAO pour les opérations "pure database" et des assistants commerciaux distincts pour les opérations plus spécifiques à l'entreprise?
    • 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 (ressemble au code C)
  3. Utiliser 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 (même si elle est découplée)
  4. Dans C # 3.0, vous pouvez utiliser des méthodes d'extension pour associer les méthodes CRUD / métier à un objet de domaine sans le toucher
    • Est-ce une approche valide? Quels sont les avantages / inconvénients?
  5. D'autres techniques?

Quelles sont les meilleures techniques pour y remédier? Je suis assez nouveau pour DDD (je lis le livre d'Evans - alors peut-être que cela va m'ouvrir les yeux)


20
2018-03-04 07:00


origine


Réponses:


Martin Fowler a beaucoup écrit sur les modèles de domaine, y compris modèles de domaine anémiques. Il a également de brèves descriptions (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 des "modèles d'architecture d'application d'entreprise".

Je suggère de regarder le Record actif et Data Mapper motifs. D'après la description de votre problème, il semble que vos classes d'assistance contiennent à la fois des règles de domaine / métier et détails d'implémentation de base de données.

L'enregistrement actif déplacerait la logique de domaine et le code de base de données de l'assistant dans les autres objets de domaine (comme votre Entity classe de base). Le mappeur de données déplacera la logique de domaine de l'assistant dans les objets du domaine et le code de la base de données dans un objet cartographique distinct. Les deux approches seraient plus orientées objet que les classes auxiliaires de style procédural.

Le livre d'Eric Evans intitulé "Domain Driven Design" est excellent. Ça devient un peu sec, mais ça en vaut vraiment la peine. InfoQ a un Mini-livre "Design rapide dirigé par le domaine" c'est une bonne introduction au livre d'Evans. De plus, "Domain Driven Design Rapidly" est disponible en format PDF gratuit.


7
2018-03-07 08:13



Afin d'éviter un modèle anémique, modifiez vos classes d'assistance:

Logique comme:
"Customer.PurchaseProduct (Product product, Payment payment)",
"Customer.KillCustomer (tueur de personnes, arme d'arme)"
devrait exister directement dans l'objet de domaine "Client".

Logique comme:
"Customer.IsCustomerAlive ()"
"Customer.IsCustomerHappy ()"
devrait aller aux spécifications.

Logique comme:
"Customer.Create ()",
"Customer.Update ()"
évidemment devrait aller aux dépôts.

Logique comme:
"Customer.SerializeInXml ()"
"Customer.GetSerializedCustomerSizeInBytes ()"
devrait aller aux services.

Les constructeurs complexes devraient aller aux 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 concerne pas la collecte et la suppression de modèles.
DDD est sur le point de penser.


15
2018-05-21 13:46



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

Interface IPurchase
      Purchase(Product);

Ainsi, n'importe quel objet de votre domaine peut l'implémenter comme requis. De cette façon, vous pouvez introduire des fonctionnalités dans vos objets de domaine, ce qui est exactement ce qu’il devrait être.


2
2018-03-04 09:12



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 qu’elle ait été grandement simplifiée à des fins de publication) était que j’avais un Account entité de domaine qui avait un debit méthode, en encapsulant la logique métier requise pour effectuer un débit réussi à partir du compte.

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

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

Avec le dépôt approprié injecté dans mon aspect, j'ai ensuite utilisé un pointcut pour intercepter les appels à cette méthode ...

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

... et appliqué des conseils:

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

À mon avis, cela donne une bonne séparation des préoccupations et permet à vos entités de domaine de se concentrer entièrement sur la logique métier de votre application.


0
2017-12-19 10:19