Permettez-moi de commencer par m'excuser pour la longueur du message, mais je voulais vraiment transmettre autant de détails à l'avance, donc je ne prends pas votre temps à faire des allers-retours dans les commentaires.
Je suis en train de concevoir une application suivant une approche DDD et je me demande quels conseils je peux suivre pour déterminer si une racine agrégée doit contenir un autre AR ou si elles doivent être laissées en tant qu'AR «autonomes» distincts.
Prenons le cas d'une application d'horloge simple qui permet aux employés de se synchroniser pour la journée. L'interface utilisateur leur permet de saisir leur ID d'employé et leur code PIN qui sont ensuite validés et l'état actuel de l'employé récupéré. Si l'employé est actuellement synchronisé, l'interface utilisateur affiche un bouton "Clock Out"; et, inversement, s'ils ne sont pas synchronisés, le bouton affiche "Clock In". L'action effectuée par le bouton correspond également à l'état du salarié.
L'application est un client Web qui appelle un serveur principal exposé via une interface de service RESTful. Mon premier passage à créer des URL intuitives et lisibles a abouti aux deux points de terminaison suivants:
http://myhost/employees/{id}/clockin
http://myhost/employees/{id}/clockout
REMARQUE: ceux-ci sont utilisés après la validation de l'ID et du code PIN de l'employé et la transmission d'un "jeton" représentant "l'utilisateur" dans un en-tête. Cela est dû au fait qu'il existe un «mode gestionnaire» qui permet à un gestionnaire ou à un superviseur de pointer ou de quitter un autre employé. Mais, pour le bien de cette discussion, j'essaie de rester simple.
Sur le serveur, j'ai un ApplicationService qui fournit l'API. Mon idée initiale pour la méthode ClockIn est quelque chose comme:
public void ClockIn(String id)
{
var employee = EmployeeRepository.FindById(id);
if (employee == null) throw SomeException();
employee.ClockIn();
EmployeeRepository.Save();
}
Cela semble assez simple jusqu'à ce que nous réalisions que les informations de carte de temps de l'employé sont réellement conservées sous forme de liste de transactions. Cela signifie que chaque fois que j'appelle ClockIn ou ClockOut, je ne change pas directement l'état de l'employé mais, au lieu de cela, j'ajoute une nouvelle entrée dans la feuille de temps de l'employé. L'état actuel de l'employé (cadencé ou non) est dérivé de l'entrée la plus récente de la feuille de temps.
Donc, si je choisis le code ci-dessus, mon référentiel doit reconnaître que les propriétés persistantes de l'employé n'ont pas changé, mais qu'une nouvelle entrée a été ajoutée dans la feuille de temps de l'employé et effectuer une insertion dans le magasin de données.
D'un autre côté (et voici la question ultime de l'article), TimeSheet semble être à la fois une racine agrégée et une identité (l'ID et la période de l'employé) et je pourrais tout aussi facilement implémenter la même logique que TimeSheet. (employeeId).
Je me retrouve à débattre du bien-fondé des deux approches et, comme indiqué dans le premier paragraphe, je me demande quels critères je devrais évaluer pour déterminer quelle approche convient le mieux au problème.