AI · Article 16 of 72 · Part 4

Why a Modular Monolith Is Often Better Than Microservices

Compare operational burden, isolation and deployment needs using explicit module boundaries and extraction triggers.

Orders, billing and reporting modules have explicit internal boundaries within a shared deployment.
Modules communicate through defined interfaces; one deployment does not require one tangled codebase.

A small team can create ten services and remain one team responsible for all ten. The diagram has gained boundaries. The operating calendar has gained deployments, credentials, logs, compatibility questions, and failure combinations.

Separating services is useful when the separation solves a real problem. A modular monolith can be a better starting point when one team needs clear internal responsibilities and one manageable deployment.

“Better” is conditional. This chapter of The AI Software Factory, in the SalarsNet AI section, examines the conditions rather than claiming a universal benchmark. The proposed default favors clear modules with a simple operating shape until independent scale, ownership, isolation, or release needs justify distribution.

No Salars architecture migration or performance comparison is reported here.

Separate logical boundaries from deployment boundaries

A logical boundary identifies which part of the system owns a business concept and behavior. A deployment boundary identifies what can be released and operated separately.

A monolith can contain distinct modules for supplier mapping, listing preparation, job recording, and billing. Those modules can expose defined interfaces and restrict access to their internal state while sharing a deployment.

A set of services can still be tightly coupled if they share tables directly, require coordinated updates, or pass vague messages whose meaning depends on internal knowledge. Physical separation does not create sound domain boundaries automatically.

The first design question is therefore who owns the task and data. The second is whether that ownership needs an independent operating unit. Answering them in order prevents the deployment diagram from determining the domain model by accident.

The shared-platform chapter explains reusable foundations. A modular monolith is one possible operating form for those foundations and apps.

Define modules around coherent work

A module should group behavior that changes together and can be explained as a job. “Utilities” can become a collection of unrelated functions with no owner or contract.

For a hypothetical software factory, supplier normalization could own format mappings and source-record interpretation. Listing preparation could own draft content and condition evidence. Job recording could own lifecycle and attempt history without deciding the domain rules.

A module interface should state inputs, outputs, errors, and authority. Another module should request an operation rather than manipulate internal tables freely.

These boundaries can be checked through code structure, review rules, and tests. A single deployment does not require unrestricted imports everywhere. The team can enforce a narrow dependency direction and identify violations.

The capabilities chapter examines grouping in more detail. Here the important point is that modularity requires deliberate ownership; it does not appear merely because folders have different names.

One deployment can reduce coordination

A single deployable artifact can make it easier to test and release a change affecting several modules. The team can evaluate a consistent version of the whole system rather than coordinate a sequence of service versions.

For a hypothetical change to job-state interpretation, the related module and consumers can be updated together. The release still needs checks, preview, promotion, and verification, but its compatibility state is simpler to describe.

This advantage depends on the system and team. A large monolith can have slow builds, tangled dependencies, or a release process that blocks unrelated work. Those are actual problems to investigate, not inevitable properties of one deployment.

A modular design aims to preserve local reasoning inside that deployment. It should let a developer identify the affected module, its consumers, and the tests needed for a change.

Do not use simplicity as an excuse to skip operating discipline. One deployment still needs access control, observability, resource limits, and a recovery plan.

Distributed services introduce new failure conditions

A call between modules in one process differs from a network call between services. The network can time out, a response can be lost, and one service can operate while another is unavailable.

Microsoft documents independent deployment and service-owned data as microservice characteristics, while listing challenges in consistency, transactions, communication, testing, latency, and versioning. These are tradeoffs to design for, not evidence that microservices always fail or monoliths always succeed. Microservices architecture style.

For a hypothetical job, service A might accept an input while service B fails to record the result. A retry needs to distinguish work that never began from work that completed without a received response.

A single process can fail too. The distinction is which failure states the architecture adds and how the team handles them. Distribution requires explicit recovery and compatibility behavior across boundaries.

The idempotency chapter examines duplicate-effect control. A deployment choice should account for that work before treating every function as a service.

Data ownership matters in either form

A modular monolith can use one database while preserving module ownership of records and changes. A shared database does not require every module to edit every table.

Define which module validates and writes each business concept. Other modules should use an interface or an appropriate read model rather than bypassing its rules.

