Principios de la arquitecturaArchitectural principles

"Si los constructores construyeran los edificios como los programadores escriben los programas, el primer pájaro carpintero que apareciera destruiría la civilización"."If builders built buildings the way programmers wrote programs, then the first woodpecker that came along would destroy civilization."
- Gerald Weinberg- Gerald Weinberg

Las soluciones de software se deben diseñar y crear con el mantenimiento en mente.You should architect and design software solutions with maintainability in mind. Los principios que se describen en esta sección le ayudarán a tomar decisiones arquitectónicas que darán como resultado aplicaciones limpias y fácil de mantener.The principles outlined in this section can help guide you toward architectural decisions that will result in clean, maintainable applications. Por lo general, estos principios le ayudarán a compilar aplicaciones a partir de componentes discretos que no están estrechamente relacionados con otras partes de la aplicación, sino que se comunican a través de interfaces explícitas o sistemas de mensajería.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.

Principios de diseño comunesCommon design principles

Separación de interesesSeparation of concerns

Un principio fundamental durante el desarrollo es la separación de intereses.A guiding principle when developing is Separation of Concerns. Este principio afirma que el software se debe separar en función de los tipos de trabajo que realiza.This principle asserts that software should be separated based on the kinds of work it performs. Por ejemplo, considere una aplicación que incluye lógica para identificar los elementos de interés que se van a mostrar al usuario y que da formato a estos elementos de una manera determinada para hacerlos más evidentes.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. El comportamiento responsable de elegir a qué elementos dar formato se debe mantener separado del comportamiento responsable de dar formato a los elementos, puesto que estos son intereses independientes que solo se relacionan entre sí de manera casual.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.

Arquitectónicamente, las aplicaciones se pueden crear de forma lógica para seguir este principio mediante la separación del comportamiento de negocios principal de la lógica de la interfaz de usuario y la infraestructura.Architecturally, applications can be logically built to follow this principle by separating core business behavior from infrastructure and user interface logic. Idealmente, la lógica y las reglas de negocios deben residir en un proyecto independiente, que no debería depender de otros proyectos de la aplicación.Ideally, business rules and logic should reside in a separate project, which should not depend on other projects in the application. Esto ayuda a garantizar que el modelo de negocio sea fácil de probar y pueda evolucionar sin que se esté estrechamente unido a detalles de implementación de bajo nivel.This helps ensure that the business model is easy to test and can evolve without being tightly coupled to low-level implementation details. La separación de intereses es una consideración clave detrás del uso de capas en arquitecturas de aplicación.Separation of concerns is a key consideration behind the use of layers in application architectures.

EncapsulaciónEncapsulation

Las diferentes partes de una aplicación deben usar la encapsulación para aislarse de otras partes de la aplicación.Different parts of an application should use encapsulation to insulate them from other parts of the application. Las capas y los componentes de la aplicación deben poder ajustar su implementación interna sin interrumpir a sus colaboradores mientras no se infrinjan los externos.Application components and layers should be able to adjust their internal implementation without breaking their collaborators as long as external contracts are not violated. El uso correcto de la encapsulación contribuye a lograr el acoplamiento flexible y la modularidad en los diseños de aplicaciones, ya que los objetos y paquetes se pueden reemplazar con implementaciones alternativas, siempre y cuando se mantenga la misma interfaz.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.

En las clases, la encapsulación se logra mediante la limitación del acceso externo al estado interno de la clase.In classes, encapsulation is achieved by limiting outside access to the class's internal state. Si un actor externo quiere manipular el estado del objeto, deberá hacerlo a través de una función bien definida (o un establecedor de propiedades), en lugar de tener acceso directo al estado privado del objeto.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. Del mismo modo, los componentes de aplicación y las propias aplicaciones deben exponer interfaces bien definidas para que sus colaboradores las usen, en lugar de permitir que su estado se modifique directamente.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. Esto libera el diseño interno de la aplicación para que evolucione con el tiempo sin tener que preocuparse de si al hacerlo afectará a los colaboradores, siempre y cuando se mantengan los contratos públicos.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.

Inversión de dependenciasDependency inversion

La dirección de dependencia dentro de la aplicación debe estar en la dirección de la abstracción, no de los detalles de implementación.The direction of dependency within the application should be in the direction of abstraction, not implementation details. La mayoría de las aplicaciones se escriben de manera que la dependencia de tiempo de compilación fluya en la dirección de ejecución del tiempo de ejecución.Most applications are written such that compile-time dependency flows in the direction of runtime execution. Esto genera un gráfico de dependencias directas.This produces a direct dependency graph. Es decir, si el módulo A llama a una función en el módulo B, que llama a una función en el módulo C, en tiempo de compilación A dependerá de B que dependerá de C, como se muestra en la 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.

