[JAVA] Quelle est la représentation de la connaissance du domaine dans le modèle [DDD]?

Article sérialisé DDD

Aperçu

Que dit Eric Evans de la définition de DDD Dans cet article, j'ai écrit que la définition du développement piloté par domaine est la suivante:

  1. Concentrez-vous sur la complexité fondamentale et les opportunités du domaine
  1. Explorez les modèles en collaboration avec des experts du domaine et des logiciels
  2. Écrire un logiciel qui représente explicitement ces modèles
  3. Parlez dans une langue omniprésente dans un contexte limité

Alors, comment le modèle verbalisé de la connaissance du domaine est-il finalement représenté dans le code?

État immuable

Tout d'abord, lorsque vous pensez aux contraintes commerciales, pensez aux *** conditions invariantes ***.

Condition invariante: condition qui doit être cohérente tout au long de la vie du modèle.

Il existe différents noms tels que les spécifications et les conditions de contrainte, mais le terme condition invariante est utilisé comme terme pour DDD, nous allons donc l'unifier.

Un modèle qui ne représente pas la connaissance du domaine

Par exemple, supposons que vous souhaitiez modéliser une tâche dans une application métier. Après avoir défini les exigences, j'ai trouvé que les conditions invariantes suivantes doivent être remplies.

Implémentons ceci.

@Entity
public class Task {
    @Id
    @GeneratedValue
    private Long id;
    private TaskStatus taskStatus;
    private String name;
    private LocalDate dueDate;
    private int postponeCount;
    public static final int POSTPONE_MAX_COUNT = 3;

    public Task() {
    }

    /*
     *Setter pour tous les articles
     */
    public void setId(Long id) { this.id = id; }
    public void setTaskStatus(TaskStatus taskStatus) { this.taskStatus = taskStatus; }
    public void setName(String name) { this.name = name; }
    public void setDueDate(LocalDate dueDate) { this.dueDate = dueDate; }
    public void setPostponeCount(int postponeCount) { this.postponeCount = postponeCount; }

    /*
     *Obtenez pour tous les articles
     */
    public Long getId() { return this.id; }
    public String getName() { return this.name; }
    public TaskStatus getTaskStatus() { return this.taskStatus; }
    public LocalDate getDueDate() { return this.dueDate; }
    public int getPostponeCount() { return this.postponeCount; }
    
    /*
     *Une méthode qui a à peine un comportement
     */
    public boolean canPostpone() { return this.postponeCount < POSTPONE_MAX_COUNT; }
}
public enum TaskStatus {
    UNDONE, DONE
}
public class TaskApplication {
    @Autowired
    private TaskRepository taskRepository;
    
    public void createTask(String name, LocalDate dueDate) {
        if (name == null || dueDate == null) {
            throw new IllegalArgumentException("Les éléments requis ne sont pas définis");
        }
        Task task = new Task();
        task.setTaskStatus(TaskStatus.UNDONE);
        task.setName(name);
        task.setDueDate(dueDate);
        task.setPostponeCount(0);
        taskRepository.save(task);
    }
    
    public void postponeTask(Long taskId) {
        Task task = taskRepository.findById(taskId);
        if (!taks.canPostpone()) {   	
            throw new IllegalArgumentException("Le nombre maximum de reports a été dépassé");
        }
        task.setDueDate(task.getDueDate().plusDays(1L));
        task.setPostponeCount(task.getPostponeCount() + 1);
        taskRepository.save(task);
    }
    //Le traitement de fin est omis
}

C'est fait! On dirait qu'il répond aux exigences. En regardant la classe TaskApplication, il ne semble y avoir aucun problème avec le processus de création et de mise à jour de la tâche. Libérons-le maintenant.

Cependant, quelques semaines après la sortie, un ingénieur qui n'est pas familier avec l'entreprise a implémenté le code suivant.

public class AnotherTaskApplication {
    @Autowired
    private TaskRepository taskRepository;
    
    public void createDoneTask(String name, LocalDate dueDate) {
        Task task = new Task();
        task.setTaskStatus(TaskStatus.DONE);  //× Génération de tâches à l'état terminé
        task.setPostponeCount(-1); //× Le nombre est un moins
        taskRepository.save(task);
    }

