introduction
- Lisez le livre DDD et laissez une note de votre compréhension (mise à jour de temps en temps).
――Pour la vérifiabilité, j'essaye de citer mes opinions autant que possible.
―― Je pense que l'exemple d'implémentation est décidé à partir des spécifications du langage Java en dehors du concept de DDD.
- L'exemple de code suppose une application Todo comme Wunderlist.
Sentiments divers sur le chemin
――Lorsque vous lisez l'explication du modèle de domaine et du service de domaine, je suis convaincu que la connaissance des règles métier conduira à l'isolement des problèmes.
- Traitement central (modèle de domaine, référentiel, usine) en tant que concept qu'une installation appelée banque devrait avoir
――Vous ne pouvez pas retirer plus que le solde de votre compte
―― Quel est le taux d'intérêt des produits d'investissement?
- Entreprise de commis de banque (service de domaine)
―― Traitement de transfert, traitement de réassemblage, etc.
--Service d'application pour augmenter la productivité des employés de la banque
――Échangez en yen ou en japonais, obtenez des formulaires en XML, etc.
--Service infra du système général
- Vous serez informé par e-mail que le transfert est terminé.
Hmmm, je pense que l'application Todo a un mauvais exemple. Dans le cas de Todo, il n'y a pas d'autre choix que de le saisir ou de le supprimer, et il n'y a presque aucun calcul. Le seul calcul qu'un objet de collection devrait avoir est de trier la date d'échéance dans l'ordre croissant, de collecter les favoris (vrai) et de les aligner à l'avant et le reste à l'arrière. Vous pouvez calculer la digestibilité de Task. Il n'y a aucune responsabilité de calculer quelque chose avec un Todo.
layer
+-- domain
| |
| +-- model
| | |
| | +-- Entity -- Aggregate
| | |
| | `-- Value Object
| |
| +-- Factory
| |
| +-- Repository ( command , query )
| |
| +-- Service
| |
| `-- ( shared )
|
+-- infrastructure - service
| | |
| | `-- ( mail sender )
| |
| `-- ( Repository Implementation )
| |
| +-- ( db I/O )
| |
| +-- ( file I/O )
| |
| `-- ( cache I/O )
|
+-- application -- Service
|
`-- interfaces
|
+-- ( controller )
| |
| `-- ( REST )
| |
| +-- ( XML )
| |
| `-- ( JSON )
|
+-- ( view )
| |
| `-- ( HTML )
|
`-- ( socket )
domain model
Livres de référence:
--Concept / Explication
- [Conception pilotée par le domaine Eric Evans (Evans)](https://www.amazon.co.jp/%E3%82%A8%E3%83%AA%E3%83%83%E3%82%AF % E3% 83% BB% E3% 82% A8% E3% 83% B4% E3% 82% A1% E3% 83% B3% E3% 82% B9% E3% 81% AE% E3% 83% 89% E3 % 83% A1% E3% 82% A4% E3% 83% B3% E9% A7% 86% E5% 8B% 95% E8% A8% AD% E8% A8% 88-Architectes% E2% 80% 99 Archive-% E3% 82% BD% E3% 83% 95% E3% 83% 88% E3% 82% A6% E3% 82% A7% E3% 82% A2% E9% 96% 8B% E7% 99% BA% E3% 81% AE% E5% AE% 9F% E8% B7% B5-% E3% 82% A8% E3% 83% AA% E3% 83% 83% E3% 82% AF% E3% 83% BB% E3% 82 % A8% E3% 83% B4% E3% 82% A1% E3% 83% B3% E3% 82% B9 / dp / 4798121967)
- [Conception pratique axée sur le domaine (Vernon)](https://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5%E3%83%89%E3%83%A1%E3% 82% A4% E3% 83% B3% E9% A7% 86% E5% 8B% 95% E8% A8% AD% E8% A8% 88-% E3% 83% B4% E3% 82% A1% E3% 83 % BC% E3% 83% B3% E3% 83% BB% E3% 83% B4% E3% 82% A1% E3% 83% BC% E3% 83% 8E% E3% 83% B3-ebook / dp / B00UX9VJGW )
- Exemple de mise en œuvre
- Principes de conception de système utiles sur le terrain (Masuda)
- Journal technique DDD (Kato) de Katojun
Entity
- Fonctionnalité
--Dispose d'un identifiant immuable tout au long de son cycle de vie.
- L'identité est confirmée par un identifiant.
- Les attributs autres que les identifiants sont variables.
- En principe, les opérations d'attributs se font via la logique métier et non par des setters.
- Implémentez equals () et hashCode () par vous-même
Présentation de l'entité
//Attributs variables autres que les identifiants
//Parce que les attributs sont indépendants les uns des autres dans cette classe@Setter est spécifié
//Lorsque les attributs sont liés les uns aux autres, effectuez des opérations d'attribut via cette classe de logique métier.
@Getter
@Setter
public final class Todo extends AbstractEntity<Todo> implements ValueObject {
// (AbstractEntity::private final UUID identifier)
private Title title;
private DueDate dueDate;
private TaskList taskList;
private Memo memo;
private Favorite favorite;
public Todo(final EntityIdentifier<Todo> identifier, final Title title, final DueDate dueDate, final TaskList taskList, final Memo memo, final Favorite favorite) {
super(identifier);
Validate.notNull(title);
Validate.notNull(dueDate);
Validate.notNull(taskList);
Validate.notNull(memo);
Validate.notNull(favorite);
this.title = title;
this.dueDate = dueDate;
this.taskList = taskList;
this.memo = memo;
this.favorite = favorite;
}
@Override
public Todo clone() {
return super.clone();
}
@Override
public boolean equals(final Object that) {
if (this == that) {
return true;
}
if (that == null || getClass() != that.getClass()) {
return false;
}
Todo o = (Todo) that;
if(!entityIdentifier.equals(o.entityIdentifier)){
return false;
}
return true;
}
@Override
public int hashCode() {
int result = Objects.hashCode(entityIdentifier);
result = 31 * result;
return result;
}
}
- ** Je souhaite suivre les objets en utilisant l'identité (= avoir un identifiant invariant tout au long du cycle de vie) **
- Qu'est-ce que l'identité d'entité?
--Différent de la similitude comparée par l'opérateur Java ==
-
- Ce mécanisme d'identité n'a pas de sens dans d'autres domaines d'application. (Evans n ° 2348-2349) *
- C'est un mécanisme pour réaliser la similitude sur la mémoire, et si elle sort de la mémoire, l'identité sera perdue.
-
- L'identité est un attribut intelligent et significatif d'une entité et ne peut pas être héritée par la fonctionnalité automatisée d'un langage de programmation. (Evans n ° 2349-2350) *
-
- Continuité tout au long du cycle de vie, où des distinctions importantes sont faites indépendamment des attributs pour les utilisateurs de l'application (Evans n ° 2339-2340) *
--Identité = équivalence basée sur l'identité, équivalence = égalité de valeur
-
- Ce n'est pas une entité qui est généralement utilisée pour désigner une entité. (Evans n ° 2339) *
- Dans certains cas, cela peut être Entity ou ValueObject
――Il n'y a pas de délimiteur dans le concept de mots, c'est décidé par la façon dont ils sont traités dans le modèle
-
- Le logiciel de la société de vente par correspondance nécessite une adresse pour confirmer la carte de crédit et adresser le colis. Cependant, même si les co-résidents passent commande auprès de la même entreprise, il n'est pas important de se rendre compte qu'ils habitent tous les deux au même endroit. Dans ce cas, l'adresse est un objet de valeur. (Evans n ° 2500-2502) *
-
- Le logiciel de service postal a un format hiérarchique composé de régions, villes, districts postaux et districts afin de systématiser les itinéraires de livraison, qui s'étend aux adresses individuelles. Ces objets d'adresse dérivent le code postal du parent dans la hiérarchie, et si le service postal décide de réaffecter le district postal, toutes les adresses qu'il contient se déplaceront ensemble. Ici, l'adresse est une entité. (Evans n ° 2503-2506 *
- ** L'identité est garantie en rendant l'identifiant invariant. Par conséquent, les attributs sont réécrits **
-
- Si un objet est identifié par identité plutôt que par attribut, alors cette identité doit être la première priorité lors de la définition de cet objet dans le modèle. (Evans n ° 2363-2364) *
Value Object
- Fonctionnalité
- Les attributs sont immuables.
- Les objets contenus dans List, etc. sont également immuables.
- Après avoir utilisé List # add () qui réécrit les attributs de l'intérieur, créez un nouvel objet et renvoyez-le.
- L'équivalence est confirmée en faisant correspondre les attributs.
- Dans certains cas, les attributs peuvent être variables.
- ** [Attention] ** Voir la section usine
- Implémentez equals () et hashCode () par vous-même
Présentation de ValueObject
// ----Fondamentalement, cela--------------------------
//Les attributs sont immuables
@Getter
public final class Task implements ValueObject {
private final String value;
private final Boolean done;
public Task(final String value) {
Validate.notNull(value);
this.value = value;
this.done = false;
}
public Task(final String value, final Boolean done) {
Validate.notNull(value);
Validate.notNull(done);
this.value = value;
this.done = done;
}
@Override
public boolean equals(final Object that) {
if (this == that) {
return true;
}
if (that == null || getClass() != that.getClass()) {
return false;
}
Task o = (Task) that;
if (!value.equals(o.value)) {
return false;
}
if (!done.equals(o.done)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = Objects.hashCode(value);
result = 31 * result + Objects.hashCode(done);
return result;
}
}
// ----Avoir un objet de valeur dans l'enfant--------------------------
//Les attributs sont immuables
//Cependant, si l'attribut est un objet tel que List, une certaine ingéniosité est requise.
public final class TaskList implements ValueObjectList<TaskList, Task> {
private final List<Task> taskList;
public TaskList() {
taskList = Collections.unmodifiableList(new ArrayList<>());
}
public TaskList(final List<Task> list) {
Validate.notNull(list);
// unmodifiableList()La raison est
//Liste retournée en passant par référence<Task>Assurez-vous que le contenu ne peut pas être réécrit
//Si vous souhaitez le réécrire, vous souhaitez forcer l'utilisation du constructeur.
//Puisque la déclaration finale ne fonctionne pas même dans l'élément, elle est utilisée dans cette position même au sens déclaratif.
taskList = Collections.unmodifiableList(list);
}
/*
Collections.unmodifiableList()La raison de l'utilisation
Si vous ne l'utilisez pas, le code de test suivant ne passera pas et vous ne pouvez pas garantir que les attributs ne changeront pas.
Si vous utilisez taskList.set(0, after)UnsupportedOperationException se produit dans
Si vous souhaitez réécrire l'élément, ArrayList<>(taskList)En passant au constructeur de TaskList
Lors de la réécriture des éléments, les attributs peuvent être garantis immuables (= un autre objet avec des attributs différents car le constructeur est utilisé).
TaskList target = new TaskList();
Task before = new Task("Acheter du lait", false);
target.add(before);
assertEquals(1, target.getList().size());
//Écraser le premier de la liste récupérée par un autre objet
List<Task> taskList = target.getList();
Task after = new Task("Vendre du lait", true);
taskList.set(0, after);
//Le montant écrasé n'est pas reflété dans la variable avant d'être sécurisé avant l'écrasement.
assertEquals(before.getFavorite(), target.getList().get(0).getFavorite());
*/
private static void accept(final Task w) {
}
public Optional<Task> find(final Task searchTask) {
Validate.notNull(searchTask);
Optional<Task> ret = Optional.empty();
for (Task existTask : getList()) {
if (existTask.equals(searchTask)) {
ret = Optional.of(existTask);
break;
}
}
return ret;
}
public List<Task> getList() {
return taskList;
}
public TaskList add(final Task... t) {
Arrays.stream(t).forEach(Validate::notNull);
List<Task> result = new ArrayList<>(taskList);
Arrays.stream(t).forEach(
v -> find(v).ifPresentOrElse(
TaskList::accept,
() -> result.add(v)
)
);
return new TaskList(result);
}
/*
La raison de ma régénération et de mon retour est
L'attribut interne est Collections#unmodifiableList()Il ne peut pas être modifié car il n'a pas changé.
En guise de mise en garde, List#add(Object):Si vous l'utilisez comme booléen, cela ne sera pas mis à jour et vous ne recevrez aucune nouvelle valeur.
*/
public TaskList remove(final Task... t) {
Arrays.stream(t).forEach(Validate::notNull);
List<Task> result = new ArrayList<>(taskList);
Arrays.stream(t).forEach(
v -> find(v).ifPresent(result::remove)
);
return new TaskList(result);
}
@Override
public boolean equals(final Object that) {
if (this == that) {
return true;
}
if (that == null || getClass() != that.getClass()) {
return false;
}
TaskList o = (TaskList) that;
if (!taskList.equals(o.taskList)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int result = Objects.hashCode(taskList);
result = 31 * result;
return result;
}
}
- ** En principe, les attributs et leur contenu ne sont pas réécrits. Devrait être jetable. ** **
- Pour deux personnes ayant le même nom et le même nom dans les données client, Value Object permet à deux personnes de partager un objet si elles ont le même nom et le même nom. Pourtant,,
-
- Si le nom d'une personne est changé, le nom de l'autre personne peut également changer! Pour éviter cela et pour partager l'objet en toute sécurité, l'objet doit être immuable. Cela signifie que vous ne pouvez pas le changer sauf en le remplaçant complètement. (Evans n ° 2533-2535) *
- Dans certains cas, cela peut être variable.
-
- Lorsque la valeur est mise à jour fréquemment, lorsque le coût de création et de destruction d'objets est élevé, lorsqu'il y a un problème de clustering lors du remplacement d'une instance, lorsque la valeur n'est pas partagée (amélioration du clustering, ou pour d'autres raisons techniques) Y compris) peut autoriser la variable. (Kato blog) *
- ** L'équivalence est garantie en faisant correspondre tous les attributs **
- Il n'y a pas de description explicite dans Evans ...
-
- Lorsqu'un objet de valeur est instancié, c'est un élément de conception qui n'a d'importance que ce qu'il est, et peu importe qui ou qui il est. (Evans n ° 2483-2484) *
-
- Les objets de valeur peuvent être constitués d'une combinaison d'autres objets. (Evans n ° 2490) *
- Relation entre la fenêtre (entité), le style de fenêtre (objet de valeur) et le matériau de construction de style (objet de valeur)
- ** * À mesure que le nombre d'attributs augmente, le coût explose lorsque vous souhaitez réécrire certains attributs. On dit que c'est un inconvénient des objets immuables. ** **
- Pour contourner ce problème, ValueObjectBuilder est implémenté.
――Il n'y a pas de concept de constructeur dans DDD, mais il semble que vous puissiez l'organiser à côté de l'usine dans le sens où il cache le processus de génération de béton.
factory
- Fonctionnalité
--Un objet qui entreprend la génération d'entité
--Déterminer s'il faut rendre la méthode statique par coût
- S'il est facile de créer une entité, il se peut qu'elle ne soit pas créée.
- Parfois, nous entreprenons le clonage de l'entité, y compris la variable ValueObject
Aperçu de l'usine
public class TodoFactory implements EntityFactory<Todo> {
@Override
public Todo create() {
return create("");
}
@Override
public Todo create(final EntityIdentifier<Todo> identifier) {
Validate.notNull(identifier);
Title t = new Title("");
DueDate d = new DueDate();
TaskList l = new TaskList();
Memo m = new Memo();
Favorite f = new Favorite();
return new Todo(identifier, t, d, l, m, f);
}
/**
* {@link Title}Spécifier{@link Todo}créer
*
* @param title
* @return
*/
public Todo create(final String title) {
Validate.notNull(title);
EntityIdentifier<Todo> identifier = new DefaultEntityIdentifier<>(Todo.class, UUID.randomUUID());
Title t = new Title(title);
DueDate d = new DueDate();
TaskList l = new TaskList();
Memo m = new Memo();
Favorite f = new Favorite();
return new Todo(identifier, t, d, l, m, f);
}
}
-
- Les référentiels et les usines ne sont pas dérivés du domaine en eux-mêmes (Evans n ° 3069-3070) *
- ** Objet qui génère un modèle de domaine **
-
- Concentrez-vous sur le début du cycle de vie et utilisez des usines pour créer et reconstruire des objets et des agrégats complexes. (Evans n ° 3066-3067) *
- ** S'il est facile à générer et qu'il n'y a pas de variation, vous n'avez pas à le préparer. ** **
- ** Dans certains cas, il reste à dupliquer le modèle de domaine **
-
- Puisque la méthode de clonage est une copie superficielle, vous pouvez oublier de faire une copie complète en tenant compte des objets variables, ou vous pouvez manquer l'initialisation en raison de non-final. (Kato blog) *
- ** Il ressort de l'histoire du défaut de la méthode de clonage qui incorpore la méthode de substitution **
- A également souligné que si vous essayez de dupliquer de manière compliquée avec la méthode de clonage du modèle de domaine, cela violera SRP.
-Cependant, si "si ce n'est pas le cas" et "ValueObject est un objet immuable", vous n'avez pas à y penser. L'objet de valeur immuable n'autorise pas les modifications élémentaires et n'autorise pas les setters. Par conséquent, puisque "changement de valeur = création d'un nouvel objet", même si le clone est une copie superficielle, le résultat après le changement de valeur est le même que celui au moment de la copie profonde.
- "Si vous créez un objet de valeur variable" est un point à considérer.
repository
-
- Les référentiels et les usines ne sont pas dérivés à part entière du domaine (Evans n ° 3069-3070) *
- ** Objet qui persiste le modèle de domaine **
-
- Le référentiel encapsule l'énorme infrastructure requise pour l'accès, tout en fournissant un moyen d'accéder aux objets persistants au milieu et à la fin du cycle de vie. (Evans n ° 3067-3069) *
- ** Objet pour restaurer le modèle de domaine **
-
- Combien de pages spécifiquement? *
agrégation
-
- La portée délimitée par l'agrégation indique dans quelle mesure la condition invariante doit être maintenue. (Evans n ° 3073-3074) *
- ** Objet qui a synthétisé le modèle de domaine **
-
- Combien de pages spécifiquement? *
- ** Limite de transaction **
-
- Combien de pages spécifiquement? *
domain service
-
- Un service est une opération fournie en tant qu'interface indépendante dans le modèle et n'encapsule pas l'état comme une entité et un objet de valeur. (Evans n ° 2632-2634) *
-
- Défini uniquement du point de vue de ce qui peut être fait au client (Evans n ° 2636-2637) *
-
- Les arguments et les résultats doivent être des objets de domaine (Evans n ° 2641) *
-
- Un excellent service a trois caractéristiques. (Evans n ° 2645-2648) *
- L'opération implique le concept de domaine, qui n'est pas une ** partie naturelle d'une entité ou d'un objet de valeur **.
- L'interface est définie du point de vue des ** autres éléments du modèle de domaine **.
- Il n'y a aucun état ** dans l'opération **.
-
- Les services techniques ne devraient avoir aucune signification commerciale. (Evans n ° 2673) *
- De l'histoire de la modélisation du terme commercial d'une banque
«En d'autres termes, un comportement commercial significatif doit être orienté vers le modèle de domaine.
- ** Lors du traitement de plusieurs modèles de domaine et de la réalisation de la logique métier **
-
- La fonction de transfert de fonds d'un compte à un autre est un service de domaine. (Evans n ° 2680-2681) *
- ** S'il n'est pas adapté à la logique métier du modèle de domaine **
-
- Cela devient difficile à gérer si vous mettez l'opération de "transfert" dans l'objet compte. Cette opération implique deux comptes et quelques règles globales (Evans n ° 2684-2685) *
- ** Cependant, ne créez pas de services de domaine autant que possible **
-
- Combien de pages spécifiquement? *
application service
- ** Lors de la réalisation du cas d'utilisation de l'application utilisant le modèle de domaine **
-
- Par exemple, si une application bancaire peut convertir des transactions et les exporter vers un fichier tableur pour que nous les analysions, l'exportation est un service d'application. Dans le domaine bancaire, le «format de fichier» n'a pas de sens et les règles métier ne sont pas impliquées. (Evans n ° 2677-2680) *
-** Très fin **
―― Cela signifie que vous ne traversez pas le calque. En regardant la description ci-dessus, ce n'est pas du tout mince, comme la génération d'une feuille de calcul.
-
- Combien de pages spécifiquement? *
infrastructure service
-
- La plupart des services abordés dans la littérature sont purement techniques et ces services appartiennent à la couche infrastructure. (Evans n ° 2662-2663) *
-
- Si le solde de votre compte tombe en dessous d'une certaine limite, vous pouvez envoyer un e-mail à vos clients. Ce système de courrier électronique est encapsulé par une interface, qui peut inclure des moyens de notification alternatifs dans certains cas. Cette interface est un service de la couche infrastructure (Evans n ° 2664-2667) *
interfaces
--GET / POST / PUT / DELETE et autres endroits pour recevoir
- N'est-ce pas le port de connexion socket?