Mon entreprise est en train de réécrire notre application Web à partir de zéro. Il s'agit d'une grande application de niveau entreprise avec un domaine complexe dans le secteur financier.
Nous utilisons un ORM (Entity Framework) pour la persistance.
En substance, la moitié de notre application se concentre sur la collecte de données brutes de l'utilisateur, leur stockage, puis l'autre moitié de l'application qui contient la plupart de notre logique de domaine réelle prend ces données brutes pour créer notre image de domaine qui diffère grandement de celles d'origine entrées brutes, et le passe dans un moteur de calcul, exécute des calculs et crache des résultats, qui sont ensuite affichés à l'utilisateur.
Dans une approche DDD utilisant des couches, il semble que les opérations CRUD passent par la couche domaine. mais au moins dans notre cas, cela ne semble pas avoir de sens.
Par exemple, lorsqu'un utilisateur accède à l'écran d'édition pour modifier un compte d'investissement, les champs à l'écran sont les champs exacts stockés dans la base de données, et non la représentation de domaine utilisée ultérieurement pour les calculs. Alors, pourquoi devrais-je charger la représentation du domaine du compte d'investissement lorsque l'écran d'édition a besoin de la représentation de la base de données (entrées brutes)?
Une fois que l'utilisateur a cliqué sur "Terminé" sur l'écran du compte d'investissement, et qu'un POST est fait au contrôleur, le contrôleur a maintenant à peu près une représentation exacte de la base de données du compte d'investissement qu'il doit enregistrer Mais pour une raison quelconque, je suis censé charger la représentation du domaine pour apporter des modifications au lieu de simplement mapper le modèle du contrôleur directement sur le modèle de base de données (modèle de structure d'entité)?
Donc, en gros, je mappe un modèle de données au modèle de domaine, juste pour qu'il puisse ensuite être mappé sur le modèle de données pour persister. Comment cela peut-il avoir un sens?
Imaginez que vous implémentez votre page de création de compte en mappant le post de formulaire directement sur un objet EF qui est ensuite enregistré dans la base de données.
Supposons en outre que la base de données comporte diverses restrictions qui empêchent la saisie de données complètement erronées. Les comptes ont toujours des clients, etc.
Tout semble fonctionner correctement. Mais alors, l'entreprise établit une nouvelle règle.
Maintenant, vous devez mettre cette logique quelque part et vous n'avez pas d'objet de domaine pour le mettre.
DDD suppose que vous aurez toujours ce genre de règles, et c'est probablement le cas. La création d'un compte doit comporter plusieurs vérifications, un journal d'audit, etc., cela ne va pas simplement être "écrire une ligne dans la base de données"
Planifiez votre domaine en supposant qu'il n'y a pas de persistance ou de contrôleurs MVC avec une logique supplémentaire. Assurez-vous de capturer tous des exigences et elles sont toutes dans le modèle de domaine.
Comment cela peut-il avoir un sens?
Réponse courte: elle ne fonctionne pas .
Réponse plus longue: les modèles lourds de développement d'un modèle de domaine ne s'appliquent pas aux parties de votre solution qui ne sont qu'une base de données.
di Dahan avait une observation intéressante qui pourrait aider à clarifier cette
Dahan considère qu'un service doit avoir à la fois une sorte de fonctionnalité et des données. S'il n'a pas de données, alors c'est juste une fonction. Si tout ce qu'il fait, c'est effectuer des opérations CRUD sur des données, alors c'est une base de données.
Le but du modèle de domaine, après tout, est de garantir que toutes les mises à jour des données maintiennent l'invariant commercial actuel. Ou, pour le dire autrement, le modèle de domaine est chargé de s'assurer que la base de données qui agit comme système d'enregistrement est correcte.
Lorsque vous traitez avec un système CRUD, vous n'êtes généralement pas le système d'enregistrement des données. Le monde réel est le livre des records, et votre base de données n'est qu'une représentation localement mise en cache du monde réel.
Par exemple, la plupart des informations qui apparaissent dans un profil d'utilisateur, comme une adresse e-mail ou un numéro d'identification émis par le gouvernement, ont une source de vérité qui vit en dehors de votre entreprise - c'est celle de quelqu'un d'autre administrateur de messagerie qui attribue et révoque les adresses e-mail, pas votre application. C'est le gouvernement qui attribue les SSN, pas votre application.
Donc, vous n'allez normalement pas faire de validation de domaine sur les données qui vous viennent du monde extérieur; vous pourriez avoir des contrôles en place pour vous assurer que les données sont bien formées et correctement aseptisées ; mais ce ne sont pas vos données - votre modèle de domaine ne reçoit pas de veto.
Dans une approche DDD utilisant des couches, il semble que les opérations CRUD passent par la couche domaine. mais au moins dans notre cas, cela ne semble pas avoir de sens.
C'est vrai pour le cas où la base de données est le livre d'archives.
Cependant, en travaillant sur beaucoup de code hérité, j'observe des erreurs courantes pour identifier ce qui est à l'intérieur du domaine et ce qui est à l'extérieur.
Une application ne peut être considérée comme CRUD que s'il n'y a pas de logique métier autour du modèle de données. Même dans ce (rare) cas, votre modèle de données n'est pas votre modèle de domaine. Cela signifie simplement que, comme aucune logique métier n'est impliquée, nous n'avons besoin d'aucune abstraction pour la gérer, et donc nous n'avons pas de modèle de domaine.
Nous utilisons le modèle de domaine pour gérer les données qui appartiennent à l'intérieur du domaine; les données provenant de l'extérieur du domaine sont déjà gérées ailleurs - nous ne faisons que mettre en cache une copie.
Greg Young utilise systèmes d'entrepôt comme illustration principale des solutions où le livre d'archives est ailleurs (c.-à-d. Le plancher de l'entrepôt). L'implémentation qu'il décrit ressemble beaucoup à la vôtre - une base de données logique pour capturer les messages reçus de l'entrepôt, puis une base de données logique distincte mettant en cache les conclusions tirées de l'analyse de ces messages.
Alors peut-être que nous avons deux contextes bornés ici? Chacun avec un modèle différent pour un
investment account
Peut être. Je serais réticent à le marquer comme un contexte délimité, car il n'est pas clair quels autres bagages sont accompagnés. Il se peut que vous ayez deux contextes, ce pourrait être un contexte avec des différences subtiles dans le langage omniprésent que vous n'avez pas encore compris.
Test décisif possible: combien d'experts de domaine avez-vous besoin de deux experts de domaine pour couvrir ce spectre, ou d'un seul qui parle des composants de différentes manières. Fondamentalement, vous pourriez être en mesure de deviner combien de contextes délimités vous avez en travaillant la loi de Conway à l'envers.
Si vous considérez que les contextes délimités sont alignés sur les services, cela peut être plus simple: devriez-vous être en mesure de déployer ces deux fonctionnalités indépendamment? Oui suggère deux contextes délimités; mais s'ils doivent être synchronisés, alors peut-être que ce n'est qu'un.
Dans votre domaine, vous ne devriez pas avoir à savoir que la base de données existe même.
Votre domaine concerne les règles métier. Les choses qui doivent survivre lorsque l'entreprise qui a créé votre base de données ferme ses portes. Autrement dit, si vous voulez que votre entreprise survive. C'est vraiment bien quand ces règles ne se soucient pas que vous ayez changé la façon dont vous conservez les données.
Les détails de la base de données existent et doivent être traités. Ils devraient vivre ailleurs. Mettez-les à travers une frontière. Contrôlez soigneusement la façon dont vous communiquez à travers cette frontière ou ce n'est pas une frontière.
Oncle Bob a ceci à dire sur quoi mettre vos données:
En règle générale, les données qui franchissent les frontières sont de simples structures de données. Vous pouvez utiliser des structures de base ou de simples objets de transfert de données si vous le souhaitez. Ou les données peuvent simplement être des arguments dans les appels de fonction. Vous pouvez également l'intégrer dans une table de hachage ou la construire dans un objet.
L'important est que des structures de données isolées et simples traversent les frontières. Nous ne voulons pas tricher et transmettre des lignes d'entités ou de bases de données. Nous ne voulons pas que les structures de données aient une sorte de dépendance qui viole la règle de dépendance.
[…] Lorsque nous transmettons des données à travers une frontière, c'est toujours sous la forme qui convient le mieux au cercle intérieur.
Il explique également comment vos couches externes doivent être des plugins pour vos couches internes afin que les couches internes ne sachent même pas qu'elles existent.
Suivez quelque chose comme ça et vous aurez un bel endroit pour ignorer la base de données où vous pouvez vous soucier des règles de validation d'entrée, des règles qui doivent être persistées d'une manière ou d'une autre, des règles pour exécuter des calculs, des règles pour envoyer ces résultats à n'importe quelle sortie. Il est en fait plus facile de lire ce type de code.
C'est soit ça, soit vous décidez que votre domaine est vraiment juste pour manipuler la base de données. Dans ce cas, la langue de votre domaine est SQL. Si c'est bien, mais ne vous attendez pas à ce que votre mise en œuvre des règles métier survive à un changement de persistance. Vous finirez par avoir besoin de les réécrire complètement.
Appliquer la théorie DDD:
Il existe deux contextes délimités dans ce domaine:
Chaque contexte délimité peut avoir une conception architecturale différente.
Exemple:
Le compte d'investissement du client est une entité (peut-être un agrégat, dépend du domaine) et la persistance des données se fait via le référentiel de l'entité (RDB ou autre type de base de données comme une base de données OO).
Il n'y a pas d'approche DDD pour les opérations CRUD. Pour avoir un champ DB lié aux données d'un objet, les principes de conception sont rompus.