YOUBA BENLoading…
Architecture · 6 min read
Split confusion into twelve services and you get the same problems across databases, APIs and network calls. Boundaries, failure design and observability come first.

A badly structured monolith is difficult to maintain. Split it into twelve services and you now have the same confusion spread across databases, APIs, containers and network calls — with latency and partial failure as a bonus.
Microservices only work when the boundaries make sense. Distribution is a tax you pay for independence. If you have not earned that independence with clear ownership, you are just buying a more expensive mess.
Boundaries before distribution
Clear ownership first. Then APIs and events — never shared tables.
✓ Recommended
Orders
OwnsCart, checkout, order lifecycle
Payments
OwnsCharges, refunds, payment status
Inventory
OwnsStock levels, reservations
Event bus · Defined APIs · Contracts
Publish events. Call public contracts. Never reach into another service's database — that's how a distributed monolith is born.
✗ Distributed monolith
Many deployables, one shared brain
Cross-service joins · coupled deploys · tangled ownership
Each service should represent a clear business area and own its own data. Orders should not reach directly into the payments database, and payments should not depend on the internal structure of inventory. Services communicate through defined APIs or events — not shared tables and hidden dependencies.
Once services are distributed, failure becomes normal. Calls time out, messages arrive twice and one unavailable service can affect several others. The architecture has to expect that — not hope it away.
These aren’t rules to apply blindly on day one. They are the practices I reach for when a system needs to be split — and I only add each one when the pain it removes is real. Start with ownership. Earn the rest.
12 practices
Database per Service
Gives each service its own database. No shared tables, no cross-service joins — talk through APIs or events instead.
Bounded Context
Splits one large domain into clear business areas. Inside a service, a word like “customer” should mean one thing — not five.
CI/CD Pipeline
Builds, tests and deploys every service automatically and independently. Manual release steps quietly kill the model.
Circuit Breaker
Stops calling a failing service so one outage does not cascade. Fail fast, recover later — don’t retry forever.
API Gateway
Routes client requests to the right service, handles cross-cutting concerns like auth, and hides the internal map.
Observability
Collects logs, metrics and traces from every service. Without them, one broken request across four hops is guesswork.
Event-Driven Architecture
Lets services communicate by publishing and consuming events — looser coupling and room to change without a chain reaction.
Saga Pattern
Coordinates distributed transactions and rolls back completed steps safely when something fails halfway through.
Containerisation
Packages each service with its dependencies so it runs the same on a laptop, a CI runner and production.
Stateless Services
Stores session and cache state outside the instance. Any replica can serve any request — restarts stay boring.
Contract Testing
Checks that APIs stay compatible across services before either side ships a breaking change.
Service Discovery
Finds healthy service instances automatically at runtime — no hard-coded addresses that break on every scale event.

Think of the practices in layers. Boundaries and a database per service decide what can change independently. An API gateway and service discovery make those pieces reachable. Events and sagas keep workflows moving when a direct call would couple teams too tightly. Observability and contract tests keep the system honest after it ships.
Finally, every service needs automated delivery. CI/CD pipelines should build, test and deploy changes independently, while contract tests make sure one service does not silently break another. Containers and stateless instances make those deploys replaceable instead of precious.
“Microservices are not automatically more scalable or maintainable. They earn those benefits only when ownership is clear, communication is controlled and failure has been designed for from the beginning.”
Product Delivery
From “Can You Build This?” to a Product People Actually Use
Most projects start with an unclear need. End-to-end delivery means turning that sentence into something built, released, supported and improved.
.NET & Testing
The Bug Was Fixed. Then It Came Back.
Unit, integration and regression tests protect different lessons. A useful suite is measured by what it protects — not how many tests it contains.