Separazione della logica dell'applicazione e della logica di dominio in Architettura pulita

1

Sto lottando con la separazione della logica tra entità e interattori o Use Case. Se progetto le entità con i principi DDD, ciascuna entità avrebbe metodi corrispondenti a casi d'uso, invece di setter e getter. In tal caso, farei caso, avrei approssimativamente una mappatura uno-a-uno delle classi di interactor e dei metodi di entità (magari con alcuni interattori che si estendono su più entità e orchestrando scenari più complessi).

Ad esempio, potrei avere la seguente classe di entità:

Sale (entity)
+createSale()
+ammendSale()
+cancelSale() 
+shipSale()
+collectSale()

E le seguenti classi di comando:

CreateSaleCommand
AmmendSaleCommand
CancelSaleCommand
ShipSaleCommand  # (this command may interact with the inventory service in a microservices context, or with the ProductStock entity in a monolithic context)
CollectSaleCommand # (this command may interact with payment and accounting services, or with the corresponding entities)

Cosa ne pensi di questo approccio? Sento che potrebbe portare a una moltiplicazione di artefatti senza troppi benefici, con la maggior parte dei comandi che sono classi anemiche che trasmettono semplicemente richieste alle entità e restituiscono risposte. Sebbene si occupino di incapsulare la logica per accedere agli archivi e ai servizi esterni, consentendo alle entità di concentrarsi esclusivamente sulla logica di dominio (i loro metodi rappresentano azioni ed eventi aziendali rilevanti e i loro dati privati che rappresentano concetti e categorie di business).

    
posta FedericoG 12.06.2018 - 20:06
fonte

4 risposte

3

Non hai bisogno di una mappatura uno-a-uno di interattori ed entità. Credo che un tale design sarebbe dannoso.

Il DDD riguarda esclusivamente i limiti di contesto e il linguaggio ubiquo all'interno di quei confini. Quando ti concentri davvero sulla creazione di oggetti per rappresentare la lingua del business, scoprirai che tutto inizia a modellarsi in modo un po 'diverso.

Mi sembra strano che una vendita possa creare o spedire se stessa. Nel mondo reale, potrei immaginare un venditore che fa una vendita. Quindi, un reparto spedizioni può gestire la logistica per spedirlo. Forse, una vendita sarebbe creata tramite un carrello della spesa on-line e spedita via e-mail. Usando questo linguaggio come esempio, esploriamo un possibile design alternativo.

SalesPerson
+createSale(Customer, Product, Price)
+amendSale(Sale, Amendment)

ShippingDepartment
+shipSale(Sale)

Aspetta un secondo !? Di solito non spediamo ordini? Forse dovrebbe andare così:

SalesPerson
+createOrder(Customer, Product, Price)
+amendOrder(Order, Amendment)

ShippingDepartment
+shipOrder(Order)

Order
+constructor(Customer, Product, Price)
+applyDiscount(Discount)
+updateShippingAddress(Address)

Entrambi si stanno avvicinando, ma in realtà il nostro team di vendita non li chiama ordini e il nostro reparto spedizioni li chiama solo ordini. Inoltre, gli ordini vengono sempre creati dall'esistenza di una vendita, quindi dovremmo riflettere anche questo.

SalesPerson
+createSale(Customer, Product, Price)
+amendSale(Sale, Amendment)

Sale
+constructor(Customer, Product, Price)
+applyDiscount(Discount)
+updateShippingAddress(Address)

ShippingDepartment
+shipOrder(Order)

Order
+constructor(Sale)
+placeOnHold()
+shipped(TrackingNumber)

E così, continua a svilupparsi.

Il modo in cui gestisco la separazione della logica è provare a visualizzare quell'oggetto nella vita reale. Quindi, pensa alle proprietà e alle azioni che sono rilevanti per il business-need / use-case / feature.

Modifica: come interagisci con i metodi su un'entità in DDD?

Invece di usare il pattern Command o altri interattori, puoi semplicemente usarli direttamente:

salesPerson = new SalesPerson()
sale = salesPerson.createSale(...)

... 

shippingDept.shipOrder(new Order(sale))

Ricorda che i due non si escludono a vicenda. Puoi ancora utilizzare un pattern Command o Use Case / interactors se ce n'è bisogno. Potresti avere qualcosa di simile a questo (1 interactor per molte entità e metodi):