For the hypothetical supplier mapping, the normalization module owns interpretation. A listing module might consume a validated item record with provenance. It should not silently alter mapping rules because a particular listing needs a shortcut.

When data cross module boundaries, preserve meaning and authority. A customer identifier, source reference, and reviewed status should not lose their context through a generic object passed everywhere.

If the system later separates a module into a service, clear ownership makes the transition easier to reason about. It does not make the migration automatic, but it reduces the ambiguity about which data and rules move.

Independent scaling can justify a service

Some work has a different resource profile. A large image-processing task may consume resources unlike a lightweight account request. Independent scaling can become useful when observed demand and limits support it.

Measure the relevant condition before extracting. Is the module actually causing contention? Can a queue, resource limit, or separate worker within the current operating model handle it? Does the team need an independent runtime?

A hypothetical image job can be moved behind a defined job interface while the core app remains a modular monolith. The architecture need not jump from one deployment to dozens of tiny services.

Keep the task contract stable where possible. Define how the caller learns about completion, failure, and retries. Extraction changes the operating conditions even when the business behavior is intended to remain the same.

No scaling result is asserted here. Actual resource observations, workload identity, and controlled comparisons would be required before claiming that extraction improved performance or cost.

Independent teams can justify independent releases

When distinct teams own stable domains, separate services can allow them to release without coordinating every change. That advantage depends on genuine autonomy.

If every service change requires the same people to approve, test, and deploy all consumers, the architecture may not provide the expected independence. The team should inspect the actual workflow rather than infer it from repository count.

A small software factory may initially have one team. Clear module ownership can support collaboration without creating separate operational systems for every agent or developer.

Multi-agent development likewise does not require microservices. Agents can work in isolated branches or worktrees with defined file ownership and review boundaries. Deployment architecture should follow the product’s operating needs, not the number of writers available.

The parallel-development chapter addresses coordination. Conflating coding-work isolation with runtime services can add infrastructure without solving merge or review problems.

Fault isolation requires actual containment

A separate service can help isolate resource failures or sensitive operations. The benefit depends on how callers behave when it fails.

If every customer request waits on that service and no appropriate recovery exists, its failure can still affect the whole product. If credentials or data remain broadly shared, physical separation may not create the intended security boundary.

Name the failure being isolated. Is it resource exhaustion, an external dependency, an action with stronger authority, or a domain whose release risk differs? Then define the controls and caller behavior that make the isolation real.

A hypothetical publishing capability might deserve a narrower operated boundary than draft preparation because it commits external changes. That is an authority argument, not merely a performance argument.

The agent-security chapter develops access boundaries. A monolith and microservice design both need them; the deployment choice should make enforcement practical.

Diagnose a tangled monolith before extracting it

A system can become difficult because responsibilities are unclear, tests are weak, or data access is unrestricted. Splitting it into services can carry those problems across a network.

Identify the actual coupling. Which modules change together? Which data are shared? Which calls depend on undocumented behavior? Which releases require coordinated changes?

Refactor a narrow internal boundary where feasible before extraction. A defined interface and contract tests can reveal whether the domain is coherent enough to operate separately.

Do not promise that refactoring will be easy. Existing behavior may be poorly understood. Preserve representative cases and recorded limitations while changing structure.

An extracted module should have a clear owner, data contract, failure model, deployment process, and recovery plan. If those are missing, the team has moved complexity rather than reduced it.

Compare total operating burden

An architecture comparison should include development, testing, deployment, monitoring, access, incident diagnosis, updates, and recovery.

A microservice can have a small codebase while adding shared infrastructure and compatibility work. A monolith can have a simple deployment while requiring more integrated testing. The appropriate choice depends on the actual system and team.

Avoid universal performance claims. A hypothetical comparison could estimate setup work for one deployment versus several services, but those values would remain assumptions until observed.

Use a bounded pilot when a consequential decision remains uncertain. Keep the same task and correctness requirements. Record actual time, resource behavior, failures, and operator work. Reserve cases not used to tune the implementation where transfer matters.

Stop when the authorized budget is exhausted or the comparison cannot resolve the immediate decision. A small exploratory result supports a local conclusion, not an industry law.

Preserve a path to change

Starting with a modular monolith should not mean accepting permanent entanglement. Clear interfaces, data ownership, contracts, and operating records can preserve options.

An extraction decision can have triggers: measured resource contention, independent ownership, required isolation, or release coupling that materially obstructs work. The trigger should name an observable problem.

