https://matiux.github.io/slides/software-organization
Github: https://github.com/matiux
Slack GRUSP: matiux
Email: m.galacci@gmail.com
Linkedin: Matteo Galacci
I Programmatori
non hanno "bisogno" di framework e/o librerie
Cosa vuol dire che i programmatori non hanno "bisogno" di framework e/o librerie?
*Spesso introducono legami infrastrutturali che invece dovremmo procrastinare il più possibile. Più riusciamo a procrastinarli, più il nostro software è sulla buona strada.
Framework
Dominio
Software
ALCUNI ELEMENTI DI UN SOFTWARE
AVERE LE IDEE CHIARE SUL DOMINIO
Tutto ciò che nel contesto aziendale è vero a prescindere dal software
Logiche e formule (di business - dominio)
Calcolare il costo di un'automobile
Carta, penna e calcolo il preventivo
ASPETTI DI DOMINIO E ASPETTI TECNOLOGICI
ASPETTI DI DOMINIO E ASPETTI TECNOLOGICI
DOMINIO
INFRASTRUTTURA
ASPETTI DI DOMINIO E ASPETTI TECNOLOGICI
DOMINIO
INFRASTRUTTURA
TEST
I test non sono un optional, e non andrebbero fatti dopo aver scritto il codice, ma prima
PULIZIA DEL CODICE
Scrivere codice è come scrivere un libro.
*Con qualche differenza per i programmi async
NAMING
*Un software che parla di infrastruttura, librerie e framework, avrà vita breve. In alcuni casi è voluto.
Ogni parola in un libro è scelta con cognizione di causa, sulla base della grammatica e di ciò che si vuole esprimere.
I software, come i libri raccontano qualcosa;
raccontano le esigenze di business. *
Nomi delle variabili, delle cartelle, delle classi e dei namespace, vanno ugualmente scelti con cognizione di causa. Prendetevi del tempo!
NAMING
function calculate($a, $b)
{
$res = $a - $b;
return ($res / $b) * 100;
}
function getPercentageChange($oldNumber, $newNumber)
{
$decreaseValue = $oldNumber - $newNumber;
return ($decreaseValue / $oldNumber) * 100;
}
foreach($data => $key as $item)
{
$this->repo->add($item);
$this->names[] = $item->name();
}
foreach($people => $personId as $person)
{
$this->people->add($person);
$this->peopleNames[] = $person->name();
}
Vogliamo essere utilizzatori o programmatori?
Si concentrano su framework o tecnologie, ma…
Spesso non conoscono la programmazione, perchè...
I framework rendono i programmatori pigri
I framework guidano le scelte e lo sviluppo del software
In ambito web, quanto detto sopra pare essere più accentuato
Essere allineati sull'utilizzo di un framework non è risolutivo per un software di qualità
Il framework se ne frega se competenze e conoscenze sono disallineate
Usare un framework non garantisce che tutti i membri del team rispettino uno standard
Se il framework va utilizzato in modo marginale, su cosa ci concentriamo?
PRINCIPI
* E' agnostico chi non prende posizione, evitando il giudizio circa un problema, in quanto non se ne ha, o non se ne può avere, abbastanza conoscenza.
** Sempre sull'esempio dei libri, pensate alla scrittura; pietra, pergamena, carta, digitale
Se il framework va utilizzato in modo marginale, su cosa ci concentriamo?
PRINCIPI
* E' agnostico chi non prende posizione, evitando il giudizio circa un problema, in quanto non se ne ha, o non se ne può avere, abbastanza conoscenza.
** Sempre sull'esempio dei libri, pensate alla scrittura; pietra, pergamena, carta, digitale
* Phil Karlton - Sviluppatore Netscape
La difficoltà di cui parla non risiede nell'azione di “scrivere” un nome, una parola.
"There are only two hard things in Computer Science: cache invalidation and naming things." *
Sta piuttosto nel significato che, grazie a quel nome, vogliamo attribuire a una cosa.
Nella vita di tutti i giorni usiamo nomi per identificare oggetti, o per esprimere concetti. Ci viene spontaneo farlo e ci rende la vita semplice (a patto di conoscere la lingua in questione, l’italiano nel nostro caso). Lo facciamo quotidianamente, per farci comprendere da chi ci ascolta.
class BookRepository
{
public function save(Book $book): void
{
//...
}
}
// Utilizzo il repository
$bookRepository->save($book);
class Libreria
{
public function aggiungi(Libro $libro): void
{
//...
}
}
// Utilizzo il repository
$libreria->aggiungi($libro);
Dal mondo DDD, Ubiquitous language
Il codice è ciò che scriviamo e che vogliamo sia comprensibile anche in futuro.
le cose alle quali dobbiamo dare nomi nel codice, sono le richieste di business (il dominio) che ci vengono fatte.
L’italiano è la nostra lingua di dominio
Se un vostro collega vi spiega qualcosa su un software scritto in inglese, in che lingua lo fa?
Quando il committente, o il business ti da indicazioni sul software da sviluppare, parlandoti del dominio, in che lingua lo fa?
// Doctrine's way
$results = $bookRepository->findByAuthor('Matteo Galacci');
foreach($results => $result)
{
echo $result->author;
}
$libriTrovati = $libri->conAutore('Matteo Galacci');
foreach($libriTrovati => $libro)
{
// Magari usiamo un Value Object (DDD)
echo $libro->autore()->nome();
}
interface BuildingInterface
{
public function getFloors(): int
}
class Tower implements BuildingInterface
{
public function getFloors(): int
{
return 34;
}
}
interface Edificio
{
public function piani(): int
}
class Torre implements Edificio
{
public function piani(): int
{
return 34;
}
}
class CalcolaValoreEdificio
{
public function execute(Edificio $edificio): float
{
}
public function __invoke(Edificio $edificio): float
{
}
}
$calcolaValoreEdificio = new CalcolaValoreEdificio();
$torre = new Torre();
$valore = $calcolaValoreEdificio->execute($torre);
$valore = $calcolaValoreEdificio($torre);
class Libro
{
private function __construct(IdLibro $idLibro)
{
$this->idLibro = $idLibro;
}
public static function creaVuoto(IdLibro $idLibro): self
{
return new self($idLibro);
}
public static function crea(IdLibro $idLibro, Autore $autore): self
{
$libro = new self($idLibro);
$libro->autore = $autre;
return $libro;
}
public static function copiaDa(Libro $libroOriginale, IdLibro $idLibro): self
{
$libro = new self($idLibro);
$libro->autore = $libroOriginale->autore();
return $libro;
}
public function autore(): Autore {}
}
Variabili come: $data, $result, $x, $y, $content, $foo, $bar
"Quando facciamo fatica a capire il codice altrui, non è scontato che il problema sia la nostra mancata conoscenza. Magari stiamo leggendo dei geroglifici."
cit. Io
* Questo principio non vale per una libreria, per un driver infrastrutturale.
Mi concentro sul dominio, e se il dominio è italiano, la modellazione del codice viene di conseguenza. *
DDD nasce con l’intento di agevolare analisi e sviluppo di domini complicati
Uno dei building blocks del DDD è l’ubiquitous language, principio che ci suggerisce di dare importanza al linguaggio di dominio.
Se con il business tratto concetti italiani, e non solo a livello linguistico, ma anche a livello concettuale ("codice fiscale", legislazione, burocrazia), perché devo introdurre una complessità (la traduzione), quando grazie al DDD sto cercando di toglierne?
Scrivere un programma dovrebbe essere una ricerca spasmodica di perfezione.
Nome di una variabile
Architettura di alto livello
Ogni volta che veniamo a meno su questa ricerca:
* Se lavorate da soli, odierete voi stessi
Nomi e concetti ben espressi danno consistenza al dominio
Nomi delle cartelle e organizzazione dei file non possono essere lasciati al caso
Propongono strutture che non enfatizzano il dominio
I framework fanno male all'organizzazione del codice (e non solo)
Colpa bilaterale
Il programmatore non studia
Il fw suggerisce la propria struttura
Ci si fida del framework pensando che il codice possa essere organizzato senza alcun criterio
Il "problema", capito fin dove il framework arriva, risiede nel programmatore.
SYMFONY 5
your-project/
├─ assets/
├─ bin/
│ └─ console
├─ config/
├─ public/
│ └─ index.php
├─ src/
│ └─ ...
├─ templates/
├─ tests/
├─ translations/
├─ var/
│ ├─ cache/
│ ├─ log/
│ └─ ...
└─ vendor/
your_project/
├─ assets/
├─ bin/
│ └─ console
├─ config/
│ ├─ packages/
│ └─ services.yaml
└─ public/
│ ├─ build/
│ └─ index.php
├─ src/
│ ├─ Kernel.php
│ ├─ Command/
│ ├─ Controller/
│ ├─ DataFixtures/
│ ├─ Entity/
│ ├─ EventSubscriber/
│ ├─ Form/
│ ├─ Migrations/
│ ├─ Repository/
│ ├─ Security/
│ └─ Twig/
├─ templates/
├─ tests/
├─ translations/
├─ var/
│ ├─ cache/
│ └─ log/
└─ vendor/
SYMFONY 5
DOMINIO
your_project/
├─ assets/
├─ bin/
│ └─ console
├─ config/
│ ├─ packages/
│ └─ services.yaml
└─ public/
│ ├─ build/
│ └─ index.php
├─ src/
│ ├─ Kernel.php
│ ├─ Command/
│ ├─ Controller/
│ ├─ DataFixtures/
│ ├─ Entity/
│ ├─ EventSubscriber/
│ ├─ Form/
│ ├─ Migrations/
│ ├─ Repository/
│ ├─ Security/
│ └─ Twig/
├─ templates/
├─ tests/
├─ translations/
├─ var/
│ ├─ cache/
│ └─ log/
└─ vendor/
SYMFONY 5
INFRASTRUTTURA
your_project/
├─ assets/
├─ bin/
│ └─ console
├─ config/
│ ├─ packages/
│ └─ services.yaml
└─ public/
│ ├─ build/
│ └─ index.php
├─ src/
│ ├─ Kernel.php
│ ├─ Command/
│ ├─ Controller/
│ ├─ DataFixtures/
│ ├─ Entity/
│ ├─ EventSubscriber/
│ ├─ Form/
│ ├─ Migrations/
│ ├─ Repository/
│ ├─ Security/
│ └─ Twig/
├─ templates/
├─ tests/
├─ translations/
├─ var/
│ ├─ cache/
│ └─ log/
└─ vendor/
SYMFONY 5
Non voglio dire che Symfony "deve" dirci come organizzare il codice o come programmare, ma sta qui l'inghippo:
Non è compito del framework dirci cosa e come fare.
E' compito del programmatore studiare.
SYMFONY 5 + HEXAGONAL ARCHITECTURE
SYMFONY 5 + Hexagonal Architecture
├── Assicurazione (Dominio)
│ └── CalcoloPreventivo (Sottodominio)
│ ├── Application
│ ├── Domain
│ └── Infrastructure
└── App
├── Data
│ └── Migrations
│ ├── Version20200521140651.php
│ └── Version20200603103548.php
└── Kernel.php
SYMFONY 5 + Hexagonal Architecture
├── Assicurazione (Dominio)
│ ├── CalcoloPreventivo (Sottodominio - BC)
│ │ ├── ListinoPrezzi (Modulo)
│ │ └── Preventivo (Modulo)
│ │ ├── Application
│ │ ├── Domain
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ └── Infrastructure
│ │ │ ├── Entity
│ │ │ │ ├── ListinoPrezzi (Modulo)
│ │ │ │ └── Preventivo (Modulo)
│ │ │ │ ├── Preventivo.php
│ │ │ │ ├── Preventivi.php
│ │ │ │ └── IdPreventivo.php
│ │ │ ├── Service
│ │ │ │ ├── ListinoPrezzi (Modulo)
│ │ │ │ └── Preventivo (Modulo)
│ │ │ │ ├── CreaPreventivo.php
│ │ │ │ └── CreaPreventivoRequest.php
│ │ │ └── ValueObject
│ │ │ └── Preventivo (Modulo)
│ │ │ └── NumeroPreventivo.php
├── Assicurazione (Dominio)
│ ├── CalcoloPreventivo (Sottodominio - BC)
│ │ ├── Application
│ │ ├── Domain
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ │
│ │ └── Infrastructure
│ │ │ ├── Entity
│ │ │ │ ├── Preventivo.php
│ │ │ │ ├── Preventivi.php
│ │ │ │ └── IdPreventivo.php
│ │ │ ├── Service
│ │ │ │ ├── CreaPreventivo.php
│ │ │ │ └── CreaPreventivoRequest.php
│ │ │ └── ValueObject
│ │ │ └── NumeroPreventivo.php
SYMFONY 5 + Hexagonal Architecture
├── Assicurazione (Dominio)
│ ├── CalcoloPreventivo (Sottodominio BC)
│ │ └── Preventivo (Modulo)
│ │ ├── Application
│ │ ├── Domain
│ │ └── Infrastructure
│ │ ├── Communication
│ │ │ └── Http
│ │ │ └── Symfony
│ │ │ └── Controller
│ │ │ └── PreventivoController.php
│ │ ├── Command
│ │ │ └── Console
│ │ │ └── Symfony
│ │ │ └── FaQualcosaInShell.php
│ │ └── Domain
│ │ ├── Service
│ │ │ └── TransactionalDomainService.php
│ │ └── Doctrine
│ │ ├── Entity
│ │ │ ├── DoctrinePreventivi.php
│ │ │ └── DoctrineIdPreventivo.php
│ │ ├── Persistence
│ │ │ └── Mapping
│ │ │ ├── Entity.Preventivo.orm.xml
│ │ │ ├── Entity.RiepilogoPreventivo.orm.xml
│ │ │ └── ValueObject.NumeroPreventivo.orm.xml
│ │ └── ValueObject
│ │ └── DoctrineNumeroPreventivo.php
SYMFONY 5 + Hexagonal Architecture
├── Assicurazione
│ ├── CalcoloPreventivo
│ │ └── Preventivo (Modulo)
│ │ ├── Application
│ │ │ ├── DataTransformer
│ │ │ │ └── PreventivoToArrayDataTransformer.php
│ │ │ └── Service
│ │ │ └── TrovaPreventivo.php
│ │ ├── Domain
│ │ └── Infrastructure