Principi architetturaliArchitectural principles

"Se i costruttori costruissero edifici come i programmatori scrivono i programmi, il primo picchio che passa distruggerebbe la civiltà.""If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization."
- Gerald Weinberg- Gerald Weinberg

È necessario ideare e progettare soluzioni software tenendo presente l'aspetto della loro gestione.You should architect and design software solutions with maintainability in mind. I principi indicati in questa sezione possono favorire scelte architetturali che daranno vita ad applicazioni pulite e gestibili.The principles outlined in this section can help guide you toward architectural decisions that will result in clean, maintainable applications. Generalmente, questi principi orientano verso la creazione di applicazioni contenenti componenti discreti che non sono strettamente accoppiati ad altre parti dell'applicazione, ma che comunicano tramite interfacce esplicite o sistemi di messaggistica.Generally, these principles will guide you toward building applications out of discrete components that are not tightly coupled to other parts of your application, but rather communicate through explicit interfaces or messaging systems.

Principi di progettazione comuniCommon design principles

Separazione delle problematicheSeparation of concerns

Un principio guida per lo sviluppo di applicazioni è la separazione dei concetti.A guiding principle when developing is Separation of Concerns. In base a questo principio, deve esistere una separazione a livello di software in funzione delle operazioni eseguite.This principle asserts that software should be separated based on the kinds of work it performs. Ad esempio, si consideri un'applicazione che include la logica per l'identificazione di elementi importanti da visualizzare all'utente e la formattazione specifica di tali elementi per renderli più evidenti.For instance, consider an application that includes logic for identifying noteworthy items to display to the user, and which formats such items in a particular way to make them more noticeable. Il comportamento responsabile della scelta degli elementi da formattare dovrebbe essere mantenuto separato dal comportamento responsabile della formattazione vera e propria degli elementi, poiché si tratta di concetti distinti e solo casualmente correlati tra loro.The behavior responsible for choosing which items to format should be kept separate from the behavior responsible for formatting the items, since these are separate concerns that are only coincidentally related to one another.

Dal punto di vista della progettazione, è possibile costruire in modo logico le applicazioni rispettando questo principio e separando il comportamento di business principale dalla logica dell'interfaccia utente e dell'infrastruttura.Architecturally, applications can be logically built to follow this principle by separating core business behavior from infrastructure and user interface logic. Idealmente, le regole e la logica di business dovrebbero risiedere in un progetto separato e autonomo rispetto agli altri progetti dell'applicazione.Ideally, business rules and logic should reside in a separate project, which should not depend on other projects in the application. Ciò garantisce che il modello di business sia facile da testare e possa evolvere senza essere strettamente associato a dettagli di implementazione di basso livello.This helps ensure that the business model is easy to test and can evolve without being tightly coupled to low-level implementation details. La separazione dei compiti è un fattore chiave nella scelta di utilizzare i livelli nelle architetture delle applicazioni.Separation of concerns is a key consideration behind the use of layers in application architectures.

IncapsulamentoEncapsulation

Parti diverse di un'applicazione devono essere isolate da altre mediante l'incapsulamento.Different parts of an application should use encapsulation to insulate them from other parts of the application. I componenti e i livelli dell'applicazione devono essere in grado di regolare la loro implementazione interna senza interrompere i collaboratori, a patto che i contratti esterni non vengano violati.Application components and layers should be able to adjust their internal implementation without breaking their collaborators as long as external contracts are not violated. Un utilizzo corretto dell'incapsulamento consente di progettare le applicazioni con modularità e un tipo di accoppiamento debole poiché gli oggetti e i pacchetti possono essere sostituiti con implementazioni alternative, purché la stessa interfaccia venga mantenuta.Proper use of encapsulation helps achieve loose coupling and modularity in application designs, since objects and packages can be replaced with alternative implementations so long as the same interface is maintained.

L'incapsulamento delle classi avviene limitando l'accesso esterno allo stato interno della classe.In classes, encapsulation is achieved by limiting outside access to the class's internal state. Se un attore esterno vuole modificare lo stato dell'oggetto, dovrà farlo tramite una funzione ben definita o un setter di proprietà anziché accedendo direttamente allo stato privato dell'oggetto.If an outside actor wants to manipulate the state of the object, it should do so through a well-defined function (or property setter), rather than having direct access to the private state of the object. Analogamente, i componenti dell'applicazione e le applicazioni stesse non devono consentire la modifica diretta del loro stato bensì esporre interfacce ben definite a uso dei collaboratori.Likewise, application components and applications themselves should expose well-defined interfaces for their collaborators to use, rather than allowing their state to be modified directly. Questo accorgimento consente alla struttura interna dell'applicazione di evolversi nel tempo senza comportare l'interruzione dei collaboratori, purché i contratti pubblici vengano mantenuti.This frees the application's internal design to evolve over time without worrying that doing so will break collaborators, so long as the public contracts are maintained.

