objects namespaces and application architecture

Architecture Choice Guide

Architecture choices are tradeoffs. A monolith, modular monolith, and microservices architecture can all be good choices in the right context. The job is to match the structure to the product, team, domain, and operational maturity.

This guide brings together the earlier architecture lessons so you can reason about the choice instead of treating one style as automatically better.

Start With The Problem

Before choosing an architecture, ask what problem you are trying to solve.

If the problem is messy code, unclear boundaries, and controllers doing too much, microservices will not fix that by themselves. They may spread the mess across multiple deployable applications.

If the problem is that one team cannot safely change billing without breaking courses, a modular monolith may help by creating clearer boundaries inside the same deployment.

If the problem is that several teams need independent ownership, scaling, deployment, and data boundaries, microservices may be worth the cost.

Choose A Monolith When Simplicity Matters

A monolith is often the right starting point for a PHP application.

Choose a monolith when:

  • the team is small
  • the domain is still changing quickly
  • one deployment is acceptable
  • operational simplicity matters
  • the application can scale as one unit for now
  • local development should stay straightforward

A monolith can still have good internal design. It can use controllers, services, repositories, domain objects, tests, queues, and clean boundaries.

The risk is that the codebase becomes a big ball of mud if boundaries are ignored. The answer is not automatically microservices. The first answer is usually better structure inside the monolith.

Choose A Modular Monolith When Boundaries Matter

A modular monolith keeps one deployment but organises code around business capabilities.

Choose a modular monolith when:

  • the application has distinct areas such as billing, courses, users, and reporting
  • changes in one area keep affecting another
  • developers need clearer ownership
  • a single deployment is still acceptable
  • the team is not ready to operate many services
  • you may want the option to extract a service later

A modular monolith is a strong default for many serious PHP applications. It gives you clearer boundaries without forcing network calls, distributed tracing, multiple databases, and independent deployments.

The risk is pretending folders are boundaries while modules still freely query each other's tables and use each other's internals. Boundaries need rules, reviews, tests, and sometimes tooling.

Choose Microservices When Independence Is Worth The Cost

Microservices are separate applications that communicate over network boundaries.

Choose microservices when:

  • teams need independent deployment
  • different parts of the system scale very differently
  • the domain boundaries are well understood
  • services can own their data
  • the organisation can monitor and operate multiple services
  • failure handling, retries, timeouts, and observability are mature enough

Microservices can be excellent when the organisation earns them. They can also be expensive when introduced too early.

The risk is a distributed monolith: many services that must deploy together, share databases, call each other synchronously for every request, and fail in chains.

A Practical Decision Table

Use this as a first pass, not a rulebook.

Small team, changing product, simple operations:
  Start with a monolith.

Growing product, clear business areas, one deployment still acceptable:
  Prefer a modular monolith.

Multiple teams, independent deployment needed, strong operations:
  Consider microservices.

Messy code, weak tests, unclear ownership:
  Improve boundaries before splitting services.

One slow feature with different scaling needs:
  Consider isolating that capability only after measuring the problem.

Architecture should be justified by constraints. "Microservices are modern" is not a constraint. "Billing must deploy independently because checkout releases are blocked twice a week" is a constraint.

Questions To Ask In A Review

When reviewing an architecture proposal, ask:

  • What specific problem does this choice solve?
  • What new complexity does it introduce?
  • Who owns each boundary?
  • Where does the data live?
  • What happens when a dependency is unavailable?
  • How will developers run this locally?
  • How will logs, metrics, traces, and errors be followed?
  • What tests prove the important flows still work?
  • What would make us reverse or revisit the decision?

Good architecture decisions include tradeoffs. If a proposal lists only benefits, it is incomplete.

Junior-Level Contribution

As a junior developer, you may not be the person making the final architecture call, but you can still contribute well.

You can notice when a controller is doing too much. You can ask whether a class belongs in billing or courses. You can avoid direct cross-module database queries. You can make service calls handle timeouts. You can write small tests around boundaries. You can document the reason for a decision in plain English.

Those habits matter more than using impressive architecture vocabulary.

What You Should Be Able To Do

After this lesson, you should be able to compare monoliths, modular monoliths, and microservices in terms of deployment, ownership, data boundaries, operational cost, and failure modes.

For junior work, the practical skill is choosing the simplest architecture that solves the actual problem while leaving the codebase easier to change next month.

Practice

Task: Architecture Decision Note

Write a short architecture decision note for a course platform that needs users, courses, billing, and reporting.

Scenario

The team has four developers. The product is still changing weekly. Billing and courses have different rules, but the team currently deploys once per day and does not have mature service monitoring yet.

Requirements

Your note should include:

  • the chosen architecture: monolith, modular monolith, or microservices
  • the problem the choice is solving
  • two benefits of the choice
  • two tradeoffs or risks
  • the first boundary you would protect
  • what would make the team revisit the decision later

Keep the note short enough that it could live in a pull request or an architecture decision record.

Show solution

One reasonable decision is a modular monolith.

Decision

Use a modular monolith with modules for Users, Courses, Billing, and Reporting.

Context

The team is small, the product is changing weekly, and the organisation does not yet have mature monitoring for multiple services. Billing and courses have different rules, so the code needs clearer boundaries than a flat monolith.

Benefits

A modular monolith keeps deployment and local development simple while giving the codebase clearer ownership boundaries.

It also leaves a path to extract a service later if one module develops a real independent scaling or deployment need.

Tradeoffs

The boundaries are still in one codebase, so developers must review imports, database access, and module ownership carefully.

One deployment means a failure in one module can still block the release of another module.

First Boundary To Protect

Protect the billing boundary first. Courses should ask billing for payment status through a public application service or read model, not query invoice tables directly.

Revisit When

Revisit this decision if billing needs independent deployment, separate uptime guarantees, a different scaling profile, or a dedicated team with strong service monitoring and operational support.

Why Not Microservices Now

Microservices would add network failure, distributed tracing, service authentication, data consistency problems, and more deployment work before the team has the operational maturity to benefit from them.