Architecture5 min read·

Why We Start With Monolithic Architecture for MVPs

When founders come to us with a new product idea, one of the first architecture decisions we make together is: monolith or microservices? Almost every time, the answer is monolith — and it's not even close.

Here's why, and how we make sure those monoliths are ready to scale.

The Microservices Trap

Microservices are the right answer for teams with 50+ engineers, well-understood domain boundaries, and years of operational experience. Netflix uses them. Uber uses them. You are not Netflix.

The promises sound great: independent deployability, team autonomy, technology flexibility. But the costs for an early-stage product are brutal:

  • Distributed tracing is hard. A single request touching 8 services produces logs in 8 places. Debugging becomes archaeology.
  • Network latency adds up. Service-to-service calls over HTTP or gRPC add milliseconds that compound across a request.
  • Deployment complexity explodes. Kubernetes, service meshes, API gateways — each is a full-time job to operate correctly.
  • Data consistency is a nightmare. Eventual consistency, sagas, distributed transactions — concepts that have no business being in a 3-month-old codebase.

We've seen too many early-stage teams spend their first 6 months building infrastructure instead of product. By the time they finish their service mesh, their runway is gone and they have nothing to show users.

What a Good Monolith Actually Looks Like

A monolith doesn't mean a ball of mud. Done right, a monolith is a single deployable unit with strong internal structure that makes it easy to extract services later — when you actually need to.

Here's what we enforce from day one:

Vertical Slicing by Domain

Instead of horizontal layers (controllers, services, repositories), we organize by business domain:

src/
  billing/
    billing.controller.ts
    billing.service.ts
    billing.repository.ts
  users/
    users.controller.ts
    users.service.ts
    users.repository.ts
  notifications/
    ...

Each domain is a self-contained module. It knows what it exposes and what it depends on. When the time comes to extract billing into its own service, the boundary is already clean.

Strict Dependency Rules

Domains should not reach into each other's internals. If notifications needs user data, it should call the users service interface — not the users.repository directly. We enforce this with module boundaries and code reviews.

Event-Driven Communication for Side Effects

Even within a monolith, we use an internal event bus for side effects. When an order is placed, the orders domain emits order.created. The notifications domain listens and sends a confirmation email. This pattern migrates to a real message queue (Kafka, RabbitMQ) with minimal changes when you eventually split.

Database Schema Isolation

Each domain owns its database tables. The billing module never queries users tables directly — it uses the users module's API. This is enforced by convention and caught in code review. When you extract a service, you also extract its schema.

When Do You Actually Need Microservices?

After working with dozens of product teams, here's the actual threshold:

  1. A specific part of your system has fundamentally different scaling requirements. Your image processing pipeline needs 10 GPU servers but your API needs 3 app servers. Split the image processor.
  2. A team boundary creates friction. Two teams are stepping on each other's code in the same module constantly. Extract a service so they can work independently.
  3. A compliance boundary requires it. PCI DSS or HIPAA might require isolating certain data handling. Extract that service.

None of these apply at MVP stage. You're building to learn, not to scale. Ship fast, learn fast, refactor when the problem is real.

The Right Time to Split

With our vertical slicing approach, the extraction process looks like this:

  1. The internal event bus swaps for Kafka or SQS — the domain code barely changes.
  2. The domain's API interface becomes an actual HTTP/gRPC service.
  3. The domain's database schema migrates to its own database.
  4. The monolith calls the new service instead of the internal module.

We've done this extraction for clients and it typically takes one sprint per domain. Compare that to teams who built microservices from day one and spend months fighting their own infrastructure.

Bottom Line

Start with a well-structured monolith. Make it modular. Enforce domain boundaries. Use internal events. When a specific, real scaling problem appears — and it will, if your product succeeds — extract that domain.

This is how you ship an MVP in 8 weeks instead of 8 months, and still have a codebase that can handle the next 10x of growth.


At VANTREXIS, we apply this architecture to every product we build — both for clients and our own internal products. If you're starting a new product and want to discuss the right technical approach, book a free discovery call.

Want to work with a team that thinks like this?

Book a free 30-minute discovery call. No pitch, no pressure.

Book a Discovery Call