Dipendenze all'interno dell'esagono interno di porte e adattatori

4

In questo articolo Mark Seemann spiega come Onion / Hexagonal Architecture (o Ports and Adapters) sono in qualche modo simili a un'architettura a strati quando viene applicato il principio di inversione di dipendenza (DIP). Soprattutto se consideri le affermazioni fatte in questo articolo a tenere l'acqua, penso che sia tutto abbastanza chiaro e diretto.

Ad ogni modo c'è una citazione su Porte e adattatori che mi ha fatto riflettere sul modo in cui ho strutturato le mie lezioni in passato

The components in the inner hexagon have few or no dependencies on each other, while components in the outer hexagon act as Adapters between the inner components, and the application boundaries: its ports.

Dato che mi piacerebbe implementare alcune logiche di app, chiamate App qui di seguito (non un nome troppo significativo, comunque), che ci consentirebbe di visualizzare un elenco di dipendenti filtrati. La visualizzazione di un elenco di dipendenti sarebbe fornita da una porta

public interface IEmployeeListProvider
{
    EmployeeList GetEmployees();
}

e la persistenza sarebbe un'altra porta

public interface IEmployeeRpository
{
    IEnumerable<Employee> GetAllEmployees();
    void AddEmployee(Employee employeeToAdd);
    void UpdateEmployee(Employee employeeToUpdate);
    // further method signatures
}

Ora implementerei la mia logica aziendale

class App : IEmployeeListProvider
{
    // most likely the filters or filter conditions would be injected.
    // and the IEmployeeRepository anyway

    public EmployeeList GetEmployees()
    {
        var employees = employeeRepository.GetAllEmployees();
        var filteredEmployees = FilterEmployees(employees);
        return EmployeeList.FromEnumerable(filteredEmployees);
    }

    private IEnumerable<Employee> FilterEmployees(IEnumerable<IEmployee> employees)
    {
        // elided
    }
}

Fondamentalmente questo è il modo in cui ho capito le porte e gli adattatori come proposto da Alistair Cockburn . Ad ogni modo, questa implementazione contraddice in qualche modo la citazione di Mark Seeman (vedi sopra), poiché App dipende da bot sulle IEmployeeRepository e sulle porte IEmployeeListProvider . Ovviamente sarebbe possibile ristrutturare il design per utilizzare una porta filtro

public interface IEmployeeFilter
{
    IEnumerable<Employee> FilterEmployees(IEnumerable<Employee> employees);
}

e fai qualcosa di simile dall'interfaccia utente

IEmployeeFilter filter = ...; // however this is constructed
IEmployeeRepository repository = ...; 

// ...

var employees = filter.FilterEmployees(repository.GetAllEmployees());

ma questo mi sembra sbagliato per diversi motivi:

  • L'interfaccia utente dipenderà da una porta DAL
  • Stiamo potenzialmente spostando la logica verso il codice dell'interfaccia utente
  • È molto probabile che l'interfaccia utente diventerà un "maiale dipendente"

Ho sbagliato l'intera citazione di Mark Seeman? C'è qualche altra parte che ho fondamentalmente sbagliato?

    
posta Paul Kertscher 11.01.2018 - 08:42
fonte

2 risposte

4

Non contraddice la dichiarazione. Concettualmente, il tuo oggetto business, App, fornisce "porte" (le due interfacce) in modo da poter collegare altri componenti in cima. Le due interfacce sono possedute dalla classe App. Sì, dipende da quelle interfacce, ma non dipende dai componenti che le implementano o le usano - e questo è esattamente il punto. IEmployeeRepository è una cosiddetta interfaccia richiesta (la classe App richiede che altri componenti implementino questa interfaccia per poter lavorare con la classe App) e IEmployeeListProvider è un'interfaccia fornita (fornito dalla classe App, per altri componenti da utilizzare). Dovresti visualizzare i tre tipi (App, IEmployeeRepository, IEmployeeListProvider) come una singola cosa (e li inseriresti nello stesso pacchetto). Quindi, gli altri componenti che implementano o utilizzano queste interfacce dipendono da essi (e, per estensione, dall'intera terzina).

Questo è ciò che (la prima parte) del principio di inversione di dipendenza afferma: "I moduli di alto livello non dovrebbero dipendere da moduli di basso livello, entrambi dovrebbero dipendere dalle astrazioni". Qui, il tuo modulo di alto livello è la classe App, e uno dei moduli di basso livello sarebbe, ad esempio, il repository. L'astrazione da cui dipendono entrambi è l'interfaccia IEmployeeRepository, ma la cosa fondamentale è che questa interfaccia è di proprietà della classe App, non dall'implementazione del repository. Quindi, per determinare la direzione della dipendenza, si guarda la direzione della freccia tra l'implementazione del repository e la coppia App + IEmployeeRepository.

    
risposta data 11.01.2018 - 10:11
fonte
0

Penso che il tuo punto di vista di Ports & Gli adattatori sono ok Anche questo è mio.

Nel suo articolo, Mark Seemann parla di due esagoni. Non sono d'accordo, penso che ci sia solo un esagono (l'interno). L'esagono interno è l'app e i lati dell'esagono interno sono le porte. Queste porte sono interfacce che appartengono all'esagono interno, al fine di comunicare con il mondo esterno (l'esterno dell'esagono interno).

Ci sono due tipi di porte:

-Diversi: offrono la funzionalità dell'app al mondo esterno (usa il limite del caso)

-Driven: astraggono ciò di cui l'app ha bisogno dal mondo esterno (memorizza / recupera i dati da un archivio dati, invia una notifica, ecc.)

Nel tuo caso, l'interfaccia "IEmployeeListProvider" è una porta del driver. L'implementazione di questo port vive all'interno dell'app (l'esagono interno). Questa implementazione, per svolgere il proprio compito, ha bisogno di accedere al database (mondo esterno), quindi utilizza una porta guidata (l'interfaccia "IEmployeeRpository").

Ci sono anche due tipi di adattatori (al di fuori dell'esagono interno):

-Diversi: accettano la richiesta di un client che desidera utilizzare l'app e chiamano un'interfaccia della porta del driver.

-Driven: implementano una porta guidata per utilizzare una risorsa necessaria all'app.

Quindi, la mia comprensione dell'architettura P & A è simile alla tua, non penso che tu abbia torto.

Non sono d'accordo con l'articolo di Seemann. Considera che i porti sono i confini del "suo" esagono esterno, e gli adattatori collegano quelle porte con l'esagono interno. Per me, le porte sono i confini dell'esagono interno, e gli adattatori collegano le porte con le "cose" (umani, web, database, ecc.) Dal mondo esterno che vogliono usare l'app (driver) o sono usate dall'app (guidata). Facendo un ulteriore passo avanti, Alistair Cockburn ha diviso le "cose" guidate in due tipi: repository e destinatari.

Vedi questo talk intitolato "Alistair in the Hexagone":

link

link

link

    
risposta data 12.02.2018 - 23:00
fonte