Inversione delle dipendenzeDependency inversion

La direzione delle dipendenza all'interno dell'applicazione deve puntare verso l'astrazione, non verso i dettagli di implementazione.The direction of dependency within the application should be in the direction of abstraction, not implementation details. La maggior parte delle applicazioni vengono scritte in modo che le dipendenze in fase di compilazione scorrano nella direzione della fase di esecuzione.Most applications are written such that compile-time dependency flows in the direction of runtime execution. Ciò produce un grafico delle dipendenze dirette.This produces a direct dependency graph. Ovvero, se il modulo A chiama una funzione nel modulo B, che a sua volta chiama una funzione nel modulo C, in fase di compilazione A dipenderà da B che dipenderà da C, come illustrato nella figura 4-1.That is, if module A calls a function in module B, which calls a function in module C, then at compile time A will depend on B which will depend on C, as shown in Figure 4-1.

Grafico dipendenze dirette

Figura 4-1.Figure 4-1. Grafico delle dipendenze dirette.Direct dependency graph.

L'applicazione del principio di inversione delle dipendenze consente ad A di chiamare metodi su un'astrazione implementata da B, consentendo ad A di chiamare B in fase di esecuzione, ma a B di dipendere da un'interfaccia controllata da A in fase di compilazione pertanto invertendo le dipendenze tipiche della fase di compilazione.Applying the dependency inversion principle allows A to call methods on an abstraction that B implements, making it possible for A to call B at runtime, but for B to depend on an interface controlled by A at compile time (thus, inverting the typical compile-time dependency). In fase di esecuzione, il flusso di esecuzione del programma rimane invariato, ma l'introduzione di interfacce significa che sarà possibile collegare facilmente diverse implementazioni di tali interfacce.At run time, the flow of program execution remains unchanged, but the introduction of interfaces means that different implementations of these interfaces can easily be plugged in.

Grafico dipendenze invertite

Figura 4-2.Figure 4-2. Grafico delle dipendenze inverse.Inverted dependency graph.

L'inversione delle dipendenze è un elemento chiave per costruire applicazioni con accoppiamento debole, perché consente di scrivere i dettagli dell'implementazione in modo che facciano riferimento ad astrazioni di alto livello e le implementino, piuttosto che il contrario.Dependency inversion is a key part of building loosely-coupled applications, since implementation details can be written to depend on and implement higher level abstractions, rather than the other way around. Le applicazioni così ottenute sono più testabili, modulari e gestibili.The resulting applications are more testable, modular, and maintainable as a result. La pratica dell'inserimento di dipendenze è resa possibile dall'applicazione del principio di inversione delle dipendenze.The practice of dependency injection is made possible by following the dependency inversion principle.

Dipendenze espliciteExplicit dependencies

Le classi e i metodi devono richiedere in modo esplicito gli oggetti in collaborazione di cui necessitano per funzionare correttamente.Methods and classes should explicitly require any collaborating objects they need in order to function correctly. I costruttori di classi forniscono alle classi l'opportunità di individuare gli elementi necessari per essere in uno stato valido e per funzionare correttamente.Class constructors provide an opportunity for classes to identify the things they need in order to be in a valid state and to function properly. Se si definiscono classi che possono essere create e chiamate ma che funzionano correttamente solo in presenza di alcuni componenti di infrastruttura o globali, queste classi si comportano in modo disonesto con i relativi client.If you define classes that can be constructed and called, but which will only function properly if certain global or infrastructure components are in place, these classes are being dishonest with their clients. Il contratto del costruttore indica al client che necessita solo degli elementi specificati, o addirittura di nessun elemento se la classe usa semplicemente un costruttore senza parametri, ma in realtà in fase di esecuzione necessita di qualcos'altro.The constructor contract is telling the client that it only needs the things specified (possibly nothing if the class is just using a parameterless constructor), but then at runtime it turns out the object really did need something else.

