A large Angular repo usually starts failing in small ways first.
Not with a dramatic outage. More often with slower delivery, unclear ownership, accidental coupling, and features that seem to “touch everything.” The code still works, but the team feels heavier every quarter. That is usually not a people problem. It is a structure problem. Angular’s own guidance still leans on consistency, clear organization, and using projects or libraries inside a workspace when code needs to be shared across applications.
The mistake I see most often is organizing the repo around technical types instead of business boundaries. A folder for components, a folder for services, a folder for models, a giant shared folder, and then a slow drift into dependency soup. It looks tidy at first, but it scales badly because teams do not own technical types. They own business areas. Angular has long recommended organizing code into cohesive blocks of functionality, and modern Angular pushes even further toward simpler standalone building blocks instead of piling more structure into NgModules.
So if I were structuring a large Angular codebase for multiple teams today, I would start with one principle: structure the repo by domain, not by artifact type.
What a badly structured repo looks like
You usually know the repo is drifting when a few things start happening at once:
- shared libraries grow faster than feature libraries
- teams change code they do not really own
- a simple feature pulls in half the workspace
- refactors feel risky because dependencies run in every direction
- “temporary” utilities become permanent platform APIs
- new developers cannot tell where a change should go
Angular’s style guidance is really about making code easier to move through and reason about consistently, and that becomes much more important in a multi-team repo than in a small app.
The design principles I would use
I would keep the rules simple.
First, define business domains. Billing, identity, reporting, product catalog, admin, onboarding, search. These become the main units of ownership.
Second, keep feature code close to where it is used. Shared code should be earned, not guessed.
Third, make dependencies point inward toward stable layers, not sideways across random features.
Fourth, prefer standalone Angular building blocks for new code. Angular now recommends standalone components for new development, and existing apps can adopt them incrementally without a breaking rewrite. That matters because standalone makes dependency edges easier to see and reduces structural noise.
How I would define the main layers
For a multi-team codebase, I usually want four broad library types:
1. Domain libraries
These hold business logic, facades, state, APIs, domain models, and domain-specific UI. Ownership is clear here: one team, one area.
2. Feature libraries
These wire together one user-facing workflow or route area inside a domain. They should depend on their domain, not on sibling features.
3. Shared UI libraries
This is for reusable design-system components and primitives, not business widgets wearing generic names.
4. Platform libraries
Auth shell, logging, error handling, analytics, config, cross-cutting infrastructure.
Angular workspaces are explicitly built to contain multiple projects, including applications and shareable libraries, which is exactly the base you want when multiple teams are working in one codebase.
A folder and library shape that scales
I would aim for something like this:
apps/
customer-portal/
admin-portal/
libs/
platform/
auth/
logging/
config/
shared/
ui/
icons/
utils/
domains/
billing/
data-access/
ui/
feature-invoices/
feature-payment-methods/
identity/
data-access/
ui/
feature-users/
feature-roles/
reporting/
data-access/
ui/
feature-dashboards/This is not about naming for its own sake. It is about making it obvious where code belongs and who should review it.
A useful rule here: if a library name cannot be understood by a new developer in ten seconds, it is probably too vague.
Dependency rules I would enforce
This is where structure becomes real.
I would not allow feature libraries to import each other freely. A billing feature should not reach directly into reporting feature internals just because it is convenient. If two areas need the same thing, extract a real shared abstraction or let one domain expose a clean public API.
My default dependency rules would be:
- app -> feature -> domain -> shared/platform
- never feature -> feature across domains
- never shared -> domain
- never shared UI with business logic inside it
- every library has a clear public surface
Angular’s library guidance is useful here: if code is meant to be reused, package it deliberately as a library instead of letting reuse happen by accident.
Ownership matters as much as folders
A repo is not well structured if nobody knows who owns a library.
For multiple teams, every domain library should have:
- a named owning team
- code review responsibility
- dependency rules
- a public API boundary
- a small readme with purpose and allowed imports
That sounds bureaucratic until the repo reaches a certain size. After that, it becomes the difference between steady delivery and endless Slack archaeology.
Governance rules that are worth having
I would keep governance lightweight but firm.
Rule 1: New shared code needs a reason.
Do not create shared libraries just because two features look similar for a week.
Rule 2: Public APIs only.
Teams should import from library entry points, not deep internal paths.
Rule 3: Every cross-domain dependency should be explainable.
If it needs a diagram, it probably needs a redesign.
Rule 4: Prefer duplication over fake abstraction early.
Cleaning up a real pattern later is cheaper than living with the wrong shared layer for two years.
Rule 5: Review architecture continuously, not only during rewrites.
Large repos degrade gradually.
What I would check in an architecture review
If I were auditing a large Angular repo, this is the checklist I would use:
- Are domains obvious from the repo structure?
- Can each team point to what it owns?
- Are most dependencies moving in one direction?
- Is the shared area small and disciplined?
- Are standalone components the default for new code?
- Are feature boundaries aligned with routes and workflows?
- Can a new developer tell where to add a change?
- Are there libraries that exist only because the repo drifted there?
Angular’s current direction around standalone, simplified imports, and library-based reuse helps here, but the framework does not solve repo design for you. You still need explicit boundaries and ownership.
The short version is this:
Large Angular codebases scale when structure matches ownership, and ownership matches business domains.
Once that alignment is in place, delivery usually gets faster for a very boring reason: fewer surprises.
And in a multi-team repo, fewer surprises is a serious architectural advantage.
A lot of teams do not need a rewrite. They need a sharper structure, clearer dependency rules, and a realistic cleanup plan. That is exactly the kind of work that fits an Angular Codebase Audit or a Fractional Angular Technical Lead engagement: less drama, better boundaries, and a repo that stops fighting the team.








