Skip to main content

Monolith or services

Start with one deployable application organized into well-separated features (a modular monolith). Split into separate services only for a concrete, measured reason.

Status: proposed in ADR-008. Until that ADR is accepted, treat this page as guidance, not a standard.

Why: splitting an application into services adds costs on day one: network failures between components, versioned contracts, distributed tracing, separate deployments, data consistency across stores, and a harder local setup. The benefits (independent scaling, independent releases) depend on a future that may not arrive. This is KISS and YAGNI applied to system structure.

Options​

OptionWhat it isWhen it fits
Monolith without internal boundariesOne deployable, code mixed togetherSmall scripts and throwaway prototypes only
Modular monolith (default)One deployable, features with clear boundaries (feature-first)Most projects: MVPs, SaaS products, internal tools
ServicesSeveral independently deployed applicationsA concrete reason from the next section applies

A modular monolith keeps the benefit people want from services, clear boundaries between parts, without the network between them.

Reasons to split​

Split a service out only when you can name the reason and show evidence for it.

ReasonEvidence to ask for
One component needs to scale differently from the restMeasured load or cost, not an expected one
A component needs a different runtime, language, or hardwareA requirement the main runtime cannot meet
Teams need to release independentlyTeams are blocked on each other's releases today
A failure in one component must not take down the restA past incident, or a hard availability requirement
Compliance requires isolating data or accessA written requirement

These are not reasons: "it might need to scale someday", "microservices are modern", or "the boundaries would be cleaner". Clean boundaries come from features and ports, which a monolith already has.

What the profiles use today​

The deployment profiles run one backend service each, next to a separate web app:

  • lean: one FastAPI API on AWS Lambda (ADR-005).
  • enterprise: one FastAPI or Spring Boot service on ECS Fargate.
  • static: a site only, with no backend.

ECS is preferred over Kubernetes. Kubernetes is used only when enterprise requirements justify it, several services need orchestration, or the project exists to demonstrate it.

Keeping a split possible​

Design the monolith so a feature could be extracted later, without building for the split now:

  • Keep each feature's layers and code together (feature-first).
  • Have other code depend on a feature's public interface, not its internals.
  • Keep external collaborators behind ports, so moving a call from in-process to over the network changes an adapter, not business rules.
  • Which feature owns which database tables, and whether features may join across them: TBD.

Record the decision​

Choosing to split, or to adopt Kubernetes, is a decision. Record it in the project's own ADR with the reason and the evidence from the table above, so the cost is accepted knowingly. The format is the handbook's ADR template.