DDD, Microservices, and Evolutionary Architectures: the Role of Software Architect
In the second-to-last chapter of my personal deep dive into the world of DDD, I clarified the differences between Event Sourcing and Event Streaming.
In this final article, I’ll focus on the figure and role of the Software Architect.
What Do You Want to Be When You Grow Up?
I wanted to become a Software Architect. As often happens when you aim for a role, I started by researching to understand what this position really entails. The question I asked myself was simple: “What is the job of a Software Architect?”
At first, like many others, I thought the main focus was designing appropriate architectural solutions—solutions that strike the right balance between simplicity and complexity to support the projects entrusted to my team. Over time, and thanks to conversations with the right people, I came to understand that this is just one part of the role.
There’s another question I’ve touched on in previous articles of this series: “Why do we develop software?” It’s a fundamental question—one everyone in the industry should ask themselves. We develop software to solve business problems. Our job is not just to write working code. That kind of exercise is best left to kata—useful for refining techniques and experimenting, but not for solving real-world issues.
Our true responsibility is to design systems that work and that solve problems.
Bringing Order and Disorder Together
And this is exactly where true complexity emerges. In the real world, we’re often dealing with two categories of systems: the ordered and the disordered (as illustrated in the diagram below).
Software, as shown in the diagram, falls under the category of ordered systems. It is governed by mathematical and engineering rules, which makes it rigorous and predictable. Business, on the other hand, is a disordered system, much like the humans who drive it.
The job of a Software Architect is therefore to design an ordered system capable of solving problems that arise within a disordered context. Think of that game where you have to fit different shapes into their matching slots. Now try fitting a cube into the circle space… That’s the daily challenge faced by software architects.
To tackle business problems, you need to speak the language of business—or better yet, build a shared language and model that both the technical team and stakeholders can understand. A Software Architect must be able to connect different languages, bridging gaps between diverse skill sets and fostering collaboration across the team.
To paraphrase Gregor Hohpe’s metaphor, the value of an architect can be measured by the number of floors in a skyscraper they’re able to connect. Their real contribution lies in helping everyone involved find a shared language—and, as a result, a shared model on which to build the final product.
Languages and Models
On these points, E. Evans was crystal clear. As we’ve explored in detail, Ubiquitous Language and Bounded Context are two fundamental patterns in Domain-Driven Design. Both rely on the deliberate use of language and the modeling of reality.
Language is the first building block. It serves to clearly define what we’re talking about, eliminating ambiguity and implicit knowledge. Everything must be explicit and shared across the entire team. This applies even in casual conversations: when joining a group of friends mid-discussion, the first instinct is to ask, “What are you talking about?” Without that request to identify the context, misunderstandings are inevitable. Sometimes it leads to a laugh, other times to an awkward reply. Without context, it’s impossible to build a reliable mental model.
In software, we often talk about models. Evans dedicates extensive discussion to them in his famous “blue book.” But what do we actually mean by a model? Software architecture is built on models. Object-oriented programming made this a core principle, aiming to model reality through ideal objects. And yet, these models often fail at their primary goal: faithfully representing reality. But why?
Philosophers Were Not Programmers, But…
If we look back to the era of ancient Greek philosophical debates, it’s clear the discussion was not about functional programming versus Object-Oriented Programming. Yet, the opposing views of two thinkers reflect a tension similar to that between these paradigms.
The first is Heraclitus (535–475 BC), who said, “No man ever steps in the same river twice, for it’s not the same river and he’s not the same man.” For Heraclitus, reality is a continuous, ever-changing flow.
The second is Plato (428–348 BC), who argued, “The sensible world around us is merely an imperfect copy of the Forms, which represent true reality.” A diametrically opposed perspective.
It’s important to remember that Plato’s thought, together with Aristotle’s, formed the foundation of Western culture for centuries. Many aspects of how we interpret the world stem, directly or indirectly, from their theories. When we use the object-oriented programming (OOP) paradigm, we are, often unknowingly, heirs of Plato.
Domain-Driven Design, while not rejecting OOP, leans more toward Heraclitus’s view. It acknowledges the importance of the model but sees it as subject to continuous evolution, immersed in a flowing reality. This perspective also underpins the principles of Agile.
The problem doesn’t lie in the model itself, but in the belief that the model is correct and valid forever. This is the real trap. When we discover that what we know—or what we think we know—is far from reality, the black swan appears, to borrow Nassim Taleb’s powerful metaphor.
The Purpose of a Model
What is the purpose of a model? A practical example helps clarify this. I am passionate about mountain hiking in every season. When I want to reach a hut I don’t know, I first look for information about the trail and elevation. A topographic map that shows natural elements of the area — such as rivers, lakes, and passes — along with man-made infrastructure like trails, represents the ideal model to tackle the hike.
If, instead, I already know the route but plan to repeat a climbing route I’ve never done before, I need a different model. In this case, I require a technical sketch of the route, showing pitch lengths, available protections, and difficulty levels for each section. A photograph of the cliff, however evocative, wouldn’t be useful as it does not provide the necessary operational details.
A photography enthusiast might argue that by choosing technical maps, I’m missing out on the beauty of the landscape. And they would be right — but we are pursuing different goals.
According to Rebecca Wirfs-Brock, a model is a simplified representation of a phenomenon or object that intentionally highlights certain aspects while ignoring others. It is an abstraction created with a specific purpose. Such an abstraction is useful only if the context is clear and shared among all involved parties.
The climbing route sketch is useful only if everyone involved aims to complete that particular route. If the goal is a simple walk, that model becomes unsuitable.
Further “Philosophical” Considerations
Returning to the design of systems aimed at solving business problems, language plays a central role in ensuring a shared understanding of the problem among all team members. Using the previous example, we cannot mix hikers and climbers and expect them to use the same map (model) to achieve different goals. It is essential to clearly define the problem’s terms and build a model consistent with the intended purpose.
As Wittgenstein stated — long before Evans — “The limits of my language mean the limits of my world.” Similarly, the limits of the Ubiquitous Language define the boundaries — the bounds — of our Bounded Context.
Language is not only a descriptive tool but also constructive: it shapes how teams interpret, model, and address the domain or its parts.
Ontology
In the context of Domain-Driven Design, a model can be understood as a partial ontological representation, designed to describe significant aspects of a complex reality.
Ontology is the branch of philosophy that studies what exists and defines how we can classify and understand the elements around us. In other words, it provides a system to organize reality by identifying categories of objects and the relationships among them.
This approach perfectly aligns with what we achieve by applying the Bounded Context and Context Mapping patterns in DDD-based projects: building a coherent and shared “map” of the domain reality, highlighting relevant entities, boundaries, and connections.
Teleology
A fundamental aspect in modeling a problem is understanding its purpose or the reason for its existence. As stated by the “second Law of Software Architecture,” the why is more important than the how.
Teleology, a branch of philosophy, focuses on studying the purposes or goals of a process or entity. It investigates the why behind an event or phenomenon, seeking to understand the end or objective it aims to achieve.
This highlights why, during domain analysis, it is essential to involve all stakeholders in the project, ensuring a complete and shared vision of the goals.
Phenomenology
In domain analysis related to the product we intend to develop, it is essential to involve not only those who designed the solution but also those who will use, market, build, and finance it. All these aspects must be evaluated and validated.
Phenomenology, the study of human experience and how we perceive and interpret reality, supports us in this task. Instead of focusing on what exists independently of us, phenomenology investigates how we live, experience, and understand the world around us — in other words, how we see and perceive things.
This involves a deep analysis of phenomena as they appear to our consciousness. For this reason, it is crucial to gather input from all stakeholders, since each has their own perception of reality. The software architect’s role is to consider and integrate these perspectives into the modeling process.
Nice, yes… but I wanted to become a Software Architect
All very interesting, but why is proper modeling truly important? Let’s analyze the impact of this approach in the daily practice of a software architect, starting with a reflection on the concepts of coupling and cohesion.
Two components are more tightly integrated — that is, more strongly coupled — the greater the **knowledge** they **exchange**. Conversely, they become more independent as the mutual knowledge decreases.
Practically speaking, in the first case, a change to one component will almost certainly require changes to the other; in the second case, it might not be necessary to modify both to update just one.
Is a high level of coupling always negative? A consultant’s typical answer is: “It depends.”
Let’s summarize the possible combinations in a table to better understand when it’s preferable to aim for cohesion and when it’s better to promote decoupling.
Within a given Bounded Context, that is, inside the model, I expect a strong cohesion among its components, with an intense exchange of information and knowledge. I do not fear this strong coupling: if I need to change the behavior of a Bounded Context, I am aware that I will also need to modify its internal components because the context is properly designed and remains isolated from others.
Conversely, if two components belong to two distinct Bounded Contexts, there must be low or no coupling between them, as I do not want to have to modify both when only one changes.
Bounded Context and Microservices
It is important to clarify that the Bounded Context pattern does not coincide with microservices, even though they are often confused or used interchangeably.
The boundaries of a Bounded Context are defined based on the model’s purpose. Within it, there is a focus on high cohesion among components. In contrast, the boundaries of a microservice are determined by the size of the artifact to be deployed. For this reason, microservices aim for very low coupling.
The motivations behind Bounded Contexts and microservices differ. The health of our codebase depends on clear semantic boundaries between Bounded Contexts, regardless of whether it is a modular monolith or microservices. Physical separation should only be done if truly necessary. Starting immediately with microservices often generates accidental and unnecessary complexity. It is better to maintain simplicity and learn progressively during domain exploration.
The greatest risk for a software architect is making important decisions when domain knowledge is still limited, typically at the start of a project. Before deciding, it is essential to reduce technical debt. Paraphrasing Socrates: “What I must learn, I learn by doing, aware that the only thing I know is that I do not know.”
Dan North also emphasizes this idea in his famous article “Introducing Deliberate Discovery“. At the start of every project, we must humbly acknowledge at least the second level of ignorance, that is, “I know that I do not know something.”
Often, we are forced to propose solutions under tight deadlines, but good architecture requires time and knowledge. Otherwise, the already high risk of error becomes almost certain. As the saying goes: if you think good architecture is expensive… try a mediocre one and then see the cost.
Conclusion
The role of a Software Architect goes beyond defining structures and technical patterns: it requires a constant balance between logic and adaptability, between rigor and flexibility.
It is not just about designing optimal solutions or writing code. It means creating a common language that connects technology and business, development teams and stakeholders.
A good Software Architect does not seek absolute answers. Instead, they provide models capable of evolving with the context. Every architectural decision must be made consciously, fully accepting that change is inevitable.
The true challenge is to learn, adapt, and lead the team through the domain’s complexity.
Ultimately, the value of a Software Architect is measured not only by the quality of the architecture but also by their ability to make the team stronger, more cohesive, and ready to face future challenges.