...e le conseguenti pessime decisioni di design.

Sto lavorando con le API di amministrazione dell’ACS di Windows Azure, API che sono esposte oltre che dal portale di Azure con una bella e comoda interfaccia anche come servizi in modo da poter programmaticamente gestire la configurazione.

I servizi che l’ACS di Azure espone sono basati su OData (almeno in apparenza) ed espongono un modello apparentemente molto comodo, sempre in apparenza :-)

Osservate il seguente stralcio di codice:

var client = CreateManagementServiceClient();
var parties = client.RelyingParties;
foreach ( var party in parties )
{
    Console.WriteLine( "DisplayName: {0}", party.DisplayName );
    Console.WriteLine( "Name: {0}", party.Name );
    Console.WriteLine( "Id: {0}", party.Id );
    Console.WriteLine( "Description: {0}", party.Description );
    Console.WriteLine();
    Console.WriteLine( "Addresses:" );
    foreach ( var address in party.RelyingPartyAddresses )
    {
        Console.WriteLine( "\t- {0} -> {1}", address.EndpointType, address.Address );
    }
}

il servizio espone una collection di relying parties, e, nell’esempio cui sopra, le enumeriamo stampando qualcosa a video, la cosa curiosa è che il ciclo sugli indirizzi non funziona, ritorna sempre una lista di indirizzi vuota.

Per farlo funzionare come desideriamo è necessario ignorare la proprietà sui cui cerchiamo di iterare e cambiarlo così:

var addresses = client.RelyingPartyAddresses.Where( rpa => rpa.RelyingPartyId == party.Id );
foreach ( var address in addresses )
{
    Console.WriteLine( "\t- {0} -> {1}", address.EndpointType, address.Address );
}

Interessante non trovate?

Ipotizzo che quella proprietà RelyingPartyAddresses su party sia il risultato del mapping delle relazioni con un ORM e, da totale ignorante in materia di OData, deduco anche che venga esposta dal servizio OData a prescindere.

Ora, senza entrare nel dettaglio del perché funziona/non funziona o del che cosa sbaglio (perché probabilmente sbaglio qualcosa io) mi pare ovvio che se una parte a voi sconosciuta espone un’API per interrogare dei dati abbia molto senso che questa API sia a prova di “ignorante” e dall’esempio di cui sopra è evidente che un Object Model esposto così come è (almeno in apparenza) non è la soluzione ai mali del mondo, anzi.

CQRS: Read Model

L’esempio di cui sopra è la dimostrazione lampante del perché abbia molto senso avere un modello in lettura che sia espressamente pensato per i casi d’uso dell’utilizzatore finale (semi-cit.) e non espressamente pensato per la comodità dello sviluppatore che l’ha scritto. Una motivazione molto semplice è che ho investito un po’ di tempo, e quindi soldi, solo per capire che non ero io quello “ignorante” in questo scenario.

Come sarebbe da fare?

Se guardiamo l’API di cui sopra ha senso quindi far funzionare la prima soluzione o la seconda? eliminando la prima dall’API? Nessuna delle due, perché la generalizzazione è il seme di tutti i mali quando si parla di design di un’API.

Quello che è necessario fare è quindi guardare a come verrebbe usata, dal mio punto di vista quello che mi aspetterei di poter fare è:

  • recuperare una lista, con un set di informazioni minimo, di Relying Party:
    • client.GetRelyingPartiesSummary();
    • che non ritorna una lista di RelyingParty ma bensì una lista di RelyingPartyDescriptors con le informazioni essenziali a capire che cosa sia la cosa che sto guardando;
  • poter accedere al dettaglio di una singola Relying Party:
    • client.GetRelyingParty( long partyId );
    • che mi ritorna una RelyingParty che ha una proprietà “Addresses” che funziona.

Se ci pensate CQRS dice una cosa estremamente semplice: leggere e scrivere sono due operazioni molto diverse, talmente diverse che non ha senso cercare di farle con lo stesso “strumento” (in questo caso il modello), ha quindi molto senso calare nel contesto ogni singola operazione e usare lo strumento giusto (modello ad hoc) per la singola operazione che stiamo osservando.

E…non mettetela sul piano del “è costoso” perché non sta in piedi, semplicemente perché state guardando un lato solo della medaglia e non l’insieme del problema (i costi), il costo non è solo quello di scrittura, ma anche quello d’uso, se ci mettete poco a realizzarla, perché fate 4 drag & drop sul vostro fantastico designer ma poi giorni per usarla non è che sia costato poco…eh…ti spiego :-)

.m

N.d.R.: non c’'e l’ho con Azure, sia chiaro, piattaforma con cui vado decisamente d’accordo, le API esposte dall’ACS sono state semplicemente lo spunto per la riflessione.