Se ci si attiene al principio delle dipendenze esplicite, le classi e i metodi comunicano esattamente ai relativi client ciò di cui hanno bisogno per funzionare.By following the explicit dependencies principle, your classes and methods are being honest with their clients about what they need in order to function. Questo rende il codice più autodocumentato e i contratti di codifica di contratti più intuitivi, perché gli utenti saranno sicuri che nel momento in cui forniscono quello che è richiesto sotto forma di metodi o parametri del costruttore, gli oggetti che stanno usando funzioneranno correttamente in fase di esecuzione.This makes your code more self-documenting and your coding contracts more user-friendly, since users will come to trust that as long as they provide what's required in the form of method or constructor parameters, the objects they're working with will behave correctly at runtime.

Singola responsabilitàSingle responsibility

Il principio di singola responsabilità si applica alla programmazione orientata agli oggetti, ma può anche essere considerato un principio architetturale simile alla separazione dei concetti.The single responsibility principle applies to object-oriented design, but can also be considered as an architectural principle similar to separation of concerns. Afferma che gli oggetti devono avere una sola responsabilità e un solo motivo per cambiare.It states that objects should have only one responsibility and that they should have only one reason to change. Nello specifico, l'unica situazione in cui l'oggetto può cambiare è se il modo in cui svolge la sua unica responsabilità richiede di essere aggiornato.Specifically, the only situation in which the object should change is if the manner in which it performs its one responsibility must be updated. L'applicazione di questo principio favorisce l'elaborazione di sistemi modulari con accoppiamento debole in cui molti nuovi comportamenti possono essere implementati come nuove classi anziché aggiungere ulteriori responsabilità a classi esistenti.Following this principle helps to produce more loosely-coupled and modular systems, since many kinds of new behavior can be implemented as new classes, rather than by adding additional responsibility to existing classes. L'aggiunta di nuove classi è sempre una pratica più sicura rispetto alla modifica delle classi esistenti, dal momento che da queste nuove classi non dipende alcun codice.Adding new classes is always safer than changing existing classes, since no code yet depends on the new classes.

In un'applicazione monolitica, è possibile applicare il principio di singola responsabilità ad alto livello e trasferirlo agli altri livelli dell'applicazione.In a monolithic application, we can apply the single responsibility principle at a high level to the layers in the application. La responsabilità della presentazione deve rimanere nel progetto dell'interfaccia utente, mentre la responsabilità dell'accesso ai dati deve essere mantenuta all'interno di un progetto di infrastruttura.Presentation responsibility should remain in the UI project, while data access responsibility should be kept within an infrastructure project. La logica di business deve essere inclusa nel progetto principale dell'applicazione, dove può essere facilmente testata ed evolvere in modo indipendente da altre responsabilità.Business logic should be kept in the application core project, where it can be easily tested and can evolve independently from other responsibilities.

Quando si applica questo principio all'architettura delle applicazioni portandolo fino all'endpoint logico, si ottengono i microservizi.When this principle is applied to application architecture, and taken to its logical endpoint, you get microservices. Ogni microservizio deve avere una singola responsabilità.A given microservice should have a single responsibility. Se si presenta la necessità di estendere il comportamento di un sistema, è preferibile farlo aggiungendo ulteriori microservizi, anziché aggiungendo responsabilità a un microservizio esistente.If you need to extend the behavior of a system, it's usually better to do it by adding additional microservices, rather than by adding responsibility to an existing one.

Altre informazioni sull'architettura dei microserviziLearn more about microservices architecture

Don't Repeat Yourself (DRY)Don't repeat yourself (DRY)

In base al principio DRY, in un'applicazione va evitata la ripetizione di un comportamento relativo a un particolare concetto perché è una frequente fonte di errori.The application should avoid specifying behavior related to a particular concept in multiple places as this is a frequent source of errors. Prima o poi, una modifica dei requisiti comporterà la modifica di questo comportamento e la probabilità che almeno un'istanza di tale comportamento non venga aggiornata determineranno un comportamento incoerente del sistema.At some point, a change in requirements will require changing this behavior and the likelihood that at least one instance of the behavior will fail to be updated will result in inconsistent behavior of the system.

Anziché duplicare la logica, è preferibile incapsularla in un costrutto di programmazione.Rather than duplicating logic, encapsulate it in a programming construct. Rendere il costrutto l'unica autorità su questo comportamento e fare in modo che qualsiasi altra parte dell'applicazione che richiede questo comportamento utilizzi il nuovo costrutto.Make this construct the single authority over this behavior, and have any other part of the application that requires this behavior use the new construct.

Nota