Gráfico de dependencias directas

Figura 4-1.Figure 4-1. Gráfico de dependencias directas.Direct dependency graph.

Aplicar el principio de inversión de dependencias permite que A llame a métodos en una abstracción que implementa B, lo que hace posible que A llame a B en tiempo de ejecución, pero que B dependa de una interfaz controlada por A en tiempo de compilación (por tanto, se invierte la dependencia de tiempo de compilación normal).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). En tiempo de ejecución, el flujo de ejecución del programa no cambia, pero la introducción de interfaces significa que se pueden conectar fácilmente otras implementaciones de estas interfaces.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.

Gráfico de dependencias invertidas

Figura 4-2.Figure 4-2. Gráfico de dependencias invertidas.Inverted dependency graph.

La Inversión de dependencias es una parte fundamental de la creación de aplicaciones de acoplamiento flexible, ya que se pueden escribir detalles de implementación de los que depender e implementar abstracciones de nivel superior, en lugar de hacerlo al 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. Como resultado, las aplicaciones son modulares y más fáciles de probar y mantener.The resulting applications are more testable, modular, and maintainable as a result. La práctica de la inserción de dependencias es posible si se sigue el principio de inversión de dependencias.The practice of dependency injection is made possible by following the dependency inversion principle.

Dependencias explícitasExplicit dependencies

Los métodos y las clases deben requerir explícitamente todos los objetos de colaboración que necesiten para funcionar correctamente.Methods and classes should explicitly require any collaborating objects they need in order to function correctly. Los constructores de clases proporcionan una oportunidad para que las clases identifiquen lo que necesitan para poder tener un estado válido y funcionar correctamente.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. Si se definen clases que se pueden construir y llamar, pero que solo funcionarán correctamente si existen determinados componentes globales o de infraestructura, estas clases no estarán siendo honestas con sus clientes.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. El contrato de constructor indica al cliente que solo necesita los elementos especificados (posiblemente ninguno si la clase solo usa un constructor sin parámetros), pero después, en tiempo de ejecución, en realidad el objeto necesita algo más.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.

Si siguen el principio de dependencias explícitas, las clases y métodos estarán siendo sinceros con sus clientes con respecto a lo que necesitan para poder funcionar.By following the explicit dependencies principle, your classes and methods are being honest with their clients about what they need in order to function. Esto hace que el código sea más autoexplicativo y los contratos de codificación más fáciles de usar, puesto que los usuarios confiarán en eso siempre que proporcionen lo que se necesita en forma de parámetros de método o constructor, los objetos con los trabajan se comportarán correctamente en tiempo de ejecución.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.

Responsabilidad únicaSingle responsibility

El principio de responsabilidad única se aplica al diseño orientado a objetos, pero también se puede considerar como un principio de arquitectura similar a la separación de intereses.The single responsibility principle applies to object-oriented design, but can also be considered as an architectural principle similar to separation of concerns. Indica que los objetos solo deben tener una responsabilidad y solo una razón para cambiar.It states that objects should have only one responsibility and that they should have only one reason to change. En concreto, la única situación en la que el objeto debe cambiar es si hay que actualizar la manera en la que lleva a cabo su única responsabilidad.Specifically, the only situation in which the object should change is if the manner in which it performs its one responsibility must be updated. El seguimiento de este principio ayuda a generar sistemas más modulares y de acoplamiento flexible, dado que muchos tipos de comportamientos nuevos se pueden implementar como clases nuevas, en lugar de mediante la adición de responsabilidad adicional a las clases existentes.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. Agregar clases nuevas siempre es más seguro que cambiar las existentes, puesto que todavía no hay código que dependa de las clases nuevas.Adding new classes is always safer than changing existing classes, since no code yet depends on the new classes.

En una aplicación monolítica, se puede aplicar el principio de responsabilidad única en un nivel general a las capas de la aplicación.In a monolithic application, we can apply the single responsibility principle at a high level to the layers in the application. La responsabilidad de la presentación debe mantenerse en el proyecto de la interfaz de usuario, mientras que la responsabilidad de acceso a los datos se debe mantener en un proyecto de infraestructura.Presentation responsibility should remain in the UI project, while data access responsibility should be kept within an infrastructure project. La lógica de negocios se debe mantener en el proyecto principal de la aplicación, donde se puede probar fácilmente y puede evolucionar con independencia de otras responsabilidades.Business logic should be kept in the application core project, where it can be easily tested and can evolve independently from other responsibilities.

Cuando este principio se aplica a la arquitectura de la aplicación y se lleva a su punto de conexión lógico, se obtienen microservicios.When this principle is applied to application architecture, and taken to its logical endpoint, you get microservices. Un microservicio determinado debe tener una sola responsabilidad.A given microservice should have a single responsibility. Si es necesario extender el comportamiento de un sistema, es mejor hacerlo agregando otros microservicios, en lugar de agregar responsabilidad a uno ya existente.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.

