Practical Taxonomy of Software Design

Understanding the differences between software architecture, architectural design, styles, patterns, design approaches, and principles is crucial for making thoughtful decisions.

Unfortunately, these terms are often mixed up or used as if they mean the same thing. This article explains what each term means, where it fits in the design hierarchy, and how they work together to shape systems and architectures.

Architecture: The structure
Software architecture refers to the basic structure of a system. It includes its components, their relationships with each other and the environment, and the principles that guide its design and evolution[1]. Here, a component is defined as a unit of composition at any scale, for example, a microservice or an object-oriented class. The architecture specifies how these components are composed to meet both functional and non-functional needs.

Common architectural forms include monoliths (a single deployable unit), modular monoliths (a monolith with strong internal modularity), microservices (broken down into independently deployable services), and serverless (event-driven functions managed by a cloud provider).

Architectural design: Decision-making process
Architectural design focuses on making early, impactful decisions that shape the system’s architecture. It involves recognizing, planning, and assessing components and their interactions to create an architecture that meets stakeholder requirements, including functionality, scalability, reliability, and other quality traits. This may involve choosing between a modular monolith and microservices, or deciding whether to take an event-driven approach for loose coupling.

Architectural styles: Guiding philosophies
An architectural style consists of a high-level set of constraints that determines how components are arranged, related, and interact. These constraints shape system architectures and influence the choice of suitable architectural patterns.

For example, the REST (Representational State Transfer) architectural style, defined in Roy Fielding’s dissertation, enforces criteria such as statelessness, a uniform interface, separation between client and server, and cacheability.

Other notable styles include RPC (Remote Procedure Call), which describes system interactions as invocations of remote procedures, and GraphQL, a client-driven style that allows clients to specify the exact data they need through one endpoint. This shifts the responsibility for data composition from the server to the client.

It's important to note that gRPC is not an architectural style; it is a high-performance framework that uses Protocol Buffers and HTTP/2 to implement the RPC style. Similarly, Kafka is not a style; it is a message broker used to implement event-driven architectural styles. Kubernetes is also not a style; it is a container orchestration platform often used to manage microservice architectures. Entity Framework and Dapper are also not styles - they are object relational mappers (ORMs) that maps datasets to objects.

Rule of Thumb: If it defines how systems communicate globally (e.g., REST, RPC), it is a Style.

Architectural patterns: Reusable solutions
Architectural patterns are tested, reusable solutions for common structural issues in software design. They provide templates for organizing components and their interactions within a certain context.

Patterns work at various levels of detail. For instance, the layered pattern divides an application into horizontal tiers (like presentation, business logic, and data access), while Model-View-Controller (MVC) separates data, presentation, and control logic within a user-facing application.

Patterns often reflect the principles of an architectural style, such as using CQRS in an event-driven system or applying the Backend-for-Frontend (BFF) pattern in a microservices architecture.

Rule of Thumb: If it defines how you organize internal responsibilities (e.g., CQRS, BFF), it is a Pattern.

Quick Example
To visualize the hierarchy before we dive deeper: Imagine a simple e-commerce API. You choose a Microservice Architecture. You enforce a REST Style for communication. Inside the service, you use the Layered Pattern to separate logic from data. This structure is then refined using Design Approaches and Principles.

Design approach: Guiding development
While architectural design determines a system’s structure, a design approach guides how developers solve problems and build software solutions. For instance, Domain-Driven Design (DDD) offers techniques for aligning software models with business domains. It emphasizes bounded contexts, a shared vocabulary, and strategic design. DDD is particularly useful for designing complex systems like microservices or modular monoliths, where clear domain boundaries matter.

Design principles: Guidelines for code-level design
Design principles are basic guidelines that inform detailed design choices. For example, the SOLID principles promote modularity, testability, and maintainability in object-oriented systems. These principles support broader concerns like Clean Architecture, which enforces separation of concerns through concentric layers (entities, use cases, interface adapters, frameworks), ensuring that business logic remains independent from external factors like databases or user interfaces.

Examples
Domain-Driven Design (DDD) is not an architecture; it is a design approach focused on modeling software around business domains. SOLID is a set of object-oriented design principles, and Test-Driven Development (TDD) is a development practice.

These can be integrated with architectural concerns, for example, using DDD to design a system made up of microservices, structured using an event-driven architectural style, implemented with TDD and SOLID, and organized internally using Clean Architecture.

Legacy modernization might use the Strangler Fig pattern, while client-specific needs could be met with the Backend-for-Frontend pattern.

Conclusion
Software architecture outlines the system’s structure. Architectural design shapes that structure. Architectural styles provide high-level constraints, patterns offer reliable structural templates, design approaches like DDD assist with complex domain modeling, and principles like SOLID ensure maintainable code.

When these layers are understood and applied together—like in a DDD-guided microservice built with an event-driven style, using CQRS and Clean Architecture—they create systems that are not only technically sound but also adaptable to change.

Mastering this taxonomy not only improves your designs but also promotes clearer communication, better collaboration, and more confident decision-making across teams.


  1. Adapted from ISO42010