PartyUseCase(ShippingDepartment)
+prepareParty(SalesPerson, Customer)
    plates = ProductRepo.findByName('plates')
    forks = ProductRepo.findByName('forks')
    platesSale = salesPerson.createSale(Customer, plates)
    forksSale = salesPerson.createSale(Customer, forks)

    ShippingDepartment.shipOrder(new Order(platesSale))
    ShippingDepartment.shipOrder(new Order(forksSale))

    ...

In alternativa, potrebbe esserci uno script semplice che viene eseguito una volta al giorno con nient'altro che questo (0interpreti):

orderRepo = new OrderRepository()
shippingDept = new ShippingDepartment()
for each order in orderRepo.getOrdersToShip()
    shippingDept.shipOrder(order)

In definitiva, se stai creando una mappatura uno-a-uno delle classi ai metodi, stai creando una complessità non necessaria.

    
risposta data 13.06.2018 - 02:06
fonte
2

La prima cosa a cui ho pensato quando ho visto la tua architettura di esempio è "Può una vendita creare se stessa? Può una vendita cancellarsi?" Ancora più importante, una vendita può imbarcarsi o riscuotere i propri soldi? "

Le parole "Anemic data model" sono considerate solo parolacce perché qualcuno da qualche parte ha deciso che "il vero orientamento all'oggetto sposa sempre insieme i dati e la logica". Ma gli oggetti di trasferimento dati sono sempre utilizzati per passare i dati oltre i limiti e rappresentano la definizione stessa di un oggetto anemico.

Fondamentalmente, "architettura pulita" è solo un altro aroma delle architetture con livelli. Il principio che regola in tutte le architetture a livelli è che le dipendenze dovrebbero fluire solo in una direzione. Ad esempio, il Business Logic Layer (BLL) deve conoscere il Data Access Layer (DAL), ma il DAL dovrebbe sapere niente sulla BLL. Questa disposizione ti consente, ad esempio, di allegare diversi BLL (ad esempio uno per ogni reparto della società) al DAL senza dover modificare il DAL stesso.

Concetti di design come questo sono difficili da discutere da soli, quindi rendiamolo più concreto.

Questoèundiagrammaarchitettonicodiun'applicazioneditradingdiesempiochedimostralemiglioripratichenellosviluppodelsoftwarechiamato ArchFirst . Si noti che indica due contesti limitati: Order Management System (OMS) e Exchange.

Ora dai un'occhiata a questo diagramma:

Tieni presente che i metodi su un ordine dipendono dal contesto in cui viene utilizzato.

Inoltre, non sono necessariamente contrario a quei metodi che sono da qualche altra parte oltre all'oggetto dell'ordine, forse in un oggetto OrderManager .

    
risposta data 13.06.2018 - 16:53
fonte
1

Non mi sembra giusto.

Ci sono almeno due principi che sono d'accordo con me. Il primo è: Keep-It-Simple . Perché introdurre uno strato puramente tecnico se non vi è alcun vantaggio? Penso che il tuo istinto sia giusto, è solo una duplicazione di cose che possono o meno risolvere qualche problema futuro. L'approccio Keep-It-Simple ci dice, se non risolve un problema ora , sbarazzati di esso.

Il secondo è solo concentrarsi sul dominio aziendale. Il problema che ho con "Comandi" è che sono puramente tecnici. In altre parole, non appartengono all'azienda, alla lingua ubiquitaria o al dominio. È cruft.

Inoltre, se ti riferisci direttamente all'architettura pulita di Uncle Bob, ecco un dell'architettura pulita da un punto di vista orientato agli oggetti , in cui descrivo in dettaglio perché queste idee sono fondamentalmente incompatibili con l'orientamento all'oggetto .

    
risposta data 13.06.2018 - 10:27
fonte
0

Interact (suppongo che ciò che intendi per i comandi ) siano simili ai servizi applicativi nell'architettura DDD / cipolla. Forniscono un ambiente di hosting per il caso d'uso, o transazione aziendale, da eseguire, e orchestrano le chiamate al dominio e ai componenti esterni.

Ad esempio, puoi avere la seguente sequenza

Start unit of work
Get entity from repository
Call entity method
Send email notification
Commit unit of work

Questo non è affatto anemico o banale come "chiamare il metodo dell'entità e restituire la risposta". Può essere vista come una sua responsabilità - devi mettere questa logica da qualche parte e se la metti in un Presenter, potrebbe diventare gonfia e incoerente.

    
risposta data 13.06.2018 - 13:59
fonte