Más información sobre la arquitectura de microserviciosLearn more about microservices architecture

Una vez y solo una (DRY)Don't repeat yourself (DRY)

La aplicación debe evitar especificar el comportamiento relacionado con un determinado concepto en varios lugares, ya que esto es una fuente de errores frecuente.The application should avoid specifying behavior related to a particular concept in multiple places as this is a frequent source of errors. En algún momento, un cambio en los requisitos hará que sea necesario cambiar este comportamiento y la probabilidad de que se produzca un error al actualizar al menos una instancia del comportamiento dará como resultado el comportamiento incoherente 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.

En lugar de duplicar la lógica, se puede encapsular en una construcción de programación.Rather than duplicating logic, encapsulate it in a programming construct. Convierta esta construcción en la única autoridad sobre este comportamiento y haga que cualquier otro elemento de la aplicación que requiera este comportamiento use la nueva construcción.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

Evite el enlace conjunto del comportamiento que solo se repita de forma causal.Avoid binding together behavior that is only coincidentally repetitive. Por ejemplo, solo porque dos constantes diferentes tengan el mismo valor no significa que debería tener solo una constante, si conceptualmente se refieren a cosas diferentes.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.

Omisión de persistenciaPersistence ignorance

La Omisión de persistencia (PI) hace referencia a los tipos que se deben conservar, pero cuyo código no se ve afectado por la elección de la tecnología de persistencia.Persistence ignorance (PI) refers to types that need to be persisted, but whose code is unaffected by the choice of persistence technology. En .NET, estos tipos a veces se denominan objeto CRL estándar (POCO), ya que no necesitan heredar de una clase base concreta o implementar una interfaz determinada.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. La omisión de persistencia es útil porque permite conservar el mismo modelo de negocio de varias formas, lo que ofrece flexibilidad adicional a la aplicación.Persistence ignorance is valuable because it allows the same business model to be persisted in multiple ways, offering additional flexibility to the application. Es posible que las opciones de persistencia cambien con el tiempo, de una tecnología de base de datos a otra, o bien que se necesiten otras formas de persistencia además de las iniciales de la aplicación (por ejemplo, el uso de una caché en Redis o Azure Cosmos DB además de un base de datos relacional).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 Cosmos DB in addition to a relational database).

Algunos ejemplos de las infracciones de este principio son estos:Some examples of violations of this principle include:

  • Una clase base requerida.A required base class.

  • Una implementación de interfaz requerida.A required interface implementation.

  • Clases responsables de guardarse a sí mismas (por ejemplo, el patrón de registro activo).Classes responsible for saving themselves (such as the Active Record pattern).

  • Constructor sin parámetros requerido.Required parameterless constructor.

  • Propiedades que requieren la palabra clave virtual.Properties requiring virtual keyword.

  • Atributos requeridos específicos de la persistencia.Persistence-specific required attributes.

El requisito de que las clases tengan cualquiera de las características o comportamientos anteriores agrega acoplamiento entre los tipos que se deben conservar y la elección de la tecnología de persistencia, lo que dificulta la adopción de nuevas estrategias de acceso de datos en el 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.

Contextos delimitadosBounded contexts

Los contextos delimitados son un patrón esencial en el diseño controlado por dominios.Bounded contexts are a central pattern in Domain-Driven Design. Proporcionan una manera de abordar la complejidad en organizaciones o aplicaciones de gran tamaño dividiéndola en módulos conceptuales independientes.They provide a way of tackling complexity in large applications or organizations by breaking it up into separate conceptual modules. Después, cada módulo conceptual representa un contexto que está separado de otros contextos (por tanto, delimitado) y que puede evolucionar independientemente.Each conceptual module then represents a context which is separated from other contexts (hence, bounded), and can evolve independently. Idealmente, cada contexto delimitado debería poder elegir sus propios nombres para los conceptos que contiene y tener acceso exclusivo a su propio almacén de persistencia.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.

Como mínimo, las aplicaciones web individuales deberían intentar ser su propio contexto delimitado, con su propio almacén de persistencia para su modelo de negocios, en lugar de compartir una base de datos con otras aplicaciones.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 comunicación entre los contextos delimitados se realiza a través de interfaces de programación, en lugar de una base de datos compartida, lo que permite que la lógica de negocios y los eventos se produzcan en respuesta a los cambios que tienen lugar.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. Los contextos delimitados se asignan estrechamente a los microservicios que, idealmente, también se implementan como sus propios contextos delimitados individuales.Bounded contexts map closely to microservices, which also are ideally implemented as their own individual bounded contexts.

Recursos adicionalesAdditional resources