Cartella per tipo o Cartella per funzione


59

Uso una guida di stile AngularJS. All'interno di questa guida c'è uno stile chiamato folder-by-feature, invece di folder-by-type, e in realtà sono curioso di sapere qual è l'approccio migliore (in questo esempio per Java)

Diciamo che ho un'applicazione in cui posso recuperare Users & Pets, usando servizi, controller, repository e oggetti di dominio ofcourse.

Prendendo gli stili delle cartelle per -....., abbiamo due opzioni per la nostra struttura di packaging:

1. Cartella per tipo

com.example
├── domain
│    ├── User.java
│    └── Pet.java
├── controllers
│    ├── UserController.java
│    └── PetController.java
├── repositories
│    ├── UserRepository.java
│    └── PetRepository.java
├── services
│    ├── UserService.java
│    └── PetService.java
│   // and everything else in the project
└── MyApplication.java

2. Cartella per funzionalità

com.example
├── pet
│    ├── Pet.java
│    ├── PetController.java
│    ├── PetRepository.java
│    └── PetService.java
├── user
│    ├── User.java
│    ├── UserController.java
│    ├── UserRepository.java
│    └── UserService.java
│   // and everything else in the project
└── MyApplication.java

Quale sarebbe un buon approccio e quali sono gli argomenti per farlo?



1
Non conosco @Laiv. La lingua / il quadro influiscono davvero sulla risposta? Indipendentemente da ciò, l'altra domanda è sicuramente rilevante.
RubberDuck,

1
Quindi devo modificare la mia risposta. E ancora, è un possibile duplicato
Laiv,

2
Non ho mai capito i vantaggi della cartella per tipo. Se sto lavorando su un ticket relativo alla funzione Pet, probabilmente trarrebbe beneficio dalla località Pet, dal controller, dal repository e dal servizio. In quale situazione avrei mai bisogno di tutti i controller, ma non delle visualizzazioni, dei repository o dei servizi?
Alexander,

2
Nessuno sembra aver menzionato che i pacchetti in Java non sono solo cartelle; influenzano anche l'accesso per le classi contenute. Per questo motivo, pacchetto per livello / tipo può effettivamente fornire un valore semantico in Java.
Angus Goldsmith,

Risposte:


69

Cartella per tipo funziona solo su progetti su piccola scala. Cartella per funzionalità è superiore nella maggior parte dei casi.

Cartella per tipo è ok quando hai solo un piccolo numero di file (diciamo meno di 10 per tipo). Non appena ottieni più componenti nel tuo progetto, tutti con più file dello stesso tipo, diventa molto difficile trovare il file effettivo che stai cercando.

Pertanto, la cartella per funzionalità è migliore grazie alla sua scalabilità. Tuttavia, se si esegue la cartella per funzionalità, si finisce per perdere informazioni sul tipo di componente rappresentato da un file (perché non si trova più in una controllercartella), quindi anche questo diventa confuso. Ci sono 2 soluzioni semplici per questo.

Innanzitutto, è possibile attenersi alle convenzioni di denominazione comuni che implicano tipicità nel nome del file. Ad esempio la popolare guida di stile AngularJS di John Papa ha i seguenti:

Linee guida per la denominazione

  • Utilizzare nomi coerenti per tutti i componenti seguendo uno schema che descriva la funzione del componente e (facoltativamente) il suo tipo. Il mio
    modello raccomandato è feature.type.js. Ci sono 2 nomi per la maggior parte delle
    risorse:

    • il nome del file (avengers.controller.js)
    • il nome del componente registrato con Angular (AvengersController)

In secondo luogo, è possibile combinare gli stili cartella per tipo e cartella per caratteristica in cartella per caratteristica per tipo:

com.example
├── pet
|   ├── Controllers
│   |   ├── PetController1.java
|   |   └── PetController2.java
|   └── Services
│       ├── PetService1.java
│       └── PetService2.java
├── user
|   ├── Controllers
│   |   ├── UserController1.java
│   |   └── UserController2.java
|   └── Services
│       ├── UserService1.java
│       └── UserService2.java

1
La guida allo stile johnpapa era esattamente quella di cui mi stavo fingendo :-)
Jelle il

1
Alcune volte (più spesso di quanto pensiamo) aggiungiamo troppa complessità a cose che avrebbero dovuto essere facili. La denominazione e il packaging sono alcune di queste cose. Il miglior consiglio è di mantenerlo semplice. La miscelazione di entrambi presenta sia i vantaggi che gli svantaggi di entrambi gli approcci.
Laiv,

1
@Laiv Nell'esempio che ho fornito, sono d'accordo sul fatto che sia eccessivo, ma nella maggior parte dei miei casi aziendali reali in cui ogni cartella del tipo può contenere facilmente 10-20 file, ritengo sia molto utile perché la cartella generale delle funzioni avrebbe un ordine di 50 file altrimenti.
Ripristina Monica il

6
falso, grazie iPhone correzione automatica! Intendevo "parlare".
Jelle,

2
@Chaotic Questo esempio è troppo banale per iniziare a parlare di dettagli, ma in quei casi in cui ci sono pezzi che vengono usati su più componenti, probabilmente dovrebbe essere un componente a sé stante da cui dipendono altri componenti.
Ripristina Monica il

26

Questo non ha davvero nulla a che fare con la tecnologia in questione, a meno che non si utilizzi un framework che impone cartelle per tipo su di te come parte di un approccio di convenzione sulla configurazione eccessiva.

Personalmente, sono fermamente convinto che la cartella per funzione sia di gran lunga superiore e dovrebbe essere usata il più possibile ovunque. Raggruppa le classi che funzionano effettivamente insieme, mentre la cartella per tipo duplica solo qualcosa che di solito è già presente nel nome della classe.


10
Vorrei aggiungere che in cartella per funzionalità rende più facile giocare con classi e metodi ambiti (protetto, pacchetto, ...).
Laiv,

Per i progetti più piccoli potresti avere solo una funzione. È molto più facile organizzare per tipo in quello scenario.
aaaaaa,

Ad esempio, Grails impone la cartella per tipo per i tuoi domini, controller, servizi, ecc.
tylerwal,

17

Lavorare con i pacchetti per funzionalità si distingue per l' elevata modularità e coesione . Ci permette di giocare con l'ambito dei componenti. Ad esempio, possiamo utilizzare i modificatori di accesso per applicare LoD e l' inversione di dipendenza per integrazioni o / ed estensioni.

Altre ragioni sono:

  • Navigazione del codice più semplice
  • Un livello più alto di astrazione
  • Riduci al minimo gli ambiti (contesti di delimitazione)
  • Modularizzazione verticale

Cartella per strato pone troppa enfasi sui dettagli di implementazione , (come menzionato da @David) ciò che non dice troppo sull'applicazione a cui stiamo lavorando. A differenza del pacchetto per funzionalità , pacchetto per strato incoraggia la modularizzazione orizzontale. Questo tipo di modularizzazione rende duro e noioso lavorare con componenti trasversali.

Infine, c'è una terza opzione. " Pacchetto per componente " che, in parole di zio Bob, sembra allineato meglio ai suoi principi di pacchetto . Se l'opinione di zio Bob è importante o no, lascio a te decidere. Lo trovo interessante poiché questa convenzione è in linea con la sua architettura pulita, che mi piace.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.