    public void changeTask(Long taskId, String name, LocalDate dueDate, TaskStatus taskStatus) {
        Task task = taskRepository.findById(taskId);
        task.setName(name); //× Changer le nom de la tâche qui ne doit pas être changé
        task.setDueDate(dueDate); //× Définissez la date d'échéance arbitrairement avec la valeur d'entrée, ignorez le nombre de reports
        task.setTaskStatu(taskStatus); //× Les tâches peuvent être rendues incomplètes
        taskRepository.save(task);     }
}

C'est incroyable, j'ai pu détruire les conditions invariantes.

Le problème est que la classe AnotherTaskAPplication est créée et implémentée, de sorte que la personne qui a implémenté la classe TaskApplication d'origine n'a pas remarqué qu'un tel traitement était écrit dans une autre classe. (Laissons le système d'examen pour l'instant)

Dans cet exemple, la spécification est exprimée par Application, mais le modèle de tâche lui-même, qui contient Setter / Getter dans tous les éléments, ne peut rien exprimer.

Cette condition est appelée *** anémie modèle de domaine ***.

Cet objet, qui n'est plus capable de représenter des conditions invariantes, n'est plus un modèle, mais simplement un *** modèle de données *** qui projette un modèle relationnel sur l'objet. Et une telle application peut être appelée *** script de transaction ***.

Alors, comment pouvons-nous faire en sorte que le modèle représente des conditions invariantes?

Un modèle qui exprime la connaissance du domaine

@Entity
class Task {
    @Id
    @GeneratedValue
    private Long id;
    private TaskStatus taskStatus;
    private String name;
    private LocalDate dueDate;
    private int postponeCount;
    private static final int POSTPONE_MAX_COUNT = 3;

    /*
     *Constructeur: représente une condition invariante lors de la création d'une entité
     */
    public Task(String name, LocalDate dueDate) {
        if (name == null || dueDate == null) {
            throw new IllegalArgumentException("Les éléments requis ne sont pas définis");
        }
        this.name = name;
        this.dueDate = dueDate;
        this.taskStatus = TaskStatus.UNDONE;
        this.postponeCount = 0;
    }

    /*
     *Méthode de transition d'état: représente une condition invariante liée à la transition d'état d'une entité créée
     */
    public void postpone() {
        if (postponeCount >= POSTPONE_MAX_COUNT) {
            throw new IllegalArgumentException("Le nombre maximum de reports a été dépassé");
        }
        dueDate.plusDays(1L);
        postponeCount++;
    }
    public void done() {
        this.taskStatus = TaskStatus.DONE;
    }
    
    //Il n'y a pas de paramètre de nom, vous ne pouvez donc pas changer le nom

    /*
     *getter, méthode d'acquisition d'état
     */
    public Long getId() { return id; }
    public String getName() { return name; }
    public LocalDate getDueDate() { return dueDate; }
    public boolean isUndone() { return this.taskStatus == TaskStatus.UNDONE; }
    public boolean canPostpone() { return this.postponeCount < POSTPONE_MAX_COUNT; }
}

//Dans le même package, enum est défini comme package private
enum TaskStatus {
    UNDONE, DONE
}
public class TaskApplication {
    @Autowired
    private TaskRepository taskRepository;

    public void createTask(String name, LocalDate dueDate) {
        Task task = new Task(name, dueDate); //les instances de tâche sont toujours créées avec des conditions invariantes
        taskRepository.save(task);
    }

    public void postpone(Long taskId) {
        Task task = taskRepository.findById(taskId);
        task.postpone(); //Le traitement qui détruit les conditions invariantes ne peut pas être écrit à partir de l'application pour l'objet acquis
        taskRepository.save(task);
    }
    //Le traitement de fin est omis
    //La validation est omise
}

Lorsque le processus d'imposition de conditions invariantes a été transféré au modèle ... Un modèle très expressif a été créé! !!

La condition invariante est magnifiquement exprimée par le constructeur du modèle Task et la méthode de transition d'état.

Certains des avantages de cette conception comprennent:

C'est une bonne chose.

Si vous écrivez ceci, vous pouvez dire que le modèle *** exprime la connaissance du domaine ***.

Rétrospective de la définition

Revenons à la définition basée sur le domaine.

  1. Concentrez-vous sur la complexité fondamentale et les opportunités du domaine
  1. Explorez les modèles en collaboration avec des experts du domaine et des logiciels
  2. Écrire un logiciel qui représente explicitement ces modèles
  3. Parlez dans une langue omniprésente dans un contexte limité

