Wednesday, October 21, 2009 

The Dependency Structure Matrix

Design is about making decisions; diagrams encode some of those decisions. Consider this simple component diagram:



We have 3 "physical" components (e.g. DLLs) X, C, D. X is further partitioned in 2 logical components: in this real-world case, the designer used namespaces to identify separate logical components inside a single physical component. The designers is also telling us that A and B depends on D, B depends on C, C depends on D. So far, so good.

UML diagrams, however, cannot easily convey some part of the reasoning. In a sense, to fully grasp the designer's intention, we have to understand not only what is in the diagram, but also what is not in the diagram. This may seem unusual, but is easily explained. Consider the picture above again. There is no dependency between A and C. Now, maybe A doesn't currently need to access C (and therefore there is no dependency) but if we need to access C from A tomorrow, it's just fine to add a dependency. Or maybe the designer's intent was to shield A from C, possibly using B as a man-in-the-middle.
That's not obvious from the diagram, and there is no place in the diagram to say that (not with a formal, standard UML syntax). Of course, good names may help. Replacing B with something more meaningful, maybe mentioning a bridge or proxy pattern, may suggest that A is not supposed to interact with C.

Is there a better way? Maybe something that can be actually checked against code? Checking code compliance with diagrams may seem so passe' or even plain absurd, given the current trend of discarding diagrams and/or reverse-engineering diagrams from code. Still, here is a real-world story:
The design above (which is, of course, largely simplified) was handed out from the original designers-implementers to a larger (offshore) team. They explained some of the design rationale (informally), and after a while, they left the company. Months later, the offshore team needed a new service from C inside A, so they did the simplest thing that can possibly work: they called C from A. After all, A and B are inside the same physical component. Whatever B can do, A can do too.
Unfortunately, a cornerstone of the original design was that A should never talk to C. The dependency was not in the diagram, because it was not supposed to exist, ever.

The team manager knew that, but given the size of the real X (about 500 KLOC) she couldn't possibly review all the changes from the offshore team. Of course, at least someone in the offshore team didn't fully grasp the designer's intent.

So, back to the original question: is there a better way? I could say "a forcefield diagram" :-), but in this specific case, there is also a well-known engineering tool: the Dependency Structure Matrix (also known as the Design Structure Matrix). A DSM encodes dependencies between "things". Not just dependencies, but also forbidden dependencies. See the following picture:



The 5 green "Y" cells correspond to the 5 existing dependencies; the "N" cells correspond to the "missing" dependencies, but they say something more: that those dependencies are forbidden. Now, this is a useful piece of information, something that can be easily checked against code. That does not mean that we can't change the design: it simply means we don't want to change the design inadvertently, just by typing in some code that was not supposed to be there. Checking code against the abstract design should just prompt a review; the design could be wrong, in which case, it should be changed (along with the DSM).

