Article sérialisé DDD
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:
- Concentrez-vous sur la complexité fondamentale et les opportunités du domaine
Alors, comment le modèle verbalisé de la connaissance du domaine est-il finalement représenté dans le code?
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.
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?
@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 ***.
Revenons à la définition basée sur le domaine.
- Concentrez-vous sur la complexité fondamentale et les opportunités du domaine
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