Disaccoppiamento, inversione delle dipendenze e architetture esagonali

September 20 2008

questa settimana è arrivato un nuovo tassello nel mosaico dell’applicazione Java che il mio team sta sviluppando in questo periodo. è ormai in code complete la user-story che ho preso in carico, che si occupa di realizzare l’aggancio ad una coda, realizzata da sviluppatori di un altro team interno, e di supportare il formato dei dati concordato con loro. dato che per me rappresentava la possibilità di fare esperienza con una tecnologia che conosco ancora poco, lo standard Java Message Service (JMS), mi sono subito proposto, a inizio iterazione, per prendere io la carta.

la nostra parte di applicazione è particolare rispetto a quanto stanno sviluppando gli altri team sul progetto, dato che si tratta di un motore di regole per elaborare dati provenienti da diverse sorgenti, e notificare via posta elettronica agli utenti gli eventi che corrispondo ai criteri da loro scelti. è un dominio molto ricco, in cui la parte di infrastruttura è secondaria.

il porre in secondo piano l’aspetto di infrastruttura è stata, oltre che una scelta, un’esigenza dettata da requisiti non funzionali ancora in parte incompleti: quali sorgenti dati usare, quali standard usare, o in alcuni casi anche “chi” realizzerà determinati sistemi esterni. per i servizi offerti invece dagli altri team interni, volevamo renderci quanto più indipendenti dalla loro pianificazione, riducendo cioè al minimo i vincoli temporali inter-team.

queste condizioni hanno quindi favorito la nascita, in modo incrementale, di un’architettura in cui al centro si pone un Domain Model indipendente, slegato quanto più possibile dagli aspetti di infrastruttura: ogni volta che una storia richiedeva l’accesso ad un servizio (anche se non ancora definito) aggiungevamo nel dominio un’interfaccia per disaccoppiare i due sistemi – un provider, broker o sender – nell’ottica di realizzare in seguito un adattatore al servizio, quando questo fosse stato pronto. si tratta dell’architettura esagonale descritta da Cockburn.

per poter disaccoppiare da tutto il cuore del sistema, ad un certo punto abbiamo trovato naturale applicare il principio di inversione delle dipendenze (Dependency-Inversion Principle), in modo che la logica di alto livello (domain) fosse slegata dai dettagli di implementazione (adapters e infrastructure) e che questi dettagli dipendessero invece da un’astrazione (le interfacce, dichiarate all’interno del domain). rispetto ad uno schema di layering canonico, in cui il livello applicativo dipende da quello di infrastruttura, abbiamo ottenuto una struttura in cui il layer applicativo non dipende da nulla, ma sono invece UI e infrastruttura a dipendere dallo strato applicativo.

la forza che ha guidato l’emergere di questa struttura è stata, nuovamente, la necessità di rendere il sistema testabile in isolamento. la realizzazione di ciascuna storia è stata così guidata da una ricca suite di test:

  • test unitari delle classi di dominio e del motore di regole, usando test-doubles (fake, stub o mock) delle interfacce dei servizi
  • test unitari degli adattatori (quando possibile), usando test-doubles delle API esterne
  • test di integrazione degli adattatori, usando istanze in memoria dei servizi
  • test di accettazione, usando adattatori e servizi in memoria

[ sulla terminologia usata – dummy, fake, stub e mock – c’è ancora molta confusione in letteratura, per questo uso il termine più generico di test-double. io in genere faccio riferimento al lavoro di riorganizzazzione di Meszaros. ad esempio, un fake è una implementazione alternativa di una interfaccia (ma dal comportamento consistente), usata solo a scopi di test, come il classico Gateway al database realizzato con una hashmap in memoria. uno stub invece è un oggetto programmabile, che altrimenti implementa un comportamento di default. un mock, infine, realizza l’endo-testing o testing delle interazioni, con cui verificare le interazioni tra oggetti. ]

per i test di integrazione ci siamo affidati a implementazioni leggere dei vari servizi, realizzate in memoria, avviabili programmaticamente in fase di setUp e disattivati al termine della suite durante il tearDown. finora, siamo riusciti a collezionare un buon numero di tool open-source, ad esempio:

  • come web-container, l’ultra leggero e versatile Jetty, abbandonando il ServletContainer di HttpUnit, ma solo dopo esserci scontrati con alcuni suoi limiti (primo tra tutti, il content-type a text/plain)
  • come server SMTP, abbiamo scelto Dumbster, in ascolto sulla canonica porta 25 locale
  • come broker JMS, si è dimostrato davvero ottimo ActiveMQ, che offre code in memoria non persistenti e visibili da tutta la virtual machine (url del tipo vm://localhost), oppure accessibili anche da client esterni (url del tipo tcp://localhost)
  • come dataSource, non avendo ancora a disposizione un sistema di deploy automatico di schemi e stored-procedure (ma ci stiamo lavorando!), abbiamo scelto di usare direttamente l’ambiente di test condiviso, affidandoci però al supporto di transazionalità nei test offerto da Spring

il punto forza di questo approcio, a cui si aggiungono Dependency-Injection (ottenuta come effetto collaterale del TDD) e uso degli standard (come JMS, servlet-container, JDBC e STMP), ci ha inoltre permesso di ottenere un ulteriore enorme risultato: realizzare sulle macchine di sviluppo delle demo al cliente andando ad agganciarci alle istanze reali dei servizi. scrivendo solo una diversa configurazione – breve e localizzata – grazie al supporto di IoC fornita da Spring abbiamo potuto usare un vero application server, un vero broker JMS, un vero server SMTP e un vero database.

la nostra particolare situazione (requisiti ancora parziali sull’integrazione ai servizi esterni) e, non lo nego, una grossa flessibilità da parte dei customer interni, ci hanno permesso di consegnare e farci approvare tutte le user-stories sviluppato fino ad ora, nonostante manchino ancora intere parti di integrazione (DAO esterni, alcuni servizi SOAP e l’infrastruttura di Single Sign-On).

per questo, oltre che estremamente interessante, divertente e pieno di sfide, non posso che considerare finora questo progetto un successo, sia per il cliente che si è visto un po’ alla volta consegnare pezzettini di funzionalità, sia per il team, che ha dato alla luce una architettura flessibile, in modo del tutto incrementale, usando come ferri del mestiere testabilità, disaccopiamento e semplicità.

Advertisements

5 Responses to “Disaccoppiamento, inversione delle dipendenze e architetture esagonali”


  1. […] interagisce correttamente con *un* sottosistema esterno. Se sviluppi secondo l’architettura esagonale, il test di accettazione verifica l’implementazione corretta di *un* lato […]


  2. […] implementation details, then adapt code when things get clearer. well, that’s the hexagonal architecture (but, you know, we like coining sexy names). so, i spent the whole first week coding the […]

  3. Claudio Fogazza Says:

    piattaforme esagonali in mare
    formano insieme gli elementi di una città
    circondati da cubi con vuoto interno circolare
    di dimensioni diverse per smorzare le onde
    tale atollo forma all’interno una laguna
    dove la città galleggiante si può espandere
    unendo i vari moduli esagonali al riparo delle onde


  4. […] Disaccoppiamento, inversione delle dipendenze e architetture esagonali « Software Engineering … – […]


  5. […] ai suoi collaboratori di eseguire qualcosa, ignorando i dettagli implementativi; infine l’architettura esagonale, ossia fare in modo che il dominio non abbia alcuna conoscenza del mondo esterno: difatti le classi […]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: