DDD microservices evolutionary architectures problem space vs solution space
Code

DDD, Microservices and Evolutionary Architectures: Problem Space vs. Solution Space

21 May 2025 - 6 minutes reading

In my previous article, i discussed about the importance of certain architectures and best practices that can be adopted.

With this blogpost i will focus on Problem Space vs. Solution Space: What’s the Difference and How Does It Impact Software Design?

Understanding Problems and/or Finding Solutions

Among the many quotes attributed to Einstein, there’s one that has always stood out to me:

If I had an hour to solve a problem, I’d spend 55 minutes thinking about the problem and 5 minutes thinking about solutions.

In other words, the problem space refers to the set of challenges a software system must address, while the solution space encompasses the possible answers to each of those challenges.

Problem Space

Humans have a natural tendency to solve problems. This idea is supported by Newell and Simon in their book Human Problem Solving, and we see it every day. In team environments, while developing software, we often look for quick solutions. We try to exit the problem space as fast as possible, as if staying there were uncomfortable. Yet that’s precisely where our clients’ needs reside. It’s where we can truly understand what problems the software must solve.

The problem space is the foundation upon which every solution should be built. Without a deep understanding of it, any solution risks being fragile—or even wrong. Mathematically speaking, the problem space is a subset of the solution space: understanding the former is essential to navigate the latter.

As developers, it’s natural to want to act quickly. But acting too soon can lead us away from the real goal. Rushing prevents us from fully exploring the problem. And the result? Incomplete requirements and partial solutions. That’s how software projects begin to fail: from a poorly understood problem and a comfort zone entered too early.

Staying in the Problem Space

Designing software means solving business problems, and as my math teacher used to say, a problem must first be understood, then solved.
Providing the wrong solution from the start puts us at a disadvantage, and we know applications grow—sometimes slowly, other times very quickly. The more time passes, the more a small initial choice can steer the project off course. Correcting the path then becomes difficult, if not impossible.

As Eric Evans says, the team grows in domain understanding alongside Business Experts. Solutions improve as the problem becomes clearer. But if we start in the wrong direction, even greater awareness will make it hard to realign. Early decisions are crucial. How can we avoid mistakes? By staying in the problem space as long as possible. Einstein said that to solve a problem, you must first understand it. Evans confirms this: the first domain model will always be wrong. We must think in iterations: analysis, hypotheses, feedback. Only this way will the solution truly emerge from the problem.

The Exploratory Model Proposed by Eric Evans.

The Exploratory Model Proposed by Eric Evans.

The Block of Early Solutions

According to Eric Evans, trying to solve everything immediately leads to what we call big front design: we remain stuck in our initial ignorance. We make crucial decisions precisely when we know the least about the problem. It’s like building foundations without knowing the ground they rest on.

Dan North urges caution: don’t force a final solution until you truly understand the domain. In his article “Introducing Deliberate Discovery“, he suggests embracing our ignorance. Only then can we discover the problem step by step. This “lack of awareness” (not knowing what we don’t know) is the second of five levels of ignorance in software development discussed by Philip Armour in his paper “Five Orders of Ignorance: Viewing Software Development as Knowledge Acquisition and Ignorance Reduction.”

This is where the risk arises. Acting too soon, without sufficient knowledge, leads us astray before we even start.

Some Tips to Avoid Rushed Solutions

If ignorance is the opposite of knowledge, how can we stay in the problem space long enough to reduce the former and grow the latter?

The temptation to propose solutions is strong. But we can learn to slow down. Here are some useful tips to help with that.

  • Maintain control of the discussion. It’s not about talking more. It’s about preventing the conversation from closing too soon. A simple gesture or glance can change direction. Clients often take many things for granted. It’s up to us to bring what remains implicit to the surface through targeted questions.
  • Embrace different solutions. At first, there may be hesitation. Then, gradually, different—and sometimes conflicting—ideas emerge. All of them should be welcomed in the solution space. Creating an open environment helps. Every point of view is a piece of the problem we are trying to understand.
  • Keep the main solutions hidden. If you facilitate the exploration, let solutions emerge naturally. Don’t force them or highlight them immediately. With techniques like EventStorming, guide without revealing too much. Let participants build, explore, and discover. If you notice similar solutions, bring them up. Ask yourself: are these just different expressions of the same idea or separate paths to explore?

Evolutionary Architecture

Architecture, like the software we build, must be able to evolve over time. We can no longer think of software as a static building. The old metaphor of architectural construction no longer holds. Software is not built once and left unchanged.

Today, flexibility is essential. We cannot risk the application becoming obsolete simply because the initial architecture cannot keep pace with change. Scalability, resilience, and elasticity are requirements that arise over time. The architecture must be able to accommodate them and grow alongside them, without becoming a constraint.

Pros and Cons of Microservices and Monoliths

Choosing microservices from the start may seem forward-thinking. It helps us respond better to changes but immediately introduces significant complexity.

With microservices come known challenges:

  • database decomposition
  • distributed workflows
  • distributed transactions
  • process automation
  • deployment via containers and orchestrators

These aspects require time, tools, skills—and above all, budget. Not every early-stage project can afford this.

The good old monolith, on the other hand, starts simple.

  • It can be implemented quickly
  • It is easy to test
  • It can be deployed without many issues

However, every benefit comes with a cost. The limitations of the monolith are exactly what microservices aim to overcome.

So, what’s the conclusion? Choosing an architecture is a critical strategic decision. It requires a clear understanding of the context and deciding how to evolve the system over time.

Modular Architecture

In recent years, an intermediate approach has gained traction: modular architecture, positioned between monoliths and microservices. The goal? To deliver a production-ready solution, not just a Proof of Concept (PoC). The key is identifying well-separated modules within the system.

To achieve this, we can use the strategic patterns of Domain-Driven Design: Bounded Context, Context Mapping, and Ubiquitous Language. These patterns are often associated with microservices. However, here they remain within a single codebase, organized into modules rather than separate services. Each module represents a distinct functional area without the need to immediately split everything into remote services. It’s a good compromise. If boundaries need to be shifted later, doing so in a modular monolith is simpler. We don’t have to dismantle an entire network of microservices. Modular architecture allows us to evolve gradually, maintaining order and flexibility from the earliest stages of development.

Conclusions

Staying in the problem space may feel uncomfortable, but it helps us approach solutions with greater awareness and make better decisions.

We have explored the advantages and limitations of monoliths and microservices, introducing modular architecture as a possible intermediate and more flexible approach.

In the next article, we will explore what we mean by “module” and the benefits that choosing a modular architecture can bring.

Article written by