Contesto
Sto sviluppando un'applicazione che usa un approccio basato sul Domain Driven Design. Voglio utilizzare un modello di design laddove appropriato e applicare tutti i principi SOLID .
Scenario
Ho un ordine e voglio consentire ai clienti di aggiungere righe di ordini ad esso.
Considera il seguente pseudo codice:
class Order
OrderLines[] (readonly)
AddOrderLine(orderLine)
class OrderLine
Product
Amount
class OrderLineFactory
OrderLine Create(product, amount)
class OrderLineRepository
Save(orderLine)
Ora consente di aggiungere una OrderLine all'Ordine:
Order.AddOrderLine(OrderLineFactory.Create(product: "Chocolate Cake", amount: 3))
A proposito, OrderLineFactory è lì perché, nel mio caso, c'è una logica relativamente complessa nella creazione delle linee d'ordine. Questo è uno dei motivi principali per applicare il modello di fabbrica.
problema
Il problema è che potrei farlo invece ...
orderLine = OrderLineFactory.Create(product: "Chocolate Cake", amount: 3)
OrderLineRepository.Save(orderLine)
... e aggirare il metodo Order.AddOrderLine
, abbreviando efficacemente tutti i controlli che vengono eseguiti in quel metodo (ad esempio proteggendo contro i duplicati o limitando la quantità dell'ordine).
Lavorando verso una soluzione
Un argomento potrebbe essere che non ci dovrebbe essere un metodo Save
su OrderLineRepository
, perché OrderLine non è una radice aggregata. Questo (proprio come voglio) renderà il cliente incapace di memorizzare la OrderLine nuda se non aggiungendola all'Ordine e salvando l'Ordine di conseguenza.
Tuttavia, il fatto di avere un metodo Save
su repository che riguardano gli aggregati non root, consente di salvare direttamente le modifiche apportate a loro, come ad esempio:
orderLine = orderLineRepository.Get(31923)
orderLine.Amount = 5
OrderLineRepository.Save(orderLine)
Non essere in grado di salvare OrderLine mi richiederebbe di fare qualcosa di simile ...
order = OrderRepository.FindByOrderLineId(31923)
order.OrderLines[31923].Amount = 5
OrderRepository.Save(order)
... che non è poi così efficiente o pratico. Potrei aggiungere un requisito per fornire l'ID ordine (effettivamente ridondante), ma ciò comporterebbe un onere inutile sul client.
Quindi diciamo che voglio seguire il mio metodo OrderLineRepository.Save
e trovare ancora un modo per impedire al mio client di essere in grado di aggirare il mio metodo Order.AddOrderLine
.
Tutto ciò a cui riesco a pensare ora è l'eliminazione di OrderLineFactory e la creazione di un servizio di dominio che combina la creazione e l'assegnazione.
class OrderService
AddOrderLine(order, product, amount)
Quindi possiamo:
order = orderRepository.Get(123)
OrderService.AddOrderLine(order: order, product: "Chocolate Cake", amount: 3))
orderRepository.Save(order)
Non mi piace molto il servizio che combina più problemi qui, però, quelli di creazione e assegnazione. Ciò richiederebbe ... una fabbrica ... di nuovo.
Ok, consideriamo una fabbrica privata, una che non è disponibile per il client ma solo per il servizio di dominio. Nel mio caso mi sarei bloccato con le unit test e Inversion Of Control. Creerei una dipendenza innescabile, dal momento che il mio cliente non sarebbe in grado di registrare il mio stabilimento non pubblico.
Riepilogo
- Voglio che il mio cliente sia in grado di creare righe d'ordine attraverso la classe Order e in nessun altro modo
- Non voglio estrapolare più problemi in una singola classe
- Voglio mantenere la creazione OrderLine al di fuori della classe Order
- Mi piacerebbe continuare a utilizzare una fabbrica per questo
Quindi sono un po 'bloccato qui, cercando di trovare il modo migliore per affrontarlo. Qualche suggerimento?