Come meglio incapsulare le funzionalità nella progettazione del software?

2

Mi piacciono il design guidato da domini e il design a cipolla. Comunque mi piacerebbe davvero che le mie caratteristiche fossero incapsulate. Se controllo qualche codice legacy e ho bisogno di investigare su alcune caratteristiche specifiche, la cosa migliore che penso possa accadermi è che vedo (diciamo in java) che ci sarebbe un pacchetto con featurename e tutte le funzionalità la funzionalità risiedeva in quel pacchetto. In modo che se elimini il pacchetto la funzionalità sarebbe andata.

Pur comprendendo che è impossibile perché alcune delle funzionalità potrebbero risiedere in altri servizi, sto tentando almeno di pensare se è corretto farlo nel mio servizio.

Il mio problema con questo è che di solito in DDD e in onion design hai il dominio che contiene un gruppo di oggetti di dominio. ora alcuni di questi oggetti sono di "mia funzionalità" e alcuni di essi sono di altre funzionalità. diciamo (l'autenticazione dell'utente è una funzione e i dettagli dell'account Twitter dell'utente sono un'altra caratteristica).

Esiste qualche metodologia di progettazione che faccia qualcosa con esso? è quello che sto suggerendo ha senso?

Esempio: hai un progetto che gestisce gli utenti in attesa in linea. Ti viene chiesto di aggiungere un limite, massimo sulla dimensione della coda. è necessario leggere la dimensione massima della coda dalla configurazione. Quindi nella tua logica è necessario applicare la dimensione massima della coda. È inoltre necessario esporre questa configurazione della dimensione massima della coda in jmx, inoltre è disponibile una API per quel servizio, quindi è necessario esporre la dimensione massima della coda come API in quel servizio.

Pacchetti di app correnti: --> conf (deals with reading configuration) // do change here. --> domain // do change here for that small subfeature. --> logic // do change also here for that small subfeature --> service (where api are) // do also a change here.

quindi nell'esempio semplicistico di cui sopra per una semplice aggiunta subfeature ho bisogno di apportare modifiche ad almeno 4 pacchetti. Ciò significa che la mia sottofunzione è diffusa tra questi pacchetti e classi.

Capisco DDD menzioni boundcontext ma nota che la mia funzione qui è molto piccola! aggiungendo solo una limitazione massima alle dimensioni. Quindi non sembra appropriato aprire un boundedcontext su ciascuna di queste piccole sottofunzioni.

Quindi, il modo in cui lo gestisco oggi vado a trovare una classe chiamata Configuration che contiene varie configurazioni. Aggiungo a quel Configuration class un membro chiamato Configuration.maxQueueSize .

Vado quindi al dominio per modellare quella nuova proprietà che ho lì diciamo Account aggiungo alla Account class Account.maxQueueSize .

Vado quindi al servizio di esporre un nuovo metodo denominato getMaxQueueSize

come vedi sono passato a più pacchetti e più classes ha già a che fare con altre funzionalità e le ho estese.

Questa è una caratteristica molto piccola, per una sola cosa non riesco a immaginare che per ogni piccola feticista aprirei un nuovo contesto limitato con i suoi pacchetti service, domain, conf (e ne deduco che ce ne siano altri) in modo che ogni funzionalità sia in il proprio pacchetto.

D'altra parte su ogni funzione ho bisogno di estendere configuration, domain, service, ... il che significa che nessuna singola funzione è incapsulata abbastanza bene in modo che io possa semplicemente cancellarla o vedere un pacchetto in cui risiede completamente in modo da poter capire meglio l'app .

    
posta Jas 21.12.2015 - 07:23
fonte

3 risposte

1

Se ho capito bene, i tuoi 4 pacchetti ( conf , domain , logic e service ) sono strati nell'applicazione. Questi livelli non separano le diverse "funzionalità" come le chiamate, ma preoccupazioni diverse. Pertanto, è perfettamente necessario toccare tutti e 4 questi pacchetti quando si aggiunge una nuova funzione per quanto piccola possa essere.

Se dovessi aggiungere una nuova funzionalità che non è correlata alla tua funzionalità di "gestione della coda", potresti aggiungere un nuovo pacchetto di funzionalità che contiene tutti e 4 i tuoi livelli, ma la "dimensione massima della coda" dovrebbe appartenere al tuo caratteristica attuale, direi.

    
risposta data 13.02.2016 - 19:21
fonte
5

Quello che stai cercando si chiama Contesto limitato in DDD.

Ora, mentre stai realizzando la tua domanda, i contesti limitati potrebbero condividere i dati. Il modo migliore per implementarlo che ho visto è semplicemente quello di duplicare le entità. Quindi Accounting context ha Customer , e quindi Production ha Customer . Ma anche se hanno lo stesso nome, hanno un significato completamente diverso. Diventa quindi responsabile dello strato sopra (molto probabilmente il livello dell'infrastruttura) per salvarlo e caricarlo nella stessa tabella. Ma dal punto di vista individuale, i clienti sono completamente separati. Ciò consente quindi di rimuovere completamente qualsiasi contesto limitato dall'intera applicazione senza influire su altre parti.

Per quanto riguarda l'architettura di cipolla, progetterei ogni "modulo funzione" come una "fetta" attraverso la cipolla in cui ogni livello è una singola libreria. Immagina di ritagliare un pezzo di torta rotonda. Quindi potresti avere le librerie Feature1.Domain, Feature1.Database e Feature2.Domain, Feature2.Database, ecc. Tra le quali ci sarebbero le relazioni secondo l'architettura della cipolla.

Ma c'è un problema. Come forse saprai, alcuni moduli potrebbero voler comunicare tra loro. Ecco dove arrivano le librerie Bridging. Detto semplicemente, potresti avere una libreria Feature1Feature2Bridge. Questa libreria si baserebbe su Feature1.Domain e Feature2.Domain fornendo astrazioni per la comunicazione e questa libreria "Bridge" implementerebbe quella comunicazione. Quindi, questa libreria verrà caricata solo se vengono caricate entrambe le funzioni.

    
risposta data 21.12.2015 - 08:27
fonte
0

Quando scopro che tutte le modifiche che mi vengono richieste richiedono modifiche minime a diversi moduli, ciò significa in genere che il modo in cui ho diviso il progetto in moduli è inappropriato.

Per fare un esempio che credo sia simile al tuo, uno dei servizi su cui lavoro converte i documenti da un vecchio formato a un nuovo formato. Ogni volta che una nuova funzione viene supportata nel nuovo formato, devo tornare in questo servizio di conversione e:

  • Aggiungi i parametri che rappresentano questa funzione agli oggetti del modello per vecchi e nuovi documenti.
  • Aggiorna la logica per recuperare il vecchio documento dal vecchio database, in modo da ottenere la versione precedente della funzione.
  • Aggiungi logica per convertire la versione precedente della funzione nella nuova versione.
  • Aggiorna un file di configurazione per dire che il servizio ora utilizza la versione del modello 1.11 invece della versione 1.10.

Nel mio caso, tutto questo è un singolo commit su un singolo repository distribuito come un singolo pacchetto , semplicemente perché è così che abbiamo scelto di suddividere i nostri pacchetti. Sì, sono ancora (approssimativamente) quattro file separati, ma non credo che questo sia un problema finché tale insieme di modifiche può essere convenientemente impegnato, rivisto, implementato, ecc. Come una singola unità coesiva (che può essere se si ha un decente strumenti).

Ad esempio, sembra che gli oggetti del modello siano in un pacchetto separato dalla logica che opera su di essi, il che probabilmente non è necessario a meno che tali oggetti non debbano essere condivisi tra molti altri servizi. Questo è ancora più vero per il tuo file di configurazione, poiché non posso immaginare che venga riutilizzato da altri servizi (a meno che la configurazione non sia eccessivamente complicata). È anche difficile pensare a uno scenario in cui l'avere l'API di un servizio e la sua logica in due pacchetti separati sarebbe una buona idea. Posso pensare ad alcuni prodotti derivati come la documentazione dell'API che forse appartengono a pacchetti separati (sebbene idealmente sarebbero autogenerati come parte del processo di build / deploy), ma l'effettiva API / logica sembra che dovrebbero normalmente andare insieme.

    
risposta data 21.12.2015 - 15:52
fonte

Leggi altre domande sui tag