Tuesday, 31 May 2016

Architecture - Key Principles and Practices



- Current thinking on architecture assumes that your design will evolve over time and you cannot know everything you need to know up front in order to fully architect your system.

- Your design will generally need to evolve during the implementation stages of the application as you learn more and as you test the design against real world requirements.

- Create your architecture with this evolution in mind so that it will be able to adapt to requirements that are not full known at the start of the design process.

- Do not attempt to over engineer the architecture and do not make assumptions that you cannot verify. Instead, keep your options open for future change.

-Consider using an incremental and iterative approach to refining your architecture.

Consider the following key principles when designing your architecture.
  •  Build to change instead of building to last. Consider how the application may need to change over time to address new requirements and challenges, and build in the flexibility to support this.
  • Model to analyze and reduce risk. Use design tools, modeling systems such as Unified Modeling Language (UML), and visualizations where appropriate to help you capture requirements and architectural and design decisions, and to analyze their impact. However, do not formalize the model to the extent that it suppresses the capability to iterate and adapt the design easily. 
  • Use models and visualizations as a communication and collaboration tool. Efficient communication of the design, the decisions you make, and ongoing changes to the design, is critical to good architecture. Use models, views, and other visualizations of the architecture to communicate and share your design efficiently with all the stakeholders, and to enable rapid communication of changes to the design.
  •  Identify key engineering decisions. Use the information in this guide to understand the key engineering decisions and the areas where mistakes are most often made. Invest in getting these key decisions right the first time so that the design is more flexible and less likely to be broken by changes.

----------------------------------------------------------------------------------------------------------------------

These key principles with help you to create an architecture that adheres to proven principles, minimizes costs and maintenance requirements, and promotes usability and extendibility.
The key principles are:

  • Separation of concerns.Divide your application into distinct features with as little overlap in functionality as possible. The important factor is minimization of interaction points to achieve high cohesion and low coupling. However, separating functionality at the wrong boundaries can result in high coupling and complexity between features even though the contained functionality within a feature does not significantly overlap.
  • Single Responsibility principle.Each component or module should be responsible for only a specific feature or functionality, or aggregation of cohesive functionality.
  • Principle of Least Knowledge (also known as the Law of Demeter or LoD). A component or object should not know about internal details of other components or objects.
  • Don’t repeat yourself (DRY).You should only need to specify intent in one place. For example, in terms of application design, specific functionality should be implemented in only one component; the functionality should not be duplicated in any other component.
  • Minimize upfront design. Only design what is necessary. In some cases, you may require upfront comprehensive design and testing if the cost of development or a failure in the design is very high. In other cases, especially for agile development, you can avoid big design upfront (BDUF). If your application requirements are unclear, or if there is a possibility of the design evolving over time, avoid making a large design effort prematurely. This principle is sometimes known as YAGNI ("You ain’t gonna need it").
----------------------------------------------------------------------------------------------------------------------

The following high level guidelines will help you to consider the wide range of factors that can affect the ease of designing, implementation, deploying, testing, and maintain your application.