There is some interesting literature about DSM in software, most from Baldwin and Clark of "Design Rules" fame, but also from others (like one I mentioned back in 2005). There are also quite a few tools to reverse-engineer a DSM from code, which makes checking code against the designed DSM relatively trivial (the bad side is that some languages, like C++, are notably hard to reverse engineer, so tools are lacking; Java and C# have both free and commercial tools available). I'm not aware of any UML tool that can generate a DSM from the diagrams, but that's theoretically trivial, and could even be built as a plug-in for some CASE tools.

As usual, there is more to say about the DSM, gravity, and the forcefield. I'll save that for my next post!

Labels: ,

Comments:
Ciao Carlo, interessanti considerazioni. Tra l'altro, questa tecnica basata sulla matrice di dipendenze funziona bene anche nel caso in cui il designer usi (per qualche ragione) l'elisione di dettagli nei diagrammi. L'elisione può creare problemi perché quello che non vedi non necessariamente significa che non sia già presente, ma può essere stato semplicemente omesso per rendere il diagramma più semplice (focalizzandolo solo sulle dipendenze essenziali in base alla mission corrente). E' ovvio, almeno secondo me, che per sfruttare sistematicamente questi strumenti serve il supporto dei CASE tool. Non mi sorprende però che attualmente nessun CASE tool professionale incorpori cose del genere: vanno molto più di moda il reverse engineering (post-mortem documentation?) e il rapid prototyping, quasi come se il design fosse un'attività totalmente automatizzabile.
 
Andrea: sicuramente la DSM va benissimo proprio nei casi di elisione dei dettagli. In realta' la mia attuale visione e' di estendere la normale DSM binaria ad una ternaria, per rappresentare i 3 tipi di relazione intesi dal progettista:
yes-now
not-now (but acceptable)
never
Sicuramente avere tutto questo integrato in un CASE tool farebbe comodo, ma io mi accontenterei anche di uno strumento standalone, purche' di uso rapido e non farraginoso, che non costringa a fare 200 click per creare una tabellina. Finira' che me lo scrivero' da solo :-).
Sul tema del design, che dire, sarebbe un discorso lungo... vediamo le conseguenze di due estremismi. Da un lato un certo modo di vedere l'agilita', con forte critica ad ogni attivita' di design "preventivo", dall'altra la pretesa di alcuni fautori dell'approccio MDA di "eliminare la codifica".
Non aiuta il fatto che il design sia una disciplina difficile da comprendere a fondo, mentre e' facile "insaccarsi" in minimi locali dove ci si limita ad applicare piu' o meno ciecamente alcuni precetti ("single responsibility", "dry", ecc).
Che dire, migliorera' :-)
PS: ma sei un Andrea che conosco in real life?
 
Carlo, si sono un Andrea che conosci in real-life (Andrea Baruzzo). Scusa ma ho scritto di fretta e, prima che mi accorgessi, avevo già spedito il messaggio senza neanche firmarmi :(. Andrea.mdl è un piccolo vezzo che mi sono concesso sin dai tempi in cui Rose era praticamente l'unico editor UML (che anno era? il 1996? mahh... un po' di capelli bianchi in più mi dicono che il tempo passa anche troppo in fretta, sigh!)

Mi chiedo una cosa: con quale criterio scrivi questa matrice? Immaginiamo per ora un'integrazione su un CASE tool mediante add-on. Chiaramente tenere "sotto controllo" tutte le classi e le relative dipendenze del sistema diventa poco pratico. Ti andrebbe bene poter editare una simile matrice per ogni package, assumendo che tale matrice rappresenti il merging di tutti gli eventuali diagrammi di classe in quel package?
Oppure preferiresti lavorare al livello del singolo diagramma di classe?
Non è neanche difficile predisporre un meccanismo di default che marca come "not-now" tutte le combinazioni di classi prive di dipendenze all'interno del package/diagramma corrente...
Avevi in mente una granularità diversa? Una soluzione più ad hoc?

Curiosamente, mi sono imbattuto sul libro di Baldwin & Clark qualche anno fa, seguendo le dissertazioni filosofiche (in ottica Ph.D) di un mio caro amico ingegnere, a proposito di architetture modulari (Design Rules è un testo caposcuola sulla modularità, anche se non è specifico del software). All'epoca pensavamo di usare una matrice simile a quella da te proposta, non tanto per ragionare sulle dipendenze, quando sulle interfacce richieste da un componente al fine di estenderlo con altri componenti e creare il design di uno specifico prodotto (anche in modo collaborativo), ad esempio una barca. L'idea era quella di introdurre meccanismi di progettazione "corretti per costruzione". Poi la sua tesi ha preso una piega un po' diversa e tutto è rimasto un po' in astratto.

Ciao,
Andrea
 
Ciao Andrea, mi pareva di riconoscere lo stile : ).

Principalmente lo userei per il diagramma dei componenti. Pero' sicuramente capiterebbe l'occasione di utilizzarlo per un class diagram o tra package, anche se il package in UML contiene elementi eterogenei; non a caso nel mio post ho preferito usare componenti stereotipati.
Dopodiche' e' possibilissimo che mi trovi ad usare una DSM per altri obiettivi, non legati ad UML, per cui sarebbe utile avere uno strumento che vive anche "da solo" (se lo scrivo io, partiro' sicuramente cosi').

Ovviamente nel caso di una "vera" integrazione sarebbe bello che fosse bidirezionale, ovvero che lo strumento avvertisse se stiamo introducendo (via UML) una dipendenza proibita (quindi la DSM deve essere comunque editabile e non semplicemente inferita, anche perche' il "never" e' impossibile da inferire).
Questo controllo comunque va esercitato anche a livello di codice, visto che alla fine il programmatore fa quello che vuole, ed a volte la fretta porta a violare lo spirito del design originale. Il quale puo' sempre cambiare, ma sarebbe bello che i pilastri fondamentali cambiassero in modo ponderato, e non semplicemente perche' era piu' comodo aggiungere una dipendenza :-).

Dettaglio: visto che sicuramente la cosa non ti crea problemi, considerando la provenienza media dei lettori del blog, la prossima volta metti un commento in inglese, l'argomento era di interesse generale!
 
Post a Comment

<< Home