Application Architecture Design Principles
Strategy combines processes, principles, and patterns. It's a daunting task requiring organizational commitment. Learn a coordinated, cross-cutting approach.
Join the DZone community and get the full member experience.Join For Free
This is an article from DZone's 2023 Software Integration Trend Report.
Read the Report
Designing an application architecture is never complete. Regularly, all decisions and components need to be reviewed, validated, and possibly updated. Stakeholders require that complex applications be delivered more quickly. It's a challenge for even the most senior technologists. A strategy is required, and it needs to be nimble. Strategy combines processes, which aid in keeping a team focused, and principles and patterns, which provide best practices for implementation. Regardless, it's a daunting task requiring organizational commitment.
Development, Design, and Architectural Processes
Applications developed without any process is chaos. A team that invents their own process and sticks to it is much better off than a team using no process. At the same time, holding a project hostage to a process can be just as detrimental. Best practices and patterns are developed over multiple years of teams looking for better ways to produce quality software in a timely manner. Processes are the codification of the best practices and patterns. By codifying best practices and patterns into processes, the processes can be scaled out to more organizations and teams.
For example, when an organization selects a development process, a senior leader may ascribe to a test-first development pattern. It becomes much easier for an organization to adopt a pattern by finding a process that outlines how the pattern is organizationally implemented. In the case of the test-first development pattern, test-driven development (TDD) may be selected as the development process.
Another technical leader in the same organization may choose to lead their team using domain-driven design (DDD), a pattern by which software design is communicated across technical teams as well as other stakeholders. Can these two design philosophies coexist? Yes. They can. Here, TDD defines how software is constructed while DDD defines the concepts that describe the software.
Avoiding Dogmatic Adherence
Regardless of development, design, or architectural process, it's key that strict adherence to a given process does not become the end goal. Unfortunately, this happens more often than it should. Remember that the intent of a process is to codify best practices in a way that allows teams to scale using the same goals and objectives.
To that end, when implementing processes, here are some points to consider:
- There's no one size fits all.
- Allow culture to mold the process.
- Maturity takes time.
- Keep focused on what you're really doing — building quality software in a timely manner.
Software architecture can be designed, articulated, and implemented in several ways. Regardless of approach, most software architecture plans address two key points: simplicity and evolution. Simplicity is a relative term in that an architectural approach needs to be easily understood within the context of the business domain. Team members should look at an architectural plan and say, "Of course, that's the obvious design." It may have taken several months to develop the plan, but a team responding in this manner is a sign that the plan is on the right track. Evolution is very important and can be the trickiest aspect of an architectural plan. It may sound difficult, but an architectural plan should be able to last ten-plus years. That may be challenging to comprehend, but with the right design principles and patterns in place, it's not as challenging as one might think.
At its core, good software architecture does its best to not paint itself into a corner. Figure 1 below contains no new revelations. However, each point is critical to a lasting software architecture:
- Building architecture that endures. This is the end goal. It entails using patterns that support the remaining points.
- Multiple platform and deployment support. The key here is that what exists today will very likely look different five years from now. An application needs to be readily able to adapt to changes in platform and deployment models, wherever the future takes it.
- Enforceable, standard patterns and compliance. Not that there's nothing new, but the software industry has decades of patterns to adopt and compliance initiatives to adhere to. Changes in both are gradual, so keeping an eye on the horizon is important.
- Reuse and extensibility from the ground up. Implementation patterns for reuse and extensibility will vary, but these points have been building blocks for many years.
- Collaboration with independent, external modules. The era of microservices helps enforce this principle. Watch for integrations that get convoluted. That is a red flag to the architecture.
- Evolutionary, module compatibility and upgrade paths. Everything in a software's architecture will evolve. Consider how compatibility and upgrades are managed.
- Design for obsolescence. Understand that many components within a software's architecture will eventually need to be totally replaced. At the beginning of each project or milestone, ask the question, "How much code are we getting rid of this release?" The effect of regular code pruning is no different than the effect of pruning plants.
Figure 1: Key architectural principles
Developing microservices is a combination of following these key architectural principles along with segmenting components into areas of responsibility. Microservices provide a unit of business functionality. Alone, they provide little value to a business. It's in the assembly of and integration with other microservices that business value is realized.
Good microservices assembly and integration implementations follow a multi-layered approach.
Horizontal and Vertical Slices
Simply stated, slicing an application is about keeping things where they belong. In addition to adhering to relevant design patterns in a codebase, slicing an application applies the same patterns at the application level. Consider an application architecture as depicted by a Lego® brick structure in the figure below:
Figure 2: Microservices architecture
Each section of bricks is separated by that thin Lego® brick, indicating a strict separation of responsibility between each layer. Layers interact only through provided contracts/interfaces. Figure 2 depicts three layers with each having a distinct purpose. Whether it be integration with devices such as a laptop or tablet, or microservices integrating with other microservices, the point at which service requests are received remains logically the same. Here, there are several entry points ranging from web services and messaging services to an event bus.
Horizontal slices of an application architecture are layers where, starting from the bottom, each layer provides services to the next layer. Typically, each layer of the stack refines the scope of underlying services to meet business use case logic. There can be no assumptions by services in lower layers on how above services interact with them. As mentioned, this is done with welldefined contracts.
In addition, services within a layer interact with one another through that layer's contracts. Maintaining strict adherence to contracts allows components at each layer to be replaced with new or enhanced versions with no disruption in interoperability.
Figure 3: Horizontal slices
Vertical slices are where everything comes together. A vertical slice is what delivers an application business objective. A vertical slice starts with an entry point that drills through the entire architecture. As depicted in Figure 4, business services can be exposed in multiple ways. Entry points are commonly exposed through some type of network protocol.
However, there are cases where a network protocol doesn't suffice. In these cases, a business service may offer a native library supporting direct integration. Regardless of the use case, strict adherence to contracts must be maintained.
Figure 4: Vertical slices
Obvious, Yet Challenging
Microservices have become a predominant pattern by which large applications are assembled. Each microservice is concerned with a very specific set of functionalities. By their very nature, microservices dictate that well-defined contracts are in place, with which other microservices and systems can integrate. Microservices that are designed and implemented for cloud-native deployments can leverage cloud-native infrastructure to support several of the patterns discussed.
The patterns and diagrams presented here will look obvious to most. As mentioned, good architecture is "obvious." The challenge is adhering to it. Often, the biggest enemy to adherence is time. The pressure to meet delivery deadlines is real and where cracks in the contracts appear. Given the multiple factors in play, there are times when compromises need to be made. Make a note, create a ticket, add a comment, and leave a trail so that the compromise gets addressed as quickly as possible.
Well-designed application architecture married with good processes supports longevity, which from a business perspective provides an excellent return on investment. Greenfield opportunities are fewer than evolving existing applications. Regardless, bringing this all to bear can look intimidating. The key is to start somewhere. As a team, develop a plan and "make it so"!
This is an article from DZone's 2023 Software Integration Trend Report.
Read the Report
Opinions expressed by DZone contributors are their own.
MLOps: Definition, Importance, and Implementation
The Role of AI and Programming in the Gaming Industry: A Look Beyond the Tables
Building the World's Most Resilient To-Do List Application With Node.js, K8s, and Distributed SQL
Which Is Better for IoT: Azure RTOS or FreeRTOS?