The most common mistake in microservice architecture is not the choice to use microservices. It is where you drew the lines.

Teams decompose along the wrong axis, end up with services that are constantly chatting with each other, and then wonder why their “microservices” have worse latency and higher operational complexity than their old monolith with none of the benefits.

The problem is almost always boundary placement.

The Signs Your Boundaries Are Wrong

You do not need to audit your entire architecture to find bad boundaries. The symptoms are obvious once you know what to look for:

Chatty service communication. If completing a single user request requires 5-10 synchronous calls between services, your boundaries are wrong. You have distributed a single logical operation across multiple services, and you are paying network latency for every step.

Shared databases. Two services that read and write the same database tables are not actually separate services. They are a distributed monolith with all the operational complexity and none of the decoupling benefits. If Service A fails and Service B cannot function, they were the same service to begin with.

Cascading failures. One service goes down and takes three others with it. This indicates tight coupling that the service boundary was supposed to eliminate.

Impossible to deploy independently. If deploying Service A requires coordinating with the team that owns Service B, you have a boundary problem. True microservices can be deployed independently by the team that owns them.

How Most Teams Draw Boundaries Wrong

The Technical Layer Trap

The most common bad pattern is decomposing by technical layer rather than business capability.

“We have a Users service, a Database service, and an Auth service.” This looks like microservices. It is not. You have taken a three-layer architecture (presentation, business logic, data) and given each layer a network hop. Worse, you have made every operation depend on all three layers being available simultaneously.

A request that used to be a local function call is now three network calls. The latency is worse. The failure modes are more complex. The operational overhead is higher. Nothing is actually decoupled.

Too Fine-Grained Decomposition

The opposite problem: services that are too small to be coherent.

A “CreateUser service” that does nothing but insert a row into a users table is not a microservice. It is a function masquerading as infrastructure. It has deployment overhead, network overhead, and monitoring overhead for something that should be a method call.

The right granularity is not “one function per service” or “one entity per service.” It is one business capability per service.

Domain-Driven Design Gives You the Right Tool

DDD’s concept of Bounded Contexts is the correct mental model for microservice boundaries. A Bounded Context is a boundary within which a particular domain model applies and is consistent.

The classic example: “Customer” means something different in a Sales context than it does in a Support context. In Sales, a Customer has a prospect stage, deal size, and pipeline probability. In Support, a Customer has ticket history, SLA tier, and escalation contacts. These are different models of the same real-world entity.

Trying to create a single unified Customer model that satisfies both contexts creates a model that satisfies neither. The correct answer is two separate contexts with their own Customer model, communicating through events or well-designed APIs at the boundary.

Practical Boundary Heuristics

If DDD feels too abstract, here are concrete tests to apply:

The two-pizza team rule (with teeth). A microservice should be owned and operated by a team small enough to feed with two pizzas. If a service requires a committee to modify, it is too central. If the team is so small they only have one service, it might be too granular.

The deployment independence test. Can this service be deployed to production without coordinating with another team? If no, the boundary is wrong or the coupling needs to be removed.

The data ownership test. Does each service own its data? If two services share tables, they share ownership, which means they share failure modes and deployment coupling.

The business event test. When something meaningful happens in your business - an order is placed, a user completes onboarding, a payment is processed - that event should cross no more than one service boundary in most cases. If it requires orchestrating five services to record one business event, your boundaries are misaligned with your business.

The Correct Decomposition Pattern

Business Domain Service Boundary What It Owns
Identity Auth/Identity Service Users, roles, sessions, OAuth
Commerce Order Service Orders, order items, order state
Commerce Payment Service Payment methods, transactions, refunds
Catalog Product Service Products, categories, inventory
Fulfillment Shipping Service Shipments, carriers, tracking
Communication Notification Service Email, SMS, push templates and delivery

These services communicate through events. Order Service emits “order.placed”. Payment Service listens and processes payment. Shipping Service listens and creates a shipment. Notification Service listens and sends confirmation. Each service does its job independently. Failures in one do not cascade to others.

When to Start With a Monolith

The honest advice: if you are building something new and your team is fewer than 10 engineers, start with a modular monolith. Get your domain model right. Understand where the natural seams are. Then extract services where the operational or scaling needs actually justify it.

Premature decomposition is a real cost. Distributed systems are harder to develop, harder to test, harder to debug, and harder to operate than monoliths. The benefits only justify those costs at a certain scale of team and traffic.

Extract services when you need independent scaling, independent deployments, or organizational separation - not because “microservices are better.”

Bottom Line

Wrong service boundaries give you the worst of both worlds: distributed system complexity without the decoupling benefits. The fix is to align boundaries with business capabilities, not technical layers. Use DDD bounded contexts as your guide, apply the deployment and data ownership tests to validate, and be honest about whether your current team size and scale actually justifies the operational cost of microservices.