Assegnazione della responsabilità per l'annullamento di un ordine

0

Durante una conversazione con il nostro esperto di domini possiamo utilizzare questa funzione:

A Customer service agent can cancel an Order by decreasing its quantity. To cancel an Order we decrease its quantity by the specified quantity, change its status to either partially cancelled or fully cancelled and log the CS agent who issued the cancellation.

Attualmente siamo divisi tra diverse soluzioni (supponendo che abbiamo agentId , orderId e cancelledQty dalla richiesta):

Soluzione 1:

CSAgent agent= (CSAgent) EmployeeRepository.getById(agentId);
Order order= OrderRepository.getById(orderId);
agent.cancel(order,cancelledQty);

vs
Soluzione 2:

CSAgent agent= (CSAgent) EmployeeRepository.getById(agentId);
Order order= OrderRepository.getById(orderId);
order.cancel(cancelledQty,agent);

La soluzione 1 cattura davvero ciò che i requisiti stanno trasmettendo e legge molto bene; tuttavia, soluzione 2 sembra più naturale quando si codifica l'implementazione del metodo cancel () in quanto aggiornerò principalmente i campi nella classe Order stessa che spero di mantenere private senza nessun setter pubblico .
L'unica cosa di cui ho bisogno dalla classe CSAgnet è un'istantanea dei suoi dati da memorizzare con Order .

Quindi, come devo determinare la migliore linea d'azione da una prospettiva DDD?

    
posta Songo 08.08.2014 - 15:35
fonte

3 risposte

5

La tua soluzione 2 è l'opzione migliore a causa dei singoli problemi di responsabilità e incapsulamento.

CSAgent agent= (CSAgent) EmployeeRepository.getById(agentId);
Order order= OrderRepository.getById(orderId);
order.cancel(cancelledQty,agent);

Mentre un agente invoca cancel() , una cancellazione non ha senso al di fuori del contesto di un oder. Quello che hai descritto è un classico esempio di un attore che agisce su un oggetto, invocando un metodo dell'oggetto.

Mettere l'oggetto 'cancel () method within CSAgent moves the CSAgent' un po 'più vicino a diventare un "oggetto dio".

Il tuo commento aggiuntivo di:

The only thing I need from the CSAgnet class is a snapshot of its data to be stored with the Order.

rafforza il motivo per cui la responsabilità di annullare un ordine appartiene a Order stessa.

Il tuo requisito aggiuntivo o potenziale di cancellazione parziale aggiunge la giustificazione per la seconda soluzione. Una cancellazione parziale avrà probabilmente una logica aggiuntiva oltre la completa cancellazione. Questa è la logica di business che appartiene al Order ed è qualcosa che la CSAgent dovrebbe ignorare beatamente.

    
risposta data 08.08.2014 - 16:46
fonte
2

La soluzione 1 viola il principio di responsabilità singola

  • CSAgent.cancel sembra che stiamo cancellando un agente!
  • I parametri del metodo suggeriscono che l'agente conosce abbastanza l'impianto per impostare correttamente lo stato dell'ordine. CSAgent sicuramente deve conoscere la differenza tra l'elaborazione degli ordini "parziale" e "completa".
  • Anche se CSAgent.cancel(order, quantity) chiama solo order.cancel(quantity) , CSAgent non ha semplicemente attività che eseguono il processo di annullamento dell'ordine per un'altra entità.

La soluzione 2 viola il principio di responsabilità singola

  • Basta cambiare CSAgent con Order sopra. È la stessa cosa.

Abbiamo bisogno di un oggetto divino

  • Bene, non esattamente; ma una classe che esegue il processo di cancellazione dell'ordine. Quel logging che manca a tutte le nostre soluzioni mi dice questo.
  • "OrderCancelProcessor" (solo per dargli un nome per il momento)

  • Sia CSAgent che Order possono avere CancelOrder() metodi ma non prenderanno riferimenti tra loro. Aggiornano solo il proprio stato.

  • Può essere elaborato in modo diverso a seconda dell'annullamento "completo" e "parziale".

Formalizza annullamento, incluso parziale e completo

  • Penso che un'entità OrderCancellation sarebbe naturalmente uscita dal processo DDD. Questo potrebbe rappresentare una richiesta di cancellazione dell'ordine immutabile (stato) - una cosa DDD.

  • Ho il sospetto che questo sarebbe passato a agent.cancel() e order.cancel() .

  • Allo stesso modo, è chiaro che ci sono diversi aspetti di "cancellazione". Sospetto che il OrderCancellation possa incapsulare i concetti "completo" e "parziale". In quanto tale comunica ai giocatori nel flusso di lavoro di cancellazione che questa cancellazione è parziale o completa.

  • Anche se la differenza (cancellazione parziale o completa) è letteralmente solo una questione di Order.Quantity zero o no; è un errore ignorare i concetti di dominio solo perché il codice sottostante può essere banale.

  • Se non incapsuli questi concetti, mi aspetto che le classi siano infestate da questo:

    • if(thisOrder.Quantity > 0) { ... } else { ...}
    • Garantisco alcuni / molti / la maggior parte / tutti i codici del cancro sopra non controllano i numeri negativi.
    • Questo codice del cancro diventa un poster figlio per DRY e SRP
  • Ogni classe coinvolta nell'annullamento del flusso di lavoro fa le sue cose in termini di annullamento dell'ordine e una quantità di ordine.

Ottenere insieme i giocatori del processo

  • Mi viene in mente Command Design Pattern . Questo design manterrà il CSAgent e il Order disaccoppiato e ci aiuterà ad aderire a SRP, DRY, ecc.

  • NOTA: il modello di comando potrebbe essere eccessivo. Tutto dipende dalle specifiche del modello di dominio di cui sono beatamente ignorante.

risposta data 10.08.2014 - 19:21
fonte
1

Se l'agente cambia stato, è necessaria una combinazione. In caso contrario, quindi la soluzione 2 con la seguente variante:

order.cancelByAgent(cancelledQty,agent.snapshot);
    
risposta data 08.08.2014 - 15:45
fonte

Leggi altre domande sui tag