- Come é un buon codice
- Design principles
- Design practices oppure pratiche a livello di progettazione
- Design patterns oppure pratiche a livello di codice (Singleton, Prototype, Factory, Builder, Facade, Proxy, Strategy, Iterator, Observer, Mediator, State)
- Architecture patterns oppure pratiche a livello di intero progetto (Client/Server, DDD, SOA; MVC…)
- Architecture styles oppure principi architetturali da seguire (REST, SOAP, IoC; Microservices)
Come è un buon codice?
- Funzionale.
- Testabile.
- Leggibile.
- Rispecchia principi SOLID.
- Rispecchia principi della buona programmazione.
Design principles (Principi di programmazione o fattori guida)
- Idea alla base =>
- Aiutano ad ottenere un software architetturalmente buono.
- Nascondere la complessità dei moduli componendo la soluzione.
- Fornire componenti o funzioni facilmente adottabili che potremmo riutilizzare anche con altre applicazioni.
- SOLID (C# article)
- Single responsibility principle (Responsabilité unique): une classe, une methode, une fonction doit avoir une et une seule responsabilité
- Quando si sviluppa un metodo è meglio che questi faccia una e una sola operazione.
- Open/Closed (Ouvert/Fermé): une classe doit être ouverte à l’estension mais fermée à la modification
- Liskov substitution (Substitution de Liskov): une instance T doit pouvoir être remplacée par une istante G (G sous-type de T) sans que cohérence du programme change.
- Interface segregation (Sègrègation des interfaces): préférer plusieurs interfaces spécifiques pour chaque client plutôt qu’une seule générale.
- Dependency Inversion (Inversion des dèpendances):
- Cosa è?
- Bisogna dipendere dalle astrazioni e non dalle implementazioni.
- Implementa la IoC.
- Implementa la Dependency Injection.
- Invece di inizializzare un’oggetto che serve alla nostra classe direttamente in essa è meglio delegarne la creazione al di fuori.
- Perché abbiamo bisogno?
- Permette di avere un sistema disaccoppiato.
- Permette di centralizzare i cambiamenti e avere quindi un’architettura migliore.
- Prendendo l’esempio di MVC.
- Sappiamo che il gran numero di logica è messa nei controlli.
- Piuttosto che inserire tutta questa logica (logging, validation, dal… ) direttamente negli stessi
- Meglio creare dei componenti da poter riusare nei vari controller.
- Meglio che i controlli si interfaccino con tali componenti invece che interagire direttamente con un’implementazione stretta.
- Così facendo, è possibile modificare tali componenti senza avere conseguenze nei controller che li usano.
- Cosa è?
- Single responsibility principle (Responsabilité unique): une classe, une methode, une fonction doit avoir une et une seule responsabilité
- Separation of concern :
- Uno stile di implementazione del codice in un modo pulito dove per esempio la business logic e l’UI non dovrebbero essere mischiati cioé risiedere in file separati.
- RIP
- Replace IF with polymorphism.
- Least Knowledge
- Uh componente non dovrebbe conosce i dettagli degli altri componenti.
- Simple factory
- Spostare la parola chiave “NEW” in una classe centralizzata.
- Lazy loading
- Load objects on demand.
- KISS (Keep it simple, stupid)
- Raccomanda la semplicità. Cioé evitare nel limite del possibile le cose non indispensabilii.
- DRY (Don’t repeat yourself)
- Evitare la ridondanza facilitando allo stesso tempo: la manutenzione, i test e il debug .
- Se c’è una certa operazione che viene ripetuta più volte all’interno del codice, è meglio pensare ad un refactoring per centralizzare questa funzionalità.
- Law of demeter
- Parlare solo agli amici immediati
- Un oggetto A puo’ richiedere un servizio da un oggetto B ma A non dovrebbe utilizzare B per accedere a un terzo oggetto e richiederne i servizi.
- MoSCoW
- Technique visant à prioriser des besoins ou des exigences en matière d’assistance à maîtrise d’ouvrage et de développement logiciel
- M : must have this, c’est-à-dire ‘doit être fait’ (vital).
- S : should have this if at all possible, c’est-à-dire devrait être fait dans la mesure du possible (essentiel).
- C : could have this if it does not affect anything else, pourrait être fait dans la mesure où cela n’a pas d’impact sur les autres tâches (confort).
- W : won’t have this time but would like in the future, ne sera pas fait cette fois mais sera fait plus tard (luxe, c’est votre zone d’optimisation budgétaire).
- YAGNI (You ain’t gonna need it) (Vous n’en aurez pas besoin)
- C’est un principe d’extreme programming qui déclare que les programmeurs ne devraient pas ajouter de fonctionnalité à un logiciel tant que celle-ci n’est pas absolument nécessaire.
Design practices (Pratiche di progettazione)
- OOP =>
- Aiuta a risolvere problemi di sviluppo noti (RESM).
- Consente di estendere e mantenere le applicazioni in un modo migliore rispetto all’approccio monolitico.
- Domain model =>
- Standardizza e centralizza il modello dati dell’applicazione.
- Usando i domain model (o oggetti) per inviare e ricevere dati attraverso i livelli ogni volta che è necessario, in modo che l’applicazione abbia lo stesso formato di dati ovunque.
- Layer =>
- In base alla funzionalità dei componenti, suddividili in diversi livelli come
- Accesso ai dati (DAL).
- Business Logic.
- Helper o Utility .
- UI.
- In base alla funzionalità dei componenti, suddividili in diversi livelli come
- Interfacce =>
- Utilizzare sempre l’astrazione per ottenere lo sviluppo di componenti ad accoppiamento debole.
Design Patterns (Strategie a livello di codice)
- Offrono soluzioni standard e testate =>
- Per rispondere a problemi ricorrenti e tipici della programmazione OOP.
- Riconosciute come buone pratiche in risposta a un problema.
- Tipi di design pattern
- Creational (corrispondono alla fase di istanziazione nella OOP, come gli oggetti sono creati/istanziati).
- Structural (corrispondono alla fase di progettazione/template nella OOP phase , come gli oggetti sono strutturati).
- Behavioral (corrispondono alla fase di esecuzione nella OOP, come gli oggetti comunicano tra di loro).
- Creational (Singleton, Prototype, Factory, Builder) =>
- Singleton :
- Si lavora con una sola istanza di una classe.
- Se si lavora con multithread è necessario gestire la concorrenza.
- Serve per mantenere lo stato in memory di un oggetto per tutta la durata dell’applicazione.
- Un metodo semplice per creare una classe Singleton in c# è tramite il meccanismo built-in di dependency injection.=
-
//program.cs //Creare un'implementazione dell'interfaccia desiderata e registrarla nel DI container services.AddSingleton<IUserManager, UserManager>();
-
- Come creare una classe singleton:
- Definire il costruttore come privato.
- Definire metodi static.
- [Opzionalmente] E’ possibile esporre una proprietà che ritorna un’istanza delle classe.
- Esempio:
-
//La funziona che recupera tale oggetto e // prima di ritornarlo verifica che non sia già stato inizializzato // solo in quel caso lo inizializza public class clsSingleton { private static int counter; private static readonly Lazy<clsSingleton> istance = new Lazy<clsSingleton>(() => new clsSingleton()); public static clsSingleton Istance = istance.value; private clsSingleton() { } public static void Hit() { counter++; } public static int getTotalHits() { return counter; } }
-
- Prototype :
- Creare un oggetto indipendente sulla base di un’altro per estenderne le proprietà, copiandone l’esatto stato.
- Singleton :
-
-
- Deep cloning
- E’ clonato l’oggetto parent più tutti gli oggetti da lui contenuti.
- Per ottenerlo è necessario procedere clonando tutti gli oggetti figli.
- Shallow cloning
- E’ copiato solo l’oggetto parent.
- Esempio :
-
//Javascript const Zombie = { eatBeans() { return 'yum'; } } const chad = Object.create(zombie, { name: {value: chad}); Object.getPrototypeOf(chad) //c# //Classe da clonare var user = new User("Nike", 34); var userCloned = user.Clone() as IUser; public class Customer : IClonable { public string Name; public int Age; public Customer(string name, int age) { this.Name = name; this.Age = age; } public Customer Clone() { return (Customer) this.MemberwiseClone(); } } Customer customerCloned = new Customer(); //creo una copia del primo oggetto customerCloned .name = "myname"; //setto i valori del primo oggetto Customer customerclone; //creo una copia del secondo oggetto customerclone = customerCloned.getClone(); //copio il primo oggetto nel secondo
-
- Deep cloning
-
-
- Factory :
- Invece di usare la parola chiave new per istanziare un oggetto si usa un metodo o una funzione che lo fa per te.
- Serve per non dovere ripetere più volte uno stesso codice.
- Server per organizzare il codice.
- Factory, Abstract Factory, Method Factory.
- Il metodo non ritorna un valore concreto ma un recipiente.
-
class IosButton() : BaseButton { } ; class AndroidButton() : BaseButton { } ; //Design pattern: Factory class MyFactory { private static Dictionary<string, BaseButton> osTypesDict = new Dictionary<string, BaseButton>(); public static BaseButton createButton(string osType) { //Design pattern: Lazy loading if(osTypesDict.count == 0) { osTypesDict.Add("Ios", new IosButton()); osTypesDict.Add("Android", new AndroidButton()); } //Design pattern: RIP replace if with poly return osTypesDict["osType"]; } } //Consuming MyFactory class //UI const button1 = MyFactory.createButton(osType);
- Può essere implementato anche tramite l’uso di un delegate :
-
var calculator = CreateCalculator(); console.writeline(calculator(2)) //84 Func<int, int> CreateCalculator() { var factor = 42; return (n) => n * factor; }
-
- Builder :
- Da usare
- Se il processo di inizializzazione di una classe è particolarmente complesso.
- Se devo disaccoppiare la rappresentazione dal processo di inizializzazione (ho bisogno di più rappresentazioni).
- Non inizializzo una classe con tutte le proprietà in un solo passaggio (nel costruttore)
- Creo un metodo per ogni proprietà della classe da inizializzare (builder)
- Li chiamo uno dopo l’altro o nella sequenza desiderata per creare l’instanza desiderata detta product (director)
- Posso avere così ottenere così diverse rappresentazioni dello stesso oggetto (product)
- In JQuery è spesso usato questo pattern
- Da usare
- Factory :
-
-
- Esempio:
-
//program.cs services.AddSingleton<IUser>(new User.Builder() .WithName("Nike") .WithAge(29) .Build()); //user.cs public interface IUser { public string Name {get;} public int Age {get;} } class User : IUser { private string name; private int age; public string Name { get { return name; } } public string Age { get { return age; } } private new() { } public Builder { private readonly User user; public Builder() { user = new User(); } public Builder WithName(string name) { user.name = name; return this; } public Builder WithAge(int age) { user.age = age; return this; } public Build() { return user; } } }
-
- Esempio:
-
- Structural (Facade, Proxy) =>
- Facade :
- Lo scopo è esporre un’interfaccia (servizio) unificata invece che esporre le interfacce dei singoli sottoinsiemi che vengono usati all’interfaccia principale.
- Orchestratore =>
- Ho una classe (al top del mio sistema) che mostra all’esterno un’interfaccia semplice.
- Nascondendo le implementazioni complesse che ci stanno dietro.
- Sotto sistemi =>
- Ho una serie di sottosistemi che vengono usati all’interno della mia classe orchestrandoli come necessario.
- Evita che il client debba fare molte chiamate a i vari sottosistemi a causa dell’esposizione di troppi sottoservizi in maniera troppo granulare.
- Un esempio sono i compiler che fanno un sacco di cose ma espongono solo alcune funzionalità semplici da usare.
- Permette di comunicare secondo uno standard.
- Permette di semplificare la UI delegando della logica alla classe orchestratore.
- JQuery usa spesso questo pattern.
- Facade :
-
-
- Esempio:
-
Esempio di facade pattern: Implementare un unico controller che richiami più servizi (sottosistemi) piuttosto che più controller (uno per ogni servizio).
-
- Esempio:
-
-
- Proxy :
- Lo scopo di questo pattern è fornire un placeholder per un altro oggetto e gestirne l’accesso.
- Non viene esposto al client direttamente un oggetto ma il suo proxy. In modo tale si possa controllare come l’end user acceda all’oggetto stesso.
- Classe Proxy :
- Invece di interagire direttamente con la classe che contiene i dati.
- Uso una classe proxy (che funziona come un’interfaccia) che punta verso la vera classe contenente i dati.
- Sostituisco la classe richiesta con un sostituto (proxy).
- E’ possibile allora creare molti classi proxy che puntano verso il grosso oggetto in memoria .
- Evitando così di doverlo duplicare, migliorando le prestazioni.
- Classe Subject :
- Definisce un’interfaccia comune tra la classe proxy e la classe reale così che ovunque la classe reale è attesa è possibile usare la classe proxy.
- Classe RealSubject :
- Definisce l’oggetto reale che il proxy definisce.
- Reactivity system in VUE.js
- I proxy sono utili quando si lavora con oggetti molto grossi che costa duplicarli in memoria.
- Proxy :
-
-
- Esempio :
-
using System; namespace Proxy.RealWorld { /// <summary> /// Proxy Design Pattern /// </summary> public class Program { public static void Main(string[] args) { // Create math proxy MathProxy proxy = new MathProxy(); // Do the math Console.WriteLine("4 + 2 = " + proxy.Add(4, 2)); Console.WriteLine("4 - 2 = " + proxy.Sub(4, 2)); Console.WriteLine("4 * 2 = " + proxy.Mul(4, 2)); Console.WriteLine("4 / 2 = " + proxy.Div(4, 2)); // Wait for user Console.ReadKey(); } } /// <summary> /// The 'Subject interface /// </summary> public interface IMath { double Add(double x, double y); double Sub(double x, double y); double Mul(double x, double y); double Div(double x, double y); } /// <summary> /// The 'RealSubject' class /// </summary> public class Math : IMath { public double Add(double x, double y) { return x + y; } public double Sub(double x, double y) { return x - y; } public double Mul(double x, double y) { return x * y; } public double Div(double x, double y) { return x / y; } } /// <summary> /// The 'Proxy Object' class /// </summary> public class MathProxy : IMath { private Math math = new Math(); public double Add(double x, double y) { return math.Add(x, y); } public double Sub(double x, double y) { return math.Sub(x, y); } public double Mul(double x, double y) { return math.Mul(x, y); } public double Div(double x, double y) { return math.Div(x, y); } } }
-
- Esempio :
-
- Behavioral (Strategy, Iterator, Observer, Mediator, State) =>
- Strategy (Add algorithms dynamically) :
- Definisce una famiglia di algoritmi (ConcreteStrategy: Strategy), incapsula ciascuno di essi e li rende intercambiabili (Context) indipendentemente dai client che lo utilizzano.
- classe Strategy =>
- Dichiara un’interfaccia comune a tutti gli algoritmi supportati.
- Context usa questa interfaccia per chiamare l’implementazione concreta definita da ConcreteStrategy
- classe ConcreteStrategy =>
- Implementa l’argoritmo desiderato usando l’interfaccia Strategy
- classe Context =>
- E’ configurato con un oggetto ConcreteStrategy
- Mostra il modello di strategia che incapsula la funzionalità sotto forma di un oggetto.
- Ciò consente ai clienti di modificare dinamicamente le strategie algoritmiche.
- Strategy (Add algorithms dynamically) :
-
-
-
-
using System; public class Program { public static void Main(string[] args) { Context context; // Three contexts following different strategies context = new Context(new ConcreteStrategyA()); context.ContextInterface(); context = new Context(new ConcreteStrategyB()); context.ContextInterface(); context = new Context(new ConcreteStrategyC()); } } /// The 'Strategy' abstract class public abstract class Strategy { public abstract void AlgorithmInterface(); } /// A 'ConcreteStrategy' class public class ConcreteStrategyA : Strategy { public override void AlgorithmInterface() { ... } } /// B 'ConcreteStrategy' class public class ConcreteStrategyB : Strategy { public override void AlgorithmInterface() { ... } } /// The 'Context' class public class Context { Strategy strategy; public Context(Strategy strategy) { this.strategy = strategy; } public void ContextInterface() { strategy.AlgorithmInterface(); } }
-
- Delegate =>
- Lo ‘stategy’ pattern può essere implementato anche con i delegate
-
public class Program { delegate int MathOp(int member1, int member2); static void Main(string[] args) { CalculateAndPrint(1, 2, Add); } static void CalculateAndPrint(int mermber1, int member2, MathOp mathOp) { var result = mathOp(mermber1, member2); Console.WriteLine(result); } static int Add(int x, int y) { return x + y; } static int Sub(int a, int b) { var ret = a - b; return ret < 0 ? 0 : ret; } }
-
- Iterator :
- Cos’è =>
- Fornisce un modo per accedere agli elementi di una raccolta di oggetti in modo sequenziale senza conoscerne la struttura sottostante.
- Questo modello è comunemente usato nei sistemi di menu di molte applicazioni come Editor, IDE, ecc.
- Client => E’ la classe che contiene una raccolta di oggetti e utilizza l’operazione Next dell’iterator per recuperare elementi dall’aggregato in una sequenza appropriata.
- Iterator => E’ un’interfaccia che definisce le operazioni per l’accesso agli elementi della raccolta in una sequenza.
- ConcreteIterator => E’ la classe che implemente l’interfaccia Iterator
- Aggregate => E’ un’interfaccia che definisce un’operazione per creare un iteratore
- Concrete Aggregate => E’ la classe che implementa l’interfaccia Aggregate
- Cos’è =>
-
-
-
-
public class Client { public void UseIterator() { ConcreteAggregate aggr = new ConcreteAggregate(); aggr.Add("One"); aggr.Add("Two"); aggr.Add("Three"); aggr.Add("Four"); aggr.Add("Five"); Iterator iterator = aggr.CreateIterator(); while (iterator.Next()) { string item = (string)iterator.Current; Console.WriteLine(item); } } } public interface Aggregate { Iterator CreateIterator(); } public class ConcreteAggregate : Aggregate { private ArrayList items = new ArrayList(); public Iterator CreateIterator() { return new ConcreteIterator(this); } public object this[int index] { get { return items[index]; } } public int Count { get { return items.Count; } } public void Add(object o) { items.Add(o); } } public interface Iterator { object Current { get; } bool Next(); } public class ConcreteIterator : Iterator { private ConcreteAggregate aggregate; int index; public ConcreteIterator(ConcreteAggregate aggregate) { this.aggregate = aggregate; index = -1; } public bool Next() { index++; return index < aggregate.Count; } public object Current { get { if (index < aggregate.Count) return aggregate[index]; else throw new InvalidOperationException(); } } }
-
- Observer :
- E’ un’impostazione di tipo push-based => Il cambiamento (nel subject) è visibile (agli observer) non appena avviene (method Update)
- Questo pattern è usato in una relazioni uno-a-molti tra oggetti così che se un’oggetto è modificato tutti i suoi oggetti dipendenti lo sono automaticamente.
- Permette ad un a un singolo oggetto, noto come soggetto, di pubblicare le modifiche al suo stato.
- Permette a molti oggetti di sottoscrivere eventi che sono trasmessi da un oggetto.
- Un esempio è una radio che emette signali
- Un esempio è Firebase, quando un dato cambia sul server, tutti i client che erano collegati cambiano la visualizzazione di quel dato
- Un modo semplice per implementare l’observer pattern è tramite un meccanismo event-delegate
- Una classe subject espone un evento che può essere sottoscritto da una classe observer
- Quando lo stato della classe subject cambia, viene lanciato l’evento e l’observer ricevera un callback
- Subject => E’ un’interfaccia che conitiene una collezione privata di observers che hanno sottoscritto eventi trasmesi dalla classe usando Notify
- ConcreteSubject => Implementa un subject e quando viene apportata una modifica al suo stato, l’oggetto chiama l’operazione di notifica della classe base per indicarlo a tutti i suoi osservatori
- Observer => E’ un’interfaccia che implementa un’operatozione Update che viene chiamata quando lo stato del Subject cambia
- ConcreteObserver => Implementa l’observer e esamina il Subject per determinare quali informaioni sono cambiate
-
-
-
-
public abstract class Subject { private ArrayList observers = new ArrayList(); public void Attach(IObserver o) { observers.Add(o); } public void Detach(IObserver o) { observers.Remove(o); } public void Notify() { foreach (IObserver o in observers) { o.Update(); } } } public class ConcreteSubject : Subject { private string state; public string GetState() { return state; } public void SetState(string newState) { state = newState; Notify(); } } public interface IObserver { void Update(); } public class ConcreteObserver : IObserver { private ConcreteSubject subject; public ConcreteObserver(ConcreteSubject sub) { subject = sub; } public void Update() { string subjectState = subject.GetState(); Console.WriteLine(subjectState); } }
-
- Mediator (E’ come un middleman) :
- Consente a più oggetti di comunicare tra loro (secondo una relazione molti a molti) senza conoscere la struttura dell’altro.
- Questo modello definisce un oggetto che incapsula il modo in cui gli oggetti interagiranno tra loro e permette il loose coupling
- Questo modello è comunemente usato nei sistemi di menu di molte applicazioni come Editor, IDE, ecc.
- In informatica lo strato Middleware di comunicazione opera secondo questo pattern. Intercetta le richiesta e crea il formato giusto da inviare al componente che gestira la risposta.
- Divisione delle responsabilità e elimina duplicazione del codice
- Mediator => E’ un’interfaccia che definisce le operazioni possono essere effettuate tra oggetti Colleague per comunicare tra loro.
- ConcreteMediator => E’ la classe che implementa le operazini di comunicazione del mediator
- Colleague => E’ una classe che definisce un’unico campo protected che ospita una referenza al mediator
- ConcreteCollegueA/B => Sono classi che comunicano tra loro tramite il mediator
-
-
-
-
public abstract class Colleague { protected IMediator _mediator; public Colleague(IMediator mediator) { _mediator = mediator; } } public class ConcreteColleagueA : Colleague { public ConcreteColleagueA(IMediator mediator) : base(mediator) { } public void Send(string msg) { Console.WriteLine("A send message:" + msg); _mediator.SendMessage(this, msg); } public void Receive(string msg) { Console.WriteLine("A receive message:" + msg); } } public class ConcreteColleagueB : Colleague { public ConcreteColleagueB(IMediator mediator) : base(mediator) { } public void Send(string msg) { Console.WriteLine("B send message:" + msg); _mediator.SendMessage(this, msg); } public void Receive(string msg) { Console.WriteLine("B receive message:" + msg); } } public interface IMediator { void SendMessage(Colleague caller, string msg); } public class ConcreteMediator : IMediator { public ConcreteColleagueA Colleague1 { get; set; } public ConcreteColleagueB Colleague2 { get; set; } public void SendMessage(Colleague caller, string msg) { if (caller == Colleague1) Colleague2.Receive(msg); else Colleague1.Receive(msg); } }
-
- State :
- Un oggetto si comporta in modo diverso in rapporto ad un numero finito di stati.
- E’ possibile creare una classe per ogni possibile stato. Tutte queste classi conddividono un metodo che agisce diversamente in ognuna di essere
- L’oggetto esporrà il suo stato all’esterno
- L’oggetto non avrà nessuna implementazione al suo interno ma chiamerà il metodo comune di queste classi
-
//Classe umano pensa in modo diverso in rapporto al proprio mood class Human { think(mod){ switch (mod) { case 'happy': return 'I am happy'; case 'sad': return 'I am sad'; } } } //E' possibile riscrivere la classe usando delle classi per ogni stato da implementare //con uno stesso metodo ma che si comporta in modo diverso interface State { string Think(); } class Happy : State { think() { return 'I am happy'; } } class Sad: State { think() { return 'I am sad'; } } class Human { state State; Human() { this.state = new Happy(); } think() { return this.state.think(); } changeState(state) { this.state = state; } }
-
Architecture pattern (Tecniche a livello di intero progetto)
Definiscono le strutture fisiche o logiche di soluzioni standard testate riguardanti le componenti di un’applicazione
- Client / Server :
- Distribuisce il sistema in due applicazioni in cui il client effettua richieste al server.
- In molti casi, il server è un database o qualsiasi altra applicazione che funge da server.
- Component-Based :
- Scompone la logica dell’applicazione in componenti funzionali o logici riutilizzabili che espongono proprietà o metodi ben definiti per interagire con i componenti come Grid, Button, Data Adapter ecc.
- DDD (Domain Driven Design) :
- Evitare ‘big ball of mud’:
- Invita a smettere di costruire grandi applicazioni (o, in discorsi DDD, “big ball of mud”).
- Domain:
- Il presupposto fondamentale del DDD è che gli esseri umani sono incapaci di creare applicazioni aziendali, ma sono in grado di comprendere problemi piccoli e ben delimitati (un “dominio”).
- Questi ‘domain’ possono essere descritti utilizzando parole ben comprese dagli sviluppatori e dagli utenti finali
- Tali parole posso assumere un valore diverso a seconda delle parti dell’applicazioni in cui vengono usate
- La parola ‘cliente’ ha un significato diverso per una persona del marketing e un addetto del supporto clienti.
- Evitare applicazioni aziendali:
- La DDD presume che la creazione di applicazioni che “integrano l’intera organizzazione” o la creazione di “modelli a oggetti a livello aziendale” sia un gioco da pazzi e destinato al fallimento.
- La DDD suggerisce di creare applicazioni solo per il marketing, solo per l’assistenza clienti, di creare applicazioni all’interno di quelle parti dell’organizzazione in cui le parole hanno un significato costante
- La definizione di “dominio” è l’area dove le parole usate per definire ciò che fa l’azienda hanno un solo significato.
- Lo sviluppatore deve scrive applicazioni che supportano un singolo dominio
- Evitare ‘big ball of mud’:
- Service-Oriented Architecture (SOA) :
- SOA è semplicemente un’applicazione che:
- fornisce servizi ad altre applicazioni indipendentemente da qualsiasi piattaforma o tecnologia/
- espone e utilizza funzionalità come servizio utilizzando contratti e messaggi.
- Nella creazione di un’applicazione per un dominio potresti, ovviamente, aver bisogno di parlare con un altro dominio.
- E’ per questo che abbiamo architetture orientate ai servizi => per consentire ai domini di comunicare tra loro.
- Limita CRAP =>
- Permette di limitare i clicli di creazione / riparazione / abbando / riscruttura tipici di un approccio classico.
- SOA è semplicemente un’applicazione che:
- MVC:
- Model = Business entities
- View = Presentation Logic
- Controller = Business Logic (l’entry point), Binding layer (connette tra loro gli altri 2 livelli)
- MVP (Model View Presenter)
- MVVM (adatto ad applicazioni desktop) :
- Model
- View (l’entry point)
- View Model (il controller di MVC)
- Repository
- Unit of Work
- N-tiers =>
- Distribuisce la funzionalità in parti separate con ogni parte che è un livello situato su un computer fisicamente separato.
- Suddivide i problemi dell’applicazione in gruppi impilati (livelli) e vengono distribuiti sullo stesso computer.
- CQRS (Command Query Responsibility Segregation)
- Si base sul fatto che ottimizzare un DB per la lettura non é come ottimizzalo per la scrittura.
- Supera la visione classi di un sistema CRUD (lettura e scrittura in un unico sistema) separando la parte di lettura e scrittura dei dati
- Separazione logica
- Divido l’applicazione i più componenti. Alla UI espongo un’interfaccia comune.
- Separazione logica
-
-
- Separazione fisica
- Più di un DB + processo di copia (oppure bus) tra i DB di scrittura e quelli di lettura.
- Separazione fisica
-
Architecture styles (Indicano dei principi da seguire)
- REST (Representational State Transfer).
- SOAP (Simple Object Access Protocol).
- IoC (Inversion of Control):
- E’ una tecnica di programmazione in cui la logica che non riguarda direttamente un modulo venga delegata a componenti esterni al modulo stesso.
- Source Locator.
- Event.
- Delegates.
- Dependency Injection (Constructor, Property, Method).
- Pattern usato per fornire le dipendenze necessarie ad una classe.
- Strategy design pattern.
- Factory pattern.
- E’ una tecnica di programmazione in cui la logica che non riguarda direttamente un modulo venga delegata a componenti esterni al modulo stesso.
- Microservices :
- Supera i limite delle architetture N-tiers che sono di tipo monolitico.
- Cioé quando faccio un cambio ad un modulo sono verosibilmente costretto a farlo anche negli altri layers della mia architettura.
- Self-contained indipendent deployable unit :
- Microservices è uno architecture style in cui si sviluppano servizi contenenti piccole porzioni di business logic che
- vengono eseguiti in processi separati.
- comunicano tra loro tramite HTTP.
- vengono automaticamente deployati.
- Il progetto è diviso in piccole strutture fisiche secondo la natura del domain.
- Per esempio in un ERP è possibile creare microservizi per le parti Invoice e Accounting separatamente.
- Ognuna di queste unità fisiche ha il loro DB e la loro struttura completa.
- Microservices è uno architecture style in cui si sviluppano servizi contenenti piccole porzioni di business logic che