As an application architect, when we face any application project, we can’t escape the client/middleware/server curse.
Even if we try the monolithic one-tier architecture, we still have all sorts of servers to deal with (like database servers, to name just one example), as well as middleware to deal with (data access layers, also just to give an example).
This curse has many implications and potential consequences to us and to our design.
What type of architecture should we propose, so today’s design does not compromise tomorrow’s changes?
How do we design, so there is a balance between flexibility and sound architecture?
How to design in such a way that our architecture can attain solidness without rigidness?
Well, the very short version of the answer to all these questions is: abstraction!
Great!, but how do we ‘put’ abstraction into our design?
And what is abstraction anyway, in terms of OO application architecture?
To start with, the need to apply abstraction as a way to solve this design problem is the main reason why we use Object Orientation in the first place.
We use Object Oriented Design in combo with Object Oriented languages just to be able to apply abstraction.
When it comes to Application Architecture, abstraction means to express our design in vague terms, instead of clearly cut-off terms.
We could make our design more flexible to future, unforeseen changes if the design itself was expressed and implemented in vague terms, instead of clearly cut-off terms.
To express our design in vague terms allows us to delay design, implementation and deployment decisions, and this is a really important tool for an architect.
But if it all comes to delaying a design, implementation or deployment decision, we could just use (very) late binding and that would be it!
Well, even though late binding is a practical solution to delay a decision to the very last moment, it does so at the expense of a marked degradation of performance.
So, what could we do?
How do we grab abstraction and delay decisions, without compromising performance and sound design?
Enter Design Patterns.
Design Patterns help us, like no other design tool, to separate software use from software implementation.
DPs help us design our solution as a layered expression of the problem domain.
The closer the component or software layer is to the actual users of the software, the closer is the design of that software layer to be expressed in conceptual, functional terms (that is, implementation independent).
The infrastructure components can also designed by this very same principle, if we consider what is "functional" when it comes to infrastructure.
For instance, if we have to implement an infrastructure component that provides e-mail services, e-mail accounts, server names, e-mail protocols, recipients, sender, etc., are "functional" requirements of any e-mail service, even though it is more implementation-specific than customer id, or product id.
DPs help us mainly at the ‘thinking’ level.
With DPs in mind, we ‘think’ our designs and architectures from an entirely different perspective.
DPs, in terms of conceptual design, help us realize that ‘Simple Is Beautiful’.
All software that we design is a component in this layered solution, and it is at least a client of another component.
Any component that we design should be thought as a services provider to its clients.
This is true to all software that we design, even the user interface layer: it provides user interface services to the users, its clients.
Each of the services provided by a given component must be thought and designed in functional terms, instead of implementation terms: what service does it provide, and not how is this service provided?
From this perspective, the most important part of each service provided by a component is the public interface of the service, the contract between consumer and provider.
Each and all components that we design, as a consumer of any service, must be thought and designed only in terms of the service that they consume and not in terms of the component that they consume from.
That is, the client should only care about the interface or contract of the service, and disregard completely the component that provides the service.
The GoF book maxim that states this design principle is: Program to an interface, not an implementation (page 18).
This principle gives us many benefits in terms of design flexibility and solid architectures.
Any given Design Pattern and design principle that we use must be thought in such a way that it maximizes the aforementioned benefits of abstraction.
DPs como una forma de abstracción
Como arquitectos de aplicaciones, cuando enfrentamos un proyecto de desarrollo de una aplicación, no podemos escapar a la maldición cliente/middleware/servidor.
Aun en el caso que usemos la arquitectura monolítica de una única capa, todavía tenemos que lidiar con todo tipo de servers (tal como servers de base de datos, por mencionar un ejemplo), al igual que con diversas capas de middleware (como la capa de acceso a datos, por nombrar un ejemplo).
Esta maldición nos plantea diversas implicancias y consecuencias tanto a nosotros como a nuestro diseño.
Qué tipo de arquitectura debemos proponer, de tal forma que el diseño de hoy no comprometa los cambios del día de mañana?
Cómo diseñar, de tal forma que haya un balance entre flexibilidad y una sólida arquitectura?
Cómo diseñar de tal forma que nuestra arquitectura pueda adquirir solidez sin rigidez?
Bueno, la versión muy corta de la respuesta a todas estas preguntas es: abstracción!
Genial!, pero como ‘introducimos’ abstracción en nuestro diseño?
Y que es abstracción al fin, en términos de una arquitectura de aplicaciones OO?
Para empezar, la necesidad de aplicar abstracción como forma de resolver este problema de diseño es la razón principal por la que usamos Orientación a Objetos en primer lugar.
Usamos Diseño Orientado a Objetos en combinación con lenguajes Orientados a Objetos con el solo propósito de aplicar abstracción.
En relación con la Arquitectura de Aplicaciones, abstracción implica expresar nuestro diseño en términos ambiguos, en vez de usar términos claramente definidos.
Nosotros podríamos hacer nuestro diseño más flexible a cambios futuros no previstos si el diseño en si estuviese expresado en términos ambiguos, en vez de términos claramente definidos.
El expresar nuestro diseño de esta forma nos permite retrasar decisiones de diseño, de implementación y de despliegue de la aplicación, y esto resulta ser una herramienta muy importante para un arquitecto.
Pero si todo se reduce a retrasar una decisión de diseño, de implementación o de despliegue, podríamos usar (very) late binding y listo! Bueno, si bien late binding es una solución práctica para retrazar una decisión hasta el ultimo momento posible, lo logra a expensas de una degradación de la performance.
Por lo tanto, que podríamos hacer? Cómo poder alcanzar la abstracción y retrasar las decisiones a la vez, sin comprometer la performance ni un sólido diseño?
Es aquí donde entran los Patrones de Diseño en escena (muy, pero muy, libre traducción de la versión en ingles, pero que linda y correcta suena!!!).
Los DPs nos ayudan, como ninguna otra herramienta de diseño podría hacerlo, al separar el uso del software de la implementación del mismo (esto es, separar uso en el cliente de implementación en el componente server, nuevamente, traducción libre, con la libertad que solo el propio autor de la idea puede tener).
Los DPs nos ayudan a diseñar nuestra solución como una expresión estratificada del dominio de problema (OK, no te gusta mi traducción, entonces te desafío a traducir layered, a ver si te la bancas?)
Cuanto más cerca esta la capa de software de los verdaderos usuarios del mismo, tanto mas cerca esta la misma de ser expresada en términos conceptuales, funcionales (esto es, independientes de la implementación).
Los componentes de infraestructura pueden ser diseñados también de acuerdo con este mismo principio, si consideramos que significa ‘funcional’ cuando se trata de infraestructura.
Por ejemplo, si tenemos que implementar un componente de infraestructura que provee servicios de e-mail, en tal caso cuentas de e-mail, nombres de servers de mail, protocolos de mail, receptores, remitente, etc., son requerimientos ‘funcionales’ de cualquier servicio de e-mail, aun cuando resultan ser mas dependientes de implementación que un ‘customer id’ o un ‘product id’ (otra vez mas, libre pero me parece correcta la traducción, digo: bueno, viejo, yo lo escribo y lo traduzco como mejor me parece! so what?).
Los DPs nos ayudan en la forma como ‘pensamos’ un diseño (nuevamente, … mejor me callo!)
Teniendo los DPs en mente, ‘pensamos’ nuestros diseños y arquitecturas desde una perspectiva totalmente diferente.
Los DPs, en términos de un diseño conceptual, nos ayudan a descubrir que ‘Lo Simple es Bello’.
Todo software que diseñamos es un componente en esta solución en capas, donde como mínimo, cada componente es cliente de otro componente.
Cada componente que diseñamos debería ser pensado como un proveedor de servicios de sus clientes.
Esto es cierto para todo software que diseñemos, inclusive para la capa de interfase de usuario: la misma provee servicios de interfase de usuario para los usuarios, sus clientes.
Cada servicio provisto por un componente dado debe ser pensado y diseñado en términos funcionales, y no en términos de implementación, por lo que la pregunta es: que es lo que el servicio provee, en vez de como debe ser provisto el servicio?
Desde esta perspectiva, lo más importante de cada servicio provisto por un componente es la interfase pública del mismo, el contrato entre consumidor y proveedor.
Todos y cada uno de los componentes que diseñamos, como consumidores de un servicio, deben ser pensados y diseñados solo en términos del servicio que consumen y nunca en términos del componente del cual consumen.
Esto es, el cliente solo debería interesarle la interfase o contrato del servicio, y no tener en cuenta para nada el componente circunstancial que provee el servicio.
La máxima del libro GoF que destaca este principio de diseño es: Programe para la interfase, no para la implementación (pagina 18, del original en ingles).
Este principio nos aporta muchos beneficios en términos de flexibilidad de diseño y de arquitecturas sólidas.
Cualquier DP dado y principio de diseño que usamos debe ser pensado de tal forma que maximice los beneficios ya mencionados de la abstracción.