0

Dans mon projet DDD, j'essaie d'implémenter le modèle d'état avec java enum.notification de validation de modèle d'état ddd

J'ai un problème lors de la validation de méthodes d'entité qui ont un comportement dépendant de l'état.

Pour valider, j'utilise le modèle de notification.

Je suis l'approche "entité toujours valide", de sorte que dans chaque opération, j'appelle d'abord une méthode de validation "isValidForOperation".

Voici le code, juste pertinent pour la simplicité:

L'entité:

public class Task extends AggregateRoot<TaskId> { 

    ... 
    private State state; 
    ... 


    // Operation with behaviour depending on the state 
    // It's a transition from "ASSIGNED" state to "IN_PROGRESS" state 
    // I apply the state pattern here 

    public void start() { 
     State next = this.state.start (this); 
     this.setState (next); 
    } 

    ... 
} 

ENUM java modélisation de l'état:

public enum State { 

     ASSIGNED { 

      public State start (Task task) { 

       // Validation method to ensure the operation can be done 
       assertTaskIsValidForStart (task); 

       // Business logic 
       ... 

       // Return the next state 
       return (State.IN_PROGRESS); 
      } 
     } 
     ... 
     // more enum values for other states 
     ... 


     // Default implementation of "start" operation 
     // It will be executed when the current state is not "ASSIGNED" 
     // So an error would be generated 

     public State start (Task task) { 

      // I can't apply notification pattern here !!! 
      // I would have to throw an exception 

     } 

} 

La méthode de validation suit le modèle de notification. Il recueille toutes les erreurs possibles dans un objet de notification. Cet objet de notification est transmis à une exception. L'exception est levée, puis la couche d'application l'attrape et renvoie tous les messages d'erreur au client.

public void assertTaskIsValidForStart (Task task) { 

     Notification notification = new Notification(); 
     if (errorCondition (task)) { 
      notification.addError(...); 
     } 
     ... 
     // more errors 
     ... 
     if (notification.hasErrors()) { 
      throw new TaskNotValidForStartException (notification.errors()); 
     } 

    } 

Comment pourrait être le modèle de notification appliqué (en conjuntion avec le motif d'état) lorsque la condition d'erreur est sur les transitions entre les états invalides?

Des idées?

MISE À JOUR:

J'ai trouvé une solution. Je mets toute l'opération qui dépend de l'état dans l'entité, et applique le modèle d'état plus fin, juste au code nécessaire. De cette façon, j'applique le motif pour calculer juste l'état suivant, de sorte que je peux vérifier si la transition est permise et appliquer le modèle de notification aussi.

code:

public class Task extends AggregateRoot<TaskId> { 

     ... 
     private State state; 
     ... 


     // Operation with behaviour depending on the state 
     // It's a transition from "ASSIGNED" state to "IN_PROGRESS" state 
     // I apply fine-grained state pattern here 

     public void start() { 

      // Validation method to ensure the operation can be done 
      // One of the validations will be if the transition is allowed 

      assertTaskIsValidForStart (this); 


      // Business logic 
      // If it depends on the state, I would apply state pattern delegating to another method 
      ... 


      // Set the next state 

      State next = this.nextStateForStart(); 
      this.setState (next); 
     } 

     ... 

     public State currentState() { 
      return this.state; 
     } 
     ... 

     public State nextStateForStart() { 
      return this.currentState().nextStateForStart(); 
     } 

     ... 
    } 



public enum State { 

     ASSIGNED { 
      public State nextStateForstart() { 
       return (State.IN_PROGRESS); 
      } 
     } 
     ... 
     // more enum values for other states 
     ... 


     // Default implementation of "start" transition 
     // It will be executed when the current state is not "ASSIGNED" 

     public State nextStateForstart() { 
      return null; 
     } 

} 



public void assertTaskIsValidForStart (Task task) { 

     Notification notification = new Notification(); 

     // Validate the transition is allowed 

     if (task.nextStateForStart() == null) { 
      notification.addError(...); 
     } 

     ... 
     // more errors 
     ... 

     if (notification.hasErrors()) { 
      throw new TaskNotValidForStartException (notification.errors()); 
     } 

    } 

Répondre

1

Je pense que votre ENUM fait trop. En plus d'avoir un ensemble fixe d'états qui ne peuvent guère être étendus, il est difficile d'introduire toute forme de contrat pour chaque état concret, ce qui résoudrait également votre problème de notification.

Introduire une classe d'état abstrait qui est la classe de base pour tous les états concrets. Un contexte est passé qui permet de définir un état successeur pour chaque état. Ce contexte peut être implémenté par votre racine agrégée. Votre notification peut être gérée par chaque état d'une manière que vous appliquez en utilisant AbstracftState, par ex. en forçant l'exécution de l'Etat pour renvoyer un objet de notification:

interface StateContext { 
    setState(AbstractState state); 
} 

class AbstractState { 
    abstract Notification execute(StateContext context); 
} 

class Task extends AggregateRoot implements StateContext { 
    AbstractState currentState; 

    .... 

    public void start() { 
     Notification n = currentState.execute(this); 
     if (n.hasErrors()) { 
      throw new Exception(n.toErrorReport()); 
     } 
    } 
} 

Maintenant, vous pouvez gagner des erreurs de chaque état avant ou après l'exécution (vous pouvez introduire un validateStart() dans chaque AbstractState qui est appelé avant l'exécution) et signaler les erreurs collectées à l'appelant.

+0

Bonjour @mbnx. Où retournes-tu l'état suivant? L'implémentation de la méthode "execute" ne peut pas, car elle renvoie la notification. – choquero70

+0

L'état peut être défini à l'aide de l'interface StateContext transmise à la méthode execute. – mbnx

0

Je modéliserais le TaskWorkflow en VO à l'intérieur de l'agrégat de tâches.

class Task { 

    private Workflow workflow; 

    public void start() { 
     workflow = workflow.followWith(Action.START, this); 
    } 

    public State currentState() { 
     return workflow.state(); 
    } 

    public List availableActions() { 
     return workflow.nextActions(); 
    } 

} 

Le flux de travail est un FSM composé de transitions entre des états joints par des actions. Tout appel à une méthode de workflow crée une nouvelle représentation de workflow pointant vers le nouvel état. Les transitions peuvent être modélisées comme une coutume directe ou plus complexe impliquant une logique métier comme vous le dites. Si vous utilisez un langage fonctionnel, vous pouvez renvoyer une Monade pour traiter les erreurs, mais dans ce cas vous pouvez en réifier et en créer une ou vous pouvez simplement lancer une exception représentant des messages agrégés.

Espérons que ça aide.

+0

Je suppose que vous implémentez le modèle d'état dans l'objet de workflow, pas vous? La méthode suivante est où vous implémentez la transition? – choquero70