Avec l'exemple ci-dessus, nous avons pu écrire un logiciel qui représentait le modèle.

Cette conception à elle seule présente de grands avantages, mais le pilotage de domaine ne s'arrête pas là.
Comment un tel modèle se comportera-t-il et comment changera-t-il lorsque les spécifications seront modifiées? En faisant correspondre la définition des mots avec des experts du domaine (domaine), nous recherchons toujours le modèle le plus récent et le plus expressif et suivons le code.

Il s'agit littéralement de *** développement piloté par modèle de domaine ***.

J'espère que vous lisez cet article et que vous pensez: "Oh, peut-être DDD?" Je continuerai à écrire des articles pour diffuser DDD à l'avenir, alors restez en contact avec moi si vous le souhaitez.

Recommended Posts

Quelle est la représentation de la connaissance du domaine dans le modèle [DDD]?
Quelle est la différence entre les responsabilités de la couche domaine et de la couche application dans l’architecture onion [DDD]
'% 02d' Quel est le% de% 2?
Qu'est-ce que @Override ou @SuppressWarnings ("SleepWhileInLoop") devant la fonction? ?? ??
Qu'est-ce qu'un test? ・ À propos de l'importance d'un test
Quel est le test de code de test de modèle
Quelle est la méthode principale en Java?
Quelle est la structure des données d'ActionText?
Qu'est-ce que JSP? ~ Connaissons les bases de JSP !! ~
Qu'est-ce que le modèle Java Servlet / JSP MVC?
Ebean.update () n'est pas exécuté dans le modèle hérité.
Récupérer le premier jour de la semaine dans les paramètres régionaux actuels (quel jour est-il aujourd'hui?)
Qu'est-ce qu'un modèle MVC?
Dans Time.strptime,% j (date totale de l'année) est
Qu'est-ce que la conception axée sur le domaine tente de résoudre [DDD]
[DDD] Quelle est l'architecture la plus accessible pour démarrer avec la conception pilotée par domaine?
Quelle est la méthode pluck?
Passez en revue les connaissances de base du rubis souvent oubliées
Qu'est-ce que la classe BufferedReader?
A quoi sert le constructeur?
Quelle est la méthode d'initialisation?
L'histoire selon laquelle un modèle n'est pas une "représentation correcte du monde réel" / la nécessité d'un contexte borné
Vous voulez savoir ce que Ruby n est la puissance de 2? (Jugement de puissance de 2)
Que faire lorsque Cloud 9 est plein dans le didacticiel Rails
La milliseconde définie dans /lib/calendars.properties de Java jre est UTC
Le problème que l'attribut du modèle User devient nul dans ActionMailer
[Exemple de référence] Le droit d'auteur est décrit dans le commentaire du code source.
Qu'est-ce qu'un extrait de code en programmation?
Qu'Est-ce que c'est? ~ 3 types de "non" ~
Qu'est-ce que @Autowired dans Spring Boot?
Ordre de traitement dans le programme
Quel type de méthode est define_method?
Quelles sont les règles de JUnit?
Que faire lorsque le préfixe c n'est pas lié dans JSP
Code de la partie où server.servlet.session.timeout est défini dans spring.session.timeout dans le système spring-boot2
Qu'est-ce qui n'était pas une utilisation équitable du détournement d'API Java sur Android?
Erreur lors de la finalisation du membre de la classe Entity utilisé dans SpringWebFlux
Revue des connaissances «étranges Java» et Java souvent oubliées dans Java Bronze
Qu'est-ce que CHECKSTYLE: OFF trouvé dans la source Java? Checkstyle à savoir de
[Débutant] Qu'est-ce que Docker en premier lieu? Explication facile à comprendre des bases!
Qu'est-ce qu'une classe en langage Java (3 /?)
Obtenez le résultat de POST en Java
Spring Autowired est écrit dans le constructeur
Qu'est-ce que Swift? Résultats obtenus en 3 semaines
Quel est le meilleur type de données de migration? ??
[Note technique] Qu'est-ce que "inclure" dans Ruby?
L'identité des paramètres de rails [: id]
Quelle est la meilleure lecture de fichier (Java)
Qu'est-ce qu'une classe en langage Java (1 /?)
Qu'est-ce qu'une classe en langage Java (2 /?)
Quelles sont les fonctionnalités mises à jour de Java 13
À quoi sert le modèle de façade?
L'histoire de l'écriture de Java dans Emacs