Evitare di associare tra loro comportamenti che sono solo casualmente ripetitivi.Avoid binding together behavior that is only coincidentally repetitive. Ad esempio, il fatto che due costanti diverse hanno entrambe lo stesso valore non implica che dovrebbe essercene solo una, se concettualmente si riferiscono ad elementi diversi.For example, just because two different constants both have the same value, that doesn't mean you should have only one constant, if conceptually they're referring to different things.

Mancato riconoscimento della persistenzaPersistence ignorance

Il mancato riconoscimento della persistenza riguarda i tipi che devono essere resi persistenti ma il cui codice non è influenzato dalla scelta della tecnologia di persistenza.Persistence ignorance (PI) refers to types that need to be persisted, but whose code is unaffected by the choice of persistence technology. In .NET tali tipi sono a volte definiti Plain Old CLR Object (POCO), in quanto non sono gravati dalla necessità di ereditare da una determinata classe base o di implementare un'interfaccia specifica.Such types in .NET are sometimes referred to as Plain Old CLR Objects (POCOs), because they do not need to inherit from a particular base class or implement a particular interface. Il mancato riconoscimento della persistenza è utile perché consente allo stesso modello di business di essere reso permanente in più modi migliorando la flessibilità dell'applicazione.Persistence ignorance is valuable because it allows the same business model to be persisted in multiple ways, offering additional flexibility to the application. Le scelte di persistenza possono cambiare nel tempo, in funzione della tecnologia del database oppure possono servire ulteriori forme di persistenza oltre a quelle inizialmente presenti nell'applicazione, ad esempio, l'utilizzo di una cache Redis o di Azure DocumentDb oltre a un database relazionale.Persistence choices might change over time, from one database technology to another, or additional forms of persistence might be required in addition to whatever the application started with (for example, using a Redis cache or Azure DocumentDb in addition to a relational database).

Alcuni esempi di violazioni di questo principio:Some examples of violations of this principle include:

  • Una classe base obbligatoria.A required base class.

  • Un'implementazione dell'interfaccia obbligatoria.A required interface implementation.

  • Classi responsabili del proprio salvataggio ad esempio, il criterio del record attivo.Classes responsible for saving themselves (such as the Active Record pattern).

  • Costruttore senza parametri obbligatorio.Required parameterless constructor.

  • Proprietà che richiedono una parola chiave virtuale.Properties requiring virtual keyword.

  • Attributi di persistenza obbligatori.Persistence-specific required attributes.

Il requisito che le classi presentino una o più funzionalità o uno o più comportamenti precedenti rende permanente l'accoppiamento tra i tipi e aggiunge la scelta della tecnologia di persistenza, rendendo più difficoltoso adottare nuove strategie di accesso ai dati in futuro.The requirement that classes have any of the above features or behaviors adds coupling between the types to be persisted and the choice of persistence technology, making it more difficult to adopt new data access strategies in the future.

Contesti limitatiBounded contexts

I contesti limitati sono un criterio centrale nell'approccio Domain-driven design.Bounded contexts are a central pattern in Domain-Driven Design. Rappresentano un modo per gestire la complessità in organizzazioni o applicazioni di grandi dimensioni tramite la suddivisione in moduli concettuali separati.They provide a way of tackling complexity in large applications or organizations by breaking it up into separate conceptual modules. Ogni modulo concettuale rappresenta quindi un contesto separato dagli altri, da qui l'aggettivo limitato, che può evolvere in modo indipendente.Each conceptual module then represents a context which is separated from other contexts (hence, bounded), and can evolve independently. Ogni contesto limitato dovrebbe essere idealmente libero di scegliere i nomi per i concetti al suo interno e dovrebbe avere accesso esclusivo al proprio archivio di persistenza.Each bounded context should ideally be free to choose its own names for concepts within it, and should have exclusive access to its own persistence store.

Come minimo, ogni singola applicazione web deve puntare ad essere il proprio contesto limitato, con un archivio di persistenza per il proprio modello di business, piuttosto che condividere un database con altre applicazioni.At a minimum, individual web applications should strive to be their own bounded context, with their own persistence store for their business model, rather than sharing a database with other applications. La comunicazione tra contesti limitati non avviene tramite un database condiviso bensì tramite interfacce di programmazione che consentono la generazione di eventi e logica di business in risposta alle modifiche che si verificano.Communication between bounded contexts occurs through programmatic interfaces, rather than through a shared database, which allows for business logic and events to take place in response to changes that take place. I contesti limitati sono strettamente mappati a microservizi, anch'essi idealmente implementati come singoli contesti limitati.Bounded contexts map closely to microservices, which also are ideally implemented as their own individual bounded contexts.

Risorse aggiuntiveAdditional resources