Monorepo Guide

Monorepo architecture diagram: the runtime graph, not the package graph

By the engineer who builds Datadef, from client work on data platforms · Reviewed August 21, 2026

Nx and Turborepo already draw a graph of your monorepo. It is the dependency graph between packages, built to decide what to rebuild, and it is very good at that job. It is not the architecture. Two services that never import each other but exchange twenty thousand messages a day through a topic are unconnected in the package graph and central to the runtime one. Drawing the second graph needs different files.

7 min readFor teams running many services out of one repository

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

190/20003 credits left
Try:

No account needed · Editable canvas, not a picture

The dependency manifests are the service inventory

The selector classifies package.json, pyproject.toml, go.mod, pom.xml, Cargo.toml, and composer.json as dependency manifests, eight of them per corpus. In a monorepo that is the closest thing to a service registry the repository has: one manifest per deployable, each naming what it is built from. Eight is also the honest limit, so on a sixty-service repo the manifests that get read are the shallow ones, and the rest of the services arrive through their Dockerfiles, their deployment manifests, and the directory counts.

Read together they also reveal the polyglot shape. A repo where three services are Go, two are Python, and the gateway is TypeScript announces that through its manifests before anyone opens a file, and the diagram can show it rather than flattening every service into an identical rectangle.

Per-service Dockerfiles land in the container class, matched on the name prefix so Dockerfile.api and Dockerfile.worker are both taken. Deployment manifests under a cluster-named directory land in the manifest class. Between the three classes, the corpus knows which services exist, what each runs, and where each is deployed.

Where the budget goes, and how ordering decides it

The hard numbers matter more in a monorepo than anywhere else: at most 40 files, 250KB in total, single files truncated past 30KB, anything past 400KB skipped. A repository with sixty services cannot contribute every service file, and no amount of prompt engineering changes that.

Two mechanisms decide what makes it in. Per-class ceilings stop one class from taking the whole budget, so a hundred deployment manifests cannot crowd out the dependency manifests and the API schemas. And inside a class, files are ordered by depth first and then alphabetically, so a root-level or shallow file wins over a deeply nested one. Shallow paths are usually the entry points, which is why the rule leans that way.

The tree summary is the safety net. Every directory to three levels ships with its file count, up to eighty lines, so the generator can see that services/payments holds 42 files even though only its manifest was read. That is what keeps the diagram from silently dropping a service it did not have room to open.

Tree summary excerpt handed to the generator
  . (7 root files)
  services/gateway/ (31 files)
  services/payments/ (42 files)
  services/ledger/ (28 files)
  packages/shared-events/ (14 files)
  deploy/k8s/base/ (19 files)
  .github/workflows/ (6 files)

The edges between services come from contracts and config

The API class carries .proto files, GraphQL schemas, and OpenAPI documents. In a service monorepo those are the interfaces: the proto package that two services share is a real edge, and it is written down rather than inferred.

The rest of the edges come from configuration. The environment blocks in compose files and deployment manifests name the databases each service connects to, the brokers it publishes to, the queues it consumes, and the third-party endpoints it calls. Those become labelled arrows, and they are the arrows that disagree with the package graph.

Set the architecture focus on the first sync when the runtime view is what you want. It moves infrastructure, containers, CI, and deployment manifests to the front of the fetch order and raises their ceilings to 16, 8, 10 and 14, while sampling models and SQL three files deep. The trade to know about: it also cuts the API ceiling from 10 to 4, so a repo whose edges live mostly in shared proto files may be better served by the balanced default. The first-sync proposal states what it found in one sentence, for example: Mostly Kubernetes manifests and Dockerfiles (26 files) with 8 data model and API files.

Naming a boundary

For a very large monorepo, connect the branch or tag that represents the part you care about and let the canvas carry the rest as zones. Teams working the same picture together can share it through team workspaces.

Why the sync matters more here

Monorepos change every day, from many directions at once, which is exactly the condition under which a hand-drawn service map decays fastest. The daily sync regenerates from the branch, skips commits that did not move it, and carries node identity across the regeneration so the diagram updates instead of rearranging itself.

A new service is usually a new directory with a manifest and a Dockerfile, which the next sync classifies without anyone touching the diagram. Put it in the repository README or the platform wiki as a live image and the service map is current by default.

FAQ

How is this different from the Nx or Turborepo graph?

Those graph the build dependencies between packages, to decide what to rebuild. This draws the runtime architecture: which services deploy, what data stores and brokers they use, and which contracts they exchange. Two services that communicate only through a queue are unconnected in a package graph and connected in this one.

Can it handle a repository with dozens of services?

It reads a bounded corpus: at most 40 files and 250KB per sync, with per-class ceilings and shallow-first ordering inside each class. Large repositories are sampled rather than fully read, and the repository tree summary reports every directory to three levels with its file count so nothing large goes unnoticed.

Which files identify the services?

The dependency manifests: package.json, pyproject.toml, go.mod, pom.xml, Cargo.toml, and composer.json. Up to eight of them fit in one corpus, so in a monorepo they act as the service inventory, alongside the per-service Dockerfiles, which are matched on the name prefix, and the deployment manifests under a directory named k8s, kube, kubernetes, manifests, charts, or helm.

Are shared proto or GraphQL contracts used?

Yes. Protocol buffer files, GraphQL schemas, and OpenAPI documents are classified together as API definitions, ten per corpus by default, and a shared contract between two services becomes a real edge in the diagram. Note that the architecture focus lowers that ceiling to four in favour of infrastructure and containers, so a contract-heavy repository may prefer the balanced default.

Which focus should a service monorepo use?

Architecture, in most cases. It promotes infrastructure, containers, CI, and deployment manifests and samples models and SQL lightly, which is the right weighting for a runtime service map. The content focus is the choice when the interesting part of the repository is the data model.