Design Practices
  • Keep design patterns consistent within each layer.
    Within a logical layer, where possible, the design of components should be consistent for a particular operation. For example, if you choose to use the Table Data Gateway pattern to create an object that acts as a gateway to tables or views in a database, you should not include another pattern such as Repository, which uses a different paradigm for accessing data and initializing business entities. However, you may need to use different patterns for tasks in a layer that have a large variation in requirements, such as an application that contains business transaction and reporting functionality.

  • Do not duplicate functionality within an application.
    There should be only one component providing a specific functionality—this functionality should not be duplicated in any other component. This makes your components cohesive and makes it easier to optimize the components if a specific feature or functionality changes. Duplication of functionality within an application can make it difficult  to implement changes, decrease clarity, and introduce potential inconsistencies.
  • Prefer composition to inheritance.Wherever possible, use composition over inheritance when reusing functionality because inheritance increases the dependency between parent and child classes, thereby limiting the reuse of child classes. This also reduces the inheritance hierarchies, which can become very difficult to deal with.
  • Establish  a coding style and naming convention for development.
    Check to see if the organization has established coding style and naming standards. If not, you should establish common standards. This provides a consistent model that makes it easier for team members to review code they did not write, which leads to better maintainability.

  • Maintain system quality using automated QA techniques during development. 
    Use unit testing and other automated Quality Analysis techniques, such as dependency analysis and static code analysis, during development. Define clear behavioral and performance metrics for components and sub-systems, and use automated QA tools during the build process to ensure that local design or implementation decisions do not adversely affect the overall system quality.

  • Consider the operation of your application.
    Determine what metrics and operational data are required by the IT infrastructure to ensure the efficient deployment and operation of your application. Designing your application’s components and sub-systems with a clear understanding of their individual operational requirements will significantly ease overall deployment and operation. Use automated QA tools during development to ensure that the correct operational data is provided by your application’s components and sub-systems.

Application Layers 

  • Separate the areas of concern.
    Break your application into distinct features that overlap in functionality as little as possible. The main benefit of this approach is that a feature or functionality can be optimized independently of other features or functionality. In addition, if one feature fails, it will not cause other features to fail as well, and they can run independently of one another. This approach also helps to make the application easier to understand and design, and facilitates management of complex interdependent systems.
  • Be explicit about how layers  communicate with each other.Allowing every layer in an application to communicate with or have dependencies upon all of the other layers will result in a solution that is more challenging to understand and manage. Make explicit decisions about the dependencies between layers and the data flow between them.
  • Use abstraction to implement loose coupling between layers.
    This can be accomplished by defining interface components such as a façade with well known inputs and outputs that translate requests into a format understood by components within the layer. In addition, you can also use Interface types or abstract base classes to define a common interface or shared abstraction (dependency inversion) that must be implemented by interface components.
  • Do not mix different types of components in the same logical layer.Start by identifying different areas of concern, and then group components associated with each area of concern into logical layers. For example, the UI layer should not contain business processing components, but instead should contain components used to handle user input and process user requests.
  • Keep the data format consistent within a layer or component.
    Mixing data formats will make the application more difficult to implement, extend, and maintain. Every time you need to convert data from one format to another, you are required to implement translation code to perform the operation and incur a processing overhead. Components,Modules, and Functions
  • A component or an object should not rely on internal details of other components or objects.Each component or object should call a method of another object or component, and that method should have information about how to process the request and, if appropriate, how to route it to appropriate sub-components or other components. This helps to create an application that is more maintainable and adaptable.

  • Do not overload the functionality of a component. For example, a UI processing component should not contain data access code or attempt to provide additional functionality. Overloaded components often have many functions and properties providing business functionality mixed with crosscutting functionality such as logging and exception handling. The result is a design that is very error prone and difficult to maintain. Applying the single responsibility and separation of concerns principles will help you to avoid this.
  • Understand how components will communicate with each other.
    This requires an understanding of the deployment scenarios your application must support. You must determine if all components will run within the same process, or if communication across physical or process boundaries must be supported— perhaps by implementing message-based interfaces.
  •  Keep crosscutting code abstracted from the application business logic as far as possible.
    Crosscutting code refers to code related to security, communications, or operational management such as logging and instrumentation. Mixing the code that implements these functions with the business logic can lead to a design that is difficult to extend and maintain. Changes to the crosscutting code require touching all of the business logic code that is mixed with the crosscutting code. Consider using frameworks and techniques (such as aspect oriented programming) that can help to manage crosscutting concerns.
  • Define a clear contract for components.Components, modules, and functions should define a contract or interface specification that describes their usage and behavior clearly. The contract should describe how other components can access the internal functionality of the component, module, or function; and the behavior of that functionality in terms of pre-conditions, post-conditions, side effects, exceptions, performance characteristics, and other factors.








    No comments:

    Post a Comment