A service can also be consolidated when its independence no longer helps and its operation creates unnecessary burden. Architecture should respond to evidence rather than treat extraction as irreversible progress.

The app registry can record current deployment and ownership. The component catalog can record shared mechanism contracts. These records help operators understand the actual system during a transition.

A useful architecture is the one the team can explain and operate under current conditions while retaining a reasonable way to respond when those conditions change.

Work through an extraction proposal

Suppose the hypothetical monolith performs file conversion and image preparation. Image jobs begin delaying ordinary requests under an observed workload. The team proposes moving image processing to a separate worker.

The proposal should first identify the measured condition: input sizes, job frequency, resource use, and which customer operations are affected. A vague statement that images are expensive does not establish the boundary. The team should also inspect whether resource limits or a queue within the current arrangement resolve the problem adequately.

If a separate worker remains justified, define its input contract and result record. The caller supplies an authorized job reference and permitted input. The worker returns a result or a clearly classified failure. It should not receive unrelated customer records or broad publishing authority.

Now list the new failure states. The job can be queued but not begun, begun but interrupted, completed without the caller receiving confirmation, or rejected because the input is outside scope. The system needs a way to distinguish and recover those states without duplicating consequential effects.

A preview comparison can check the actual task under representative harmless cases. Correctness and customer separation remain required. Resource and latency measurements should use comparable conditions. Operator work, such as diagnosing a failed job, belongs in the comparison.

The decision could be to extract, retain the monolith with a better resource boundary, or gather another observation. The proposal has value because it names the condition and alternatives. It does not need to defend services as an ideology.

Review the boundary with a small table

A decision table can organize facts without converting them into an automatic architecture score.

Question Evidence to inspect Decision implication
Does the module scale differently? Actual workload and resource observations Consider an independent worker if present alternatives are inadequate.
Does a separate team own it? Real review and release responsibilities Independent deployment may help if the team can operate autonomously.
Does it need stronger isolation? Failure or authority being contained Design caller behavior and access boundaries that make containment real.
Does it change with its callers? Recent coordinated changes and contract assumptions Keep coupled behavior together or clarify the contract before separating.
Can the team recover failures? Recorded recovery cases and operator procedure Defer extraction if added failure states remain undefined.

This table is a proposed review aid, not a measured predictor of architecture success. It helps expose the facts that should change the decision.

Keep operations understandable during transition

A partial migration can be harder to operate than either end state. Some jobs may use the old path and others the new worker. A support person needs to know which path handled a particular result.

Record implementation version and route at the job level where appropriate. Preserve a clear authoritative result. Do not let both systems perform the same external write simply because the team is comparing them.

A rollback needs to consider queued work and stored state. Restoring the old code while new jobs remain in another queue can produce inconsistent behavior. The recovery plan should state what happens to pending work and how operators identify completed effects.

After the transition, remove unnecessary compatibility mechanisms when authorized and safe. Temporary adapters and credentials can otherwise become permanent undocumented dependencies. Preserve the decision history while making the current operating path clear.

What Would We Do at Salars?

A proposed Salars software foundation would begin with explicit domain modules and a manageable deployment where the team’s needs permit it. Supplier interpretation, listing preparation, job records, and authorization would have distinct contracts and owners.

The team would not create a separate service for every app or agent automatically. It would examine actual scaling, isolation, release, and ownership needs, then extract a bounded capability when those conditions justified the operating cost.

Before extraction, the team would preserve data ownership, failure behavior, tests, and recovery. A comparison would use real task records and independent correctness criteria. No architecture benchmark or migration result is claimed here.

A modular monolith is a reasoned starting option for a small team, not a promise that one deployment will remain best forever. Clear responsibilities give the team an operating shape it can inspect and maintain.

Sources

Sources inspected October 7, 2026. Architecture examples, extraction triggers, and Salars design are proposed or hypothetical. No deployment comparison or performance experiment was executed for this article.

Discussion

What would you add or question? Add your comment below. A human reviews it before publication.

Loading comments…

Join the discussion

Comments are public after approval. Please do not include links, email addresses, or private information. For one short AI reply, address @AIGuide in your comment or reply to its opening comment. Cloudflare verifies submissions to limit spam. Read our community guidelines.

The wider community forum is also open: Browse article discussions in the forum · Forum home