From Monolith to Observable Microservices Using Domain-Driven Design

Microservices

YOUR COSMETIC CARE STARTS HERE

Find the Best Cosmetic Hospitals

Trusted • Curated • Easy

Looking for the right place for a cosmetic procedure? Explore top cosmetic hospitals in one place and choose with confidence.

“Small steps lead to big changes — today is a perfect day to begin.”

Explore Cosmetic Hospitals Compare hospitals, services & options quickly.

✓ Shortlist providers • ✓ Review options • ✓ Take the next step with confidence

This article is based on a talk by Maria Gomez, Head of Technology at ThoughtWorks Spain, originally published on InfoQ. It draws on her experience helping organisations through digital transformation journeys.

The Problem with Monolith Decomposition

The typical starting point is familiar: a monolith that worked well for years but now slows your teams down. Delivery is painful, the codebase is brittle, and getting anything to production takes too long. The promise of microservices — team autonomy, faster deployments, independent scaling — is compelling.

But the reality is rarely that clean. Most systems accumulated years of half-finished decomposition attempts, shared databases accessed by multiple services, chatty inter-service communication, and integrations layered at every level. The root cause of this complexity is almost always the same: service boundaries were drawn around technical concerns rather than business capabilities. The result is a distributed system with the wrong seams.

Domain-Driven Design as a Foundation

Domain-Driven Design (DDD) is an approach to building software with complex, evolving business requirements. Rather than letting database schemas or technical layers dictate structure, DDD anchors the software model in the business domain itself. Key concepts include:

  • Domain: The sphere of activity an organisation operates in — finance, retail, healthcare, and so on.
  • Subdomain: A distinct slice of that domain — payments, statements, or credit card applications within finance, for example.
  • Ubiquitous Language: A shared, precise vocabulary used consistently by both developers and domain experts when discussing a subdomain.
  • Bounded Context: An explicit boundary within which a particular domain model and its ubiquitous language apply. The same word — “customer” — may mean something entirely different in a Sales context versus a Support context.

A concrete example: in an e-commerce system, you might have two subdomains — Sales and Support. Both have a concept of “customer”, but in Sales a customer carries payment and financial information, while in Support a customer carries case history and contact preferences. Treating these as the same entity across both contexts causes the coupling and confusion that DDD is designed to prevent.

The power of defining bounded contexts is that they map naturally to team structures. A product team that owns the Sales context — with both technical and product people — can evolve that domain independently, achieving the autonomy that microservices are supposed to provide, at the organisational level as well as the technical one.

Identifying Boundaries with EventStorming

Knowing that you need bounded contexts is one thing; discovering what they are in a real, complex system is another. One highly effective tool for this is EventStorming, a collaborative workshop format created by Alberto Brandolini. It brings developers, product owners, subject matter experts, and infrastructure people into the same room to build a shared map of a business process using sticky notes on a large wall.

Three core concepts drive an EventStorming session:

  • Domain Event: Something that has already happened and is significant to the business — for example, Item Added to Basket.
  • Command: An external instruction that triggers an event — for example, User Presses Buy.
  • Aggregate: The part of the system that receives commands and decides whether to produce an event — for example, the Basket.

Example: Cinema Ticketing Flow

Imagine an online cinema ticketing site. The workshop begins with two anchor events: the user arrives on the site, and the user receives a booking confirmation email. Participants then fill in everything in between — show selected, seats reserved, ticket added to cart, payment processed, and so on. Once events are mapped, the team identifies the commands that trigger each event, then the aggregates that handle those commands.

Patterns begin to emerge: a cluster of commands and events looks like the behaviour of a buyer, another cluster resembles an accountant, a third resembles customer service. Lines are drawn around these clusters — those lines are your bounded contexts. The first attempt will not be perfect, and that is expected. This is an iterative process, revisited over months as the team learns more about the domain.

Example: Fashion Retailer Checkout Domain

In a real engagement with a Spanish fashion retailer undergoing digital transformation, the team applied EventStorming to the checkout domain — a large, complex slice of an existing monolith. Starting from the moment a user pressed the basket button (the first event: Order Created) through to the confirmation email, the team mapped events, commands, and aggregates. Potential subdomains emerged: promotions, orders, payments, and deliveries.

The boundaries were fuzzy at first, and the team continued refining them over time — which is the normal, healthy trajectory of this kind of work.

Isolating Contexts Before Extracting Services

Once bounded contexts are identified, the next step is isolation — within the monolith — before any extraction to a separate service. This is a critical discipline that is easy to skip and costly to ignore. A bounded context that is still entangled with shared databases or tightly coupled to other subdomains via direct calls is not ready to become a microservice.

Isolation means:

  • No shared database tables with other contexts.
  • No direct coupling to internal implementation details of sibling subdomains.
  • A clean, well-defined API that the rest of the monolith uses to interact with this context.

This refactoring is difficult and takes time. But once a context is truly isolated inside the monolith, extracting it into a standalone deployable is a comparatively straightforward mechanical step. A well-defined bounded context is already halfway to being a well-defined microservice. Add independent deployment, technology-stack independence, isolated failure handling, and individual scalability, and you have a microservice.

Choosing Where to Start

Faced with multiple candidate bounded contexts, teams must pick one to extract first. The selection should be driven by business value, not technical preference. Common selection criteria include:

  • Pace of change: Extract a context where the business wants to experiment and iterate quickly.
  • Performance pressure: Extract a context that is consuming disproportionate monolith resources.
  • Risk isolation: Extract a context where failures should not cascade to the whole system.

In the retailer example, the delivery context was chosen because the business wanted a platform for experimenting with new delivery options. The team could articulate this clearly: extracting the delivery service would provide the experimentation platform the product team needed. Extraction done purely for technical reasons, without a clear business rationale, is much harder to sustain and prioritise.

Observability: The Operational Dimension

Decomposing a monolith into microservices does not reduce system complexity — it relocates it. Code may become simpler in isolation, but the distributed system as a whole is harder to understand, debug, and operate. Testing, deployment, security, and observability all need deliberate attention.

Observability is the ability to interrogate a running system and get accurate answers that improve your understanding of what it is doing. It encompasses far more than error detection: it includes understanding normal behaviour, comparing hypotheses, and answering arbitrary questions about system state. DDD gives you a strong foundation here too — because you understand the business domain, you know what questions are worth asking.

Observable microservices require a deliberate strategy across four areas: logging, monitoring, alerting, and tracing.

Logging

Manually SSHing into a production box to read a log file does not scale beyond a single service. With tens or hundreds of services, you need a log aggregation pattern from day one — not as an afterthought.

The pattern works by having each service write structured logs to a local file or stream, which a log shipper collects and forwards to a centralised store. From there, visualisation and querying tools provide unified access across all services. The ELK Stack (Elasticsearch, Logstash, Kibana) is a widely-used implementation of this pattern; Splunk is another common option.

Equally important is log structure standardisation. If each service logs in a different format, cross-service queries become impractical. Define a shared schema — for example, mandatory JSON fields such as timestamp, service_name, trace_id, level, and message — that every service must emit. Services can add service-specific fields beyond the mandatory set. This standard should be established before the second microservice goes live.

Monitoring

Monitoring provides the ongoing visibility needed to make both technical and business decisions. A centralised dashboard accessible to the whole team — not just operations — lets everyone observe the effect of a new deployment or a changed configuration on the broader system.

In the retailer example, after extracting the delivery service, the team monitored technical metrics (error rates, response times, throughput) alongside a business hypothesis: that introducing the new microservice would not reduce sales conversion. They validated this by implementing semantic monitoring — automating representative end-to-end user journeys and running them continuously in production, then plotting the results on the same dashboard as technical metrics.

Tooling choices range from Kibana (if already in use for logs) to purpose-built options like Prometheus, Grafana, or Datadog. The key discipline is curation: regularly review the metrics being tracked and remove those that no longer provide actionable insight. A dashboard full of noise is worse than a minimal one.

Alerting

An alert is a signal requiring immediate action. Several principles apply:

  • Every alert must have a defined action. The person responding — who may not have built the service — needs to know what to do. Alerts without runbooks are noise.
  • Not every alert needs a human. Aim for the majority of alerts to be resolved by automation. Autoscaling on cloud infrastructure is the most common example: if a service is under load, it should scale itself without waking someone at 3 a.m.
  • Standardise alert structure. Define templates for what an alert must contain so that all microservices follow the same format.

Kibana and Prometheus both include alerting capabilities. OpsGenie is a dedicated alert management tool that integrates with many monitoring backends and supports on-call routing, escalation policies, and runbook links.

Distributed Tracing

In a monolith, a request typically lives within a single process and transaction — straightforward to trace. In a microservices architecture, a single user request may traverse a dozen services, some communicating synchronously, some asynchronously. Debugging production issues or finding performance bottlenecks requires understanding that entire journey.

The minimum step is to assign a trace ID (also called a correlation ID) to every request that enters the system and propagate it through every downstream call. Most microservice frameworks provide middleware to do this automatically. With trace IDs in your centralised logs, you can search for a single ID and reconstruct the request path — though this is still a manual, sometimes tedious process.

Dedicated distributed tracing tools go further. They collect span data from each service — start time, duration, service name, parent span — and assemble it into a visual call graph. This shows exactly where time was spent and where failures occurred, without manual log correlation. Popular options include Zipkin, Jaeger, Datadog APM, and Dynatrace.

To avoid vendor lock-in and ensure interoperability, standardise on an open tracing format. OpenTelemetry (which superseded the earlier OpenTracing and OpenCensus standards) is the current industry standard and is supported by the major visualisation and APM tools. Instrument your services against the OpenTelemetry SDK so that the choice of backend remains flexible.

Key Takeaways

  • Business value drives decomposition. Service boundaries should reflect business capabilities, not technical layers. Every extraction decision should be tied to a concrete business outcome.
  • Isolate before you extract. A bounded context must be fully decoupled inside the monolith — no shared databases, clean API — before it is extracted into a standalone service.
  • Use EventStorming to discover boundaries collaboratively. Include developers, product owners, domain experts, and infrastructure people. Expect to iterate over months.
  • Observability is not optional. Build logging aggregation, monitoring dashboards, alert runbooks, and distributed tracing in from the first microservice — not retrospectively.
  • Standardise early. Log schema, alert structure, and tracing format should be agreed upon before multiple services exist. Retrofitting standards onto a large fleet of services is expensive.
  • Start small and iterate. Don’t wait until everything is clear. Begin with one bounded context, learn from operating it in production, and carry that knowledge forward.

Microservices redistribute complexity rather than eliminate it. The teams that navigate this successfully are the ones that treat the operational and organisational concerns with the same rigour they apply to the technical decomposition — and that use the domain knowledge they have built up through DDD to ask the right questions of their systems in production.

0 0 votes
Article Rating
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
0
Would love your thoughts, please comment.x