Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 19

Choosing the Right Structural Pattern

Recommended resources

Introduction

In our last lesson, we compared the Layered, Hexagonal, and Clean architectural patterns, establishing that their primary goal is to protect your core business logic by inverting dependencies. You learned about the trade-offs between the simplicity of a classic layered approach and the long-term maintainability offered by Hexagonal and Clean architectures.

Today, we transition from understanding these patterns to actively selecting and justifying one for a given project. This is the culminating skill of this module and a cornerstone of architectural thinking. We'll move beyond the "how" of implementation to the "why" of decision-making. You'll learn a systematic framework for analyzing project requirements and constraints to choose the most appropriate structure, and how to articulate your reasoning—a crucial step in moving from an intuitive developer to a deliberate architect.


The Architect's Mindset: Everything is a Trade-Off

Your experience building large systems has undoubtedly involved making countless design decisions. The first step in formalizing this process is to embrace the first law of software architecture: everything is a trade-off. There are no universally "best" practices in structural design, only choices that are more or less appropriate within a specific context. A developer might focus on the performance of a particular component, while an architect must balance that against maintainability, cost, team capabilities, and long-term business goals.

The following video by Mark Richards provides an excellent illustration of this shift in perspective. It starts with a seemingly simple technical choice and masterfully unpacks the complex web of trade-offs that an architect must consider.

How to Think Like an Architect - Mark Richards

This segment from a talk by Mark Richards is foundational for today's lesson. It contrasts the developer and architect perspectives on a single problem.

Watch these two key parts: The talk opens with a fantastic example comparing full event payloads vs. key-based payloads. Pay close attention to how the "obvious" choice from a performance standpoint is challenged by considerations like coupling, contracts, and data integrity. Next, watch the section that defines the three components of architectural thinking. This will frame our work for the rest of the lesson: understanding business drivers, expanding technical breadth, and analyzing trade-offs.

As the video demonstrates, architectural thinking isn't about knowing all the answers; it's about asking the right questions and knowing how to analyze the compromises. The developer's focus on making things work efficiently is vital, but the architect's role is to ensure the system can evolve and succeed over its entire lifecycle.


A Framework for Architectural Decisions

To move from an abstract understanding of trade-offs to a concrete decision, we need a structured process. This involves identifying the key constraints and requirements of your project and then evaluating how well different architectural patterns satisfy them.

The following article provides a superb, pragmatic framework for this analysis. It outlines the fundamental constraints that shape architectural choices and then evaluates common patterns against them.

Architectural Tradeoffs and Decision Frameworks | Software Architecture | Bitloops Resources

This resource from Bitloops is a practical guide to making architectural decisions. It moves from theory to a checklist-driven approach.

Please read the following two sections: First, focus on The Fundamental Constraints. This section lists the critical questions you need to ask before you even consider a pattern. These include domain complexity, team size, scaling needs, and more. Next, read the section that evaluates several architectures, specifically focusing on Layered Architecture, Hexagonal/Ports and Adapters, and Clean/Onion Architecture. For each, note the "Fits well when," "Tradeoff you accept," and "Red flags" lists. This is the core of our justification process.

The article gives us a powerful toolkit. The constraints act as a lens through which we can view our project, and the pattern evaluations provide a catalog of potential solutions with their associated costs and benefits.

Let's summarize the key decision criteria and how they apply to the patterns we've studied:

Constraint Favors Layered Architecture Favors Hexagonal / Clean Architecture
Domain Complexity Low (e.g., simple CRUD, data transformation) Medium to High (e.g., complex business rules, workflows)
Team Size/Structure Small, single team Can support multiple teams working on different adapters
Longevity/Evolution Short-lived projects, prototypes, stable requirements Long-lived systems (5+ years), evolving requirements
Testability Needs Basic integration testing is sufficient High; requires isolated unit testing of core logic
Technology Volatility Stable, single tech stack (e.g., one DB, one framework) High; need to swap external tech (APIs, DBs, SDKs) is likely

Putting it into Practice: Two Scenarios

Let's apply this framework to scenarios relevant to your work in computer vision. For each, think about the constraints and use them to justify a choice.

Scenario 1: The Research Prototype

  • The Project: A team of two developers is building a proof-of-concept for a novel image segmentation algorithm. The goal is to quickly process a static dataset of 1,000 images and measure the algorithm's accuracy. The core algorithm is written in Python with PyTorch. If successful, the project will be greenlit for a complete rewrite.
  • Analysis of Constraints:
    • Domain Complexity: Low. The focus is on the algorithm itself, not complex surrounding business rules.
    • Team Size: Very small (2 developers).
    • Longevity/Evolution: Very short. It's a throwaway prototype.
    • Testability Needs: Low. The primary validation is the final accuracy metric, not granular unit tests of business logic.
    • Technology Volatility: Low. The tech stack is fixed for the prototype's lifespan.
  • Architectural Choice & Justification:
    A simple Layered Architecture (or even a well-structured script) is the most appropriate choice. The overhead of defining ports and adapters for Hexagonal/Clean architecture would drastically slow down iteration speed with no long-term payoff. The primary driver is time-to-result, and the simplicity of a layered approach directly serves that goal. The "Red flags" for Hexagonal/Clean in the article—a startup with uncertain requirements, a simple domain, a small team—are all present here.

Scenario 2: The Industrial Inspection Platform

  • The Project: An automated quality control system for a factory floor. The system will ingest video feeds from multiple camera models, each with its own proprietary SDK. It runs complex inspection sequences, applies business rules to classify defects, and sends notifications to both a human-in-the-loop UI and an inventory management system. A team of eight will build and maintain it for an expected 10-year lifespan. New camera models and inspection algorithms will be added quarterly.
  • Analysis of Constraints:
    • Domain Complexity: High. It involves intricate workflows, state management, and critical business logic.
    • Team Size: Medium (8 developers).
    • Longevity/Evolution: Very high. The system must be adaptable for a decade.
    • Testability Needs: High. The core logic must be verifiable in isolation to ensure reliability.
    • Technology Volatility: High. The need to support new cameras (adapters), different notification systems (adapters), and potentially new ML frameworks makes technological flexibility crucial.
  • Architectural Choice & Justification:
    Hexagonal or Clean Architecture is the clear winner. The high domain complexity and need for isolated testability demand a separation of the core logic. The "pluggable" nature of adapters is a perfect fit for integrating various camera SDKs and notification systems without impacting the core inspection logic. The upfront investment in creating ports (e.g., CameraInterface, NotificationService) and adapters will pay for itself every time a new piece of hardware is introduced or an external service is swapped. This aligns perfectly with the "Fits well when" criteria from the article: high testability, multiple adapter implementations, and likely technology replacement.

Documenting Your Decision: The ADR

Once you've made a decision, it's crucial to document why. Over a project's long life, team members change, and the original context is often lost. An Architecture Decision Record (ADR) is a lightweight document that captures a significant architectural choice, its context, and its consequences.

Architectural Tradeoffs and Decision Frameworks | Software Architecture | Bitloops Resources

Let's revisit the Bitloops article to see how to formalize our justification.

Read the section on the Architecture Decision Record (ADR). Pay attention to the simple structure: Context, Decision, Consequences, and Alternatives Considered.

Adopting the practice of writing ADRs is a significant step in formalizing your architectural planning. For Scenario 2, an ADR would clearly state that Hexagonal Architecture was chosen to prioritize long-term maintainability and testability in a technologically volatile environment, even at the cost of increased upfront complexity compared to a layered monolith.


Conclusion

In this lesson, we bridged the gap between knowing architectural patterns and choosing one. You now have a robust framework for making and justifying these critical decisions, a skill that lies at the heart of software architecture.

Key Takeaways:

  • Architectural choice is about trade-offs, not perfection. The goal is to find the pattern that best fits the project's unique constraints.
  • Use a systematic framework: Analyze constraints like domain complexity, team size, required longevity, and technology volatility before selecting a pattern.
  • Match the pattern to the problem:
    • Layered Architecture: Best for simple applications, prototypes, or projects where speed of initial development is the top priority.
    • Hexagonal/Clean Architecture: An investment for complex, long-lived systems where testability, flexibility, and maintainability are paramount.
  • Document your reasoning: Use Architecture Decision Records (ADRs) to capture the context and justification for your choices, ensuring future teams understand the "why."

You've now completed your tour of high-level codebase structures. In the next module, we'll zoom in on the very heart of these architectures: the domain model. We'll begin by exploring the core building blocks of Domain-Driven Design (DDD)—Entities, Value Objects, and Aggregates. Understanding these concepts is essential for crafting the stable, independent core that these structural patterns are designed to protect.