ARTICLES WITH CONTEXT

Practical Angular architecture for real life

Complete Angular architecture index geared toward real practice — real problems, real patterns, real decisions. No empty theory.

Practical Angular architecture for real life
14 Apr

Practical Angular architecture for real life

Complete Angular architecture index geared toward real practice — real problems, real patterns, real decisions. No empty theory.

Most Angular architecture courses teach theory. Not this one.

Here you learn to detect real problems, make situated decisions and build systems that endure when the team grows, the product changes and time passes.

Twenty modules. Practice oriented. No padding.


INDEX — Practical Angular architecture for real life

01 — Base architectural problems
  • Business logic in components
  • God services
  • Duplicate or scattered status
  • Coupling between features
  • Mixed responsibilities
  • Folders that look neat but don't scale
  • Premature abstractions
  • Unnecessary over-engineering
  • Architecture designed for today but not for growth
  • Code that is difficult to delete, move or refactor
02 — Actual project structure
  • Feature-based vs layer-based
  • When to use each approach
  • How to detect a structure that does not scale
  • How to divide by domains or bounded contexts
  • How to organize folders for real teams
  • Useful naming conventions
  • What to put and what NOT to put in shared
  • How to check if your current structure supports ×10
03 — Component architecture
  • Components too large
  • Components with too many responsibilities
  • Smart vs dumb components
  • Container/presentational in modern Angular
  • Component composition
  • When to reuse and when NOT
  • How to design clean component APIs
  • Inputs/Outputs vs signals vs services
  • Typical problems in templates
  • How to detect components that are difficult to maintain
04 — Real state in Angular
  • What types of state exist
  • How to detect misuse of state
  • Local state vs shared vs global vs server state
  • Signals vs RxJS
  • State placement
  • When to use service state
  • When to use signal stores
  • When to use NgRx
  • How to detect overcentralization
  • How to detect distributed chaos
  • Typical errors in side effects
  • State normalization
  • Status Checklist
05 — Communication and data flow
  • Data down / events up
  • Correct communication between components
  • Incorrect communication between components
  • Communication between features
  • Signs of hidden coupling
  • Using services to communicate
  • Using signals to communicate
  • When an event bus is a bad idea
  • How to check dependency addresses
  • How to detect hard-to-follow data flow
06 — Data layer and API access
  • Services vs repositories
  • DTOs vs models
  • Transformations and mapping
  • Where to put adapters
  • Typical errors when consuming APIs
  • How to decouple frontend from backend
  • Retry, caching, pagination
  • Infinite scroll
  • REST vs GraphQL from architecture
  • How to check if your data layer is clean or mixed
07 — Routing architecture
  • Design routes with intention
  • UX + SEO in routes
  • Lazy routes
  • Guards
  • Resolve
  • URL as status source
  • Deep linking
  • Coupled routes
  • Badly thought out routes
  • Checklist to review navigation and scalability
08 — Rendering and global strategy
  • SPA, SSR, SSG, ISR
  • How to choose according to context
  • SEO vs complexity
  • Performance vs cost
  • Angular SSR architecture
  • Hydration and architectural impact
  • When NOT to use SSR
  • How to check if an app needs a different rendering strategy
09 — Performance architecture
  • Change detection, OnPush, signals and performance
  • Lazy loading real
  • Code splitting and bundle strategy
  • Caching
  • Virtual scrolling and memoization
  • Performance budgets
  • How to detect architectural bottlenecks
  • What to check before "optimizing"
  • How to distinguish code problem vs architecture problem
10 — Testing architecture
  • What to test and what not
  • Unit vs integration vs e2e
  • Testability by design
  • How to detect difficult-to-test code
  • Mocking with meaning
  • Fragility in testing and overtesting
  • Contract testing frontend-backend
  • Checklist to check if an architecture favors or breaks testing
11 — Nx and real monorepo
  • When it's worth it and when it's not
  • Apps vs libs, boundaries, dependency graph
  • Shared libraries well and poorly made
  • Affected, caching, code ownership
  • How to scale to multiple teams
  • Monorepo anti-patterns
  • Review checklist
12 — Microfrontends and module federation
  • When yes and when no
  • What real problem do they solve?
  • Hidden costs
  • Host vs remotes, versioning, communication between MFEs
  • Independent deployment and organizational complexity
  • Checklist to decide if it pays
13 — Useful design systems
  • What problem do they solve and when is a serious one not needed?
  • Component libraries, tokens, theming, variants
  • Consistency vs flexibility
  • Storybook, design-development sync
  • Typical errors
  • How to check if your UI system is scaling or crashing
14 — Frontend security
  • XSS, CSRF, sanitization
  • Auth architecture: JWT, refresh tokens
  • Guards and role-based access
  • Security issues in SSR
  • Practical revision checklist
15 — Observability and maintenance
  • Logging, error tracking, Sentry, metrics
  • Conceptual tracing, feature flags, A/B testing
  • How to detect blind spots
  • How to check if an app is operable in production
16 — DevEx and platform thinking
  • Developer Experience and tooling
  • CI/CD, generators, schematics, automation
  • How to detect unnecessary friction
  • How to design architecture for teams and not just code
17 — Tradeoffs and decision making
  • How to justify decisions
  • Cost vs benefit, complexity vs scalability, speed vs maintainability
  • Build vs buy
  • How to write ADRs
  • How to defend a decision in an interview or real job
18 — Angular architect anti-patterns
  • Overengineering, useless layers, premature abstractions
  • shared poorly designed, unnecessary global state
  • Cross dependencies, silent coupling
  • Bad modularization, ignore real metrics
  • red flags checklist
19 — Practical audit of Angular apps
  • How to review an existing app
  • What to look at first
  • What signs indicate serious debt
  • How to prioritize problems
  • What to fix first and what not to touch yet
  • How to do a useful architectural review
20 — Real cases and training
  • Ecommerce audit
  • SaaS dashboard audit
  • Enterprise backoffice audit
  • Public app audit with SEO
  • App design from scratch
  • Chaotic app redesign
  • Interview questions
  • Detection, decision and improvement exercises

Module 0 — System Base

Point 0 is not a content module. It is the way of seeing that you will use throughout the roadmap.

Before talking about specific problems in Angular, you need to answer a question:

How ​​do you look at an app you don't know and decide if it's good or bad?

There are four base skills for that.


1. Analyze an app without touching code

The first reading of an app is not in the editor. It's in the structure. Before opening a single file, you can extract information:

  • What are the folders called? Do the names say what they do?
  • Is there a folder shared that weighs more than everything else?
  • How many nesting levels does the structure have?
  • Do the modules or features have domain names or technical names?
  • Where are the services? Loose or within features?

This already tells you a lot about whether whoever made the app thought in business terms or in terms of technical layers.


2. Review architecture in a practical way

Reviewing is not reading code from top to bottom. It is asking questions with criteria:

  • Where does the business logic live?
  • Who knows what in each layer?
  • How many sites do you have to change to add a new feature?
  • Are there dependencies going in the wrong direction?

A senior architect does not start with the most complex component. Start with the highest risk points: shared services, global state, and the data access layer.


3. Detect problems before programming

Most architectural damage occurs in early decisions that seem irrelevant:

  • "I'm putting this on a shared service for now"
  • "The parent component handles this state, then we move it"
  • "We use NgRx for everything, so it's consistent"

These decisions have real consequences months later. The architectural criterion is to know what each decision is buying and what debt it is incurring.


4. Convert theory into real checklist

Each concept we will see in this roadmap — smart/dumb components, state placement, god services, etc. — has to end in a question you can ask yourself while reviewing real code:

  • Does this component know where the data comes from?
  • Does this service have more than one reason to change?
  • Does this state exist in two different places?

If you can't turn a concept into a review question, you haven't internalized it yet.


Module 1 — How to analyze an Angular app without touching code

The first architectural review occurs before opening the editor. Just from the folder structure and file names you can already extract clear signals of quality. This is what a senior architect would do the first 10 minutes with an unknown app.

Why does it matter?

  • If you take a long time to detect architectural problems, the cost of correcting them grows exponentially.
  • A poorly designed structure from day 1 becomes technical debt that blocks the team months later.
  • Developing quick visual judgment allows you to make better decisions before writing a single line of code.

Step 1 — Read the folder structure as a map

The folder structure is the first visible architectural decision. It tells you how the team organized their mental world: do they think in terms of technology or in terms of business?

What is a feature?

A feature is a complete business functionality. Not a file type. Not a technical layer.

Think of an ecommerce app. The features are:

  • The product catalog
  • The shopping cart
  • The checkout process
  • The user profile
  • Order management

Each of those is a piece of business that makes sense on its own. A user enters, browses the catalog, adds to cart, checks out. Those are features.

Model 1 — Organization by technical layers (problematic at scale)

This is what almost everyone does when they start because it seems neat:

src/app/
  components/
    product-card.component.ts
    product-list.component.ts
    cart-item.component.ts
    checkout-form.component.ts
    user-profile.component.ts
  services/
    product.service.ts
    cart.service.ts
    checkout.service.ts
    user.service.ts
  models/
    product.model.ts
    cart.model.ts
    user.model.ts

The real problem: you have to modify the checkout flow. To understand what's involved, you navigate between three different folders — you look for the component in components/, the service in services/, the model in models/. If there is a specific checkout pipe, it is in pipes/ mixed with pipes from other features.

To change a single feature, you navigate through the entire app. That's coupling by structure. When the team grows, two people working on different features touch the same folders constantly → conflicts in git, difficulty knowing who owns what.

Model 2 — Organization by features (what scales)

src/app/
  features/
    catalog/
      components/
        product-card.component.ts
        product-list.component.ts
      services/
        product.service.ts
      models/
        product.model.ts
      catalog.routes.ts
    cart/
      components/
        cart-item.component.ts
        cart-summary.component.ts
      services/
        cart.service.ts
      cart.routes.ts
    checkout/
      components/
        checkout-form.component.ts
        order-confirmation.component.ts
      services/
        checkout.service.ts
      checkout.routes.ts
  shared/
  core/

Why this works: When you tap checkout, everything in checkout is together. A new developer knows exactly where to look. One person can own an entire feature. You can delete an entire feature by deleting only its folder. Git conflicts between different features almost completely disappear.

The key rule of thumb: if you delete a feature, can you delete its entire folder without touching anything else? If the answer is yes, the feature is well isolated. If you have to go looking for pieces throughout the app, it is not.

Step 2 — Read the names

Names are documentation. A bad name hides intent and adds cognitive load to each person who reads the code.

shared/ — what it should contain and what it shouldn't

shared/ is for things that multiple features use and that do NOT have their own business logic.

Correct in shared/:

shared/
  components/
    button/
    modal/
    spinner/
    avatar/
  pipes/
    date-format.pipe.ts
    truncate.pipe.ts
  directives/
    click-outside.directive.ts
  validators/
    email-validator.ts

They are reusable UI pieces or pure utilities. They don't know anything about the business. A ButtonComponent does not know if it is a checkout or user profile button. He only knows how to be a button.

Incorrect in shared/:

shared/
  services/
    cart.service.ts           ← lógica de negocio aquí
    user-auth.service.ts      ← pertenece a auth/core
    product-filter.service.ts ← pertenece a catalog
    report-generator.service.ts ← pertenece a reporting

When you see services with business logic inside shared/, it means that someone didn't know where to put them and put them there. Over time shared/ becomes the catch-all of the app.

core/ — what it is and what it is not

core/ is for singleton infrastructure that needs the entire app, once.

Correct in core/:

core/
  services/
    auth.service.ts
    logger.service.ts
    error-handler.service.ts
  interceptors/
    auth.interceptor.ts
    error.interceptor.ts
  guards/
    auth.guard.ts
  models/
    user-session.model.ts

Incorrect in core/:

core/
  components/
    dashboard.component.ts  ← eso es una feature
    home.component.ts       ← igual
  services/
    product.service.ts      ← pertenece a catalog
    report-generator.service.ts ← pertenece a reporting

These are things that are instantiated once and affect the entire app. The authentication interceptor intercepts all HTTP requests for the entire app, which is why it lives in core/.

Vague names — red flags

A vague name is a postponed decision. When someone creates data.service.ts, they haven't decided what data that service is talking about.

what you see What it can mean
shared/ very big Everything that didn't fit anywhere else. It becomes a mixed bag.
common/ The same as shared/, but worse named. Without criteria of what goes in.
utils/ with services Business logic disguised as utility. Serious red flag.
helpers/ Same as utils/. The name says nothing about responsibility.
components/ in root No organization by domain. All components together.
core/ with 40 files Poorly defined core. shared/ was used as the second.
app.service.ts A god service waiting to grow. Maximum alarm signal.
data.service.ts Data on what? Indefinite responsibility from the name.
helper.service.ts It ends up being a service with unrelated methods.
main.component.ts What is "main"? Generic name that hides real responsibility.

Name rule: If the file name doesn't tell you exactly what it does, the file probably does too much or is poorly located.

Concrete examples of vague names and what they hide:

  • utils/format.ts with 800 lines: mix of date formatting + form validation + API transformation.
  • helper.service.ts: ends up being a god service with unrelated methods.
  • app.service.ts: a service called as the entire app that has responsibilities for the entire app.

Step 3 — Count and measure without opening files

Before reading code, you can make these observations directly from the structure:

Component size as a sign

Sign What does it indicate?
+300-400 lines in one component You are almost always doing too much. It is not an absolute rule but it is worth investigating.
+10 inputs and outputs Too complex API. Candidate to be divided into several components.
Direct HTTP calls in component Mix presentation with data access. Service layer missing.
1-2 components per feature They're probably doing too much. Lack of internal division.
+20 global providers Too much state and centralized logic. Not everything needs to be global.

Global services — the danger of providedIn: 'root'

A global service means that any component of any feature can inject and use it. That creates invisible dependencies between features that should be independent.

// Servicio que DEBERÍA ser local a la feature de catalog:
@Injectable({ providedIn: 'root' })  // ← señal de problema
export class ProductFilterService { ... }

// Ahora cualquier componente de cualquier feature puede usarlo.
// Si catalog cambia su lógica de filtrado, puede romper
// algo en una feature aparentemente no relacionada.

Step 4 — app.module.ts or app.config.ts: what to look for

In apps with modules (Angular < 17 or legacy)

@NgModule({
  imports: [
    BrowserModule,
    HttpClientModule,
    RouterModule,
    AuthModule,       // ← bien, infraestructura singleton

    ProductModule,    // ← señal de problema
    CartModule,       // ← señal de problema
    CheckoutModule,   // ← señal de problema
    UserModule,       // ← señal de problema
    DashboardModule,  // ← señal de problema
    // Si hay 15+ módulos de features aquí,
    // el lazy loading no está funcionando.
  ],
  providers: [
    ProductService,   // ← si hay 20+ providers aquí,
    CartService,      // hay demasiada centralización
    UserService,
    AuthService,
  ]
})

Feature modules should be loaded lazy (on demand), not imported directly into the root module. If they are all here, they are all loaded at startup even if the user never needs them.

In standalone apps (Angular 17+)

// app.config.ts
export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes, withLazyLoading()),   // ← correcto
    provideHttpClient(withInterceptors([authInterceptor])),
    provideAnimations(),

    ProductService,   // ← esto NO debería estar aquí
    CartService,      // ← pertenece a la feature de cart
  ]
}

Global config rule: The global config should only have infrastructure that affects the entire app: router with lazy loading, HttpClient with global interceptors, animations, infrastructure singleton services (auth, logger, error handler). If you see feature services in the global config, someone is registering things globally for convenience, not necessity.

Complete checklist — First reading of an Angular app

Structure

  • Is the structure organized by features (business domain) or by technical layers?
  • Can I delete an entire feature by deleting just its folder without touching anything else?
  • Do the features have business domain names (checkout, catalog) or technical names (list, form, detail)?
  • Are there folders with vague names: utils, helpers, common, misc, data?

shared/ and core/

  • Does shared/ contain only UI components and utilities without business logic?
  • Does core/ contain only singleton infrastructure (interceptors, guards, auth service)?
  • Are there services with business logic within shared/?
  • Does core/ have feature components or domain services?

Services and providers

  • Are the business services within your features or loose globally?
  • Are there more than 20 global providers?
  • Does the root module or app.config.ts import feature modules directly (without lazy)?
  • Are there services with providedIn: 'root' that should be local to a feature?

Names

  • Do the file names say exactly what they do?
  • Are there files whose name could apply to any part of the app?
  • Is there a file named app.service.ts, data.service.ts or helper.service.ts?
  • Does shared/ weigh more than any individual feature?

Exercise

Search for any public Angular project on GitHub — it can be your own or an open source one. Without opening any files, just looking at the folder structure and names:

  1. What organization model do you use: features or technical layers?
  2. What names give you doubts or suspicions?
  3. Where do you suspect the business logic is?
  4. Which folder do you think has the most mix of responsibilities?
  5. Could you delete a feature without touching anything else?

Share what you observe in the session and we review it together as a senior architect would do.


Prompt: analyze your project with the 4 points

Copy this prompt and use it with any AI (ChatGPT, Claude, Copilot) passing it the folder tree of your project:


Analyze the project architecture applying these 4 points of Pillar 1 and generate a report.

POINT 1 — Folder structure

Analyze apps/ and libs/ and answer:

  • Is the organization by features (business domain) or by technical layers?
  • Can you delete an entire feature by deleting only its folder?
  • Do the features have domain names (checkout, catalog) or technical names (list, form, detail)?
  • Are there folders with vague names: utils, helpers, common, misc, data?
  • Shows the actual tree of folders you have found.

POINT 2 — File and folder names

Search the entire project and list:

  • Files with vague names: data.service.ts, helper.service.ts, app.service.ts, main.component.ts, utils with services inside.
  • Services with business logic within shared/.
  • Components or domain services within core/.
  • Does shared/ weigh more than any individual feature? File count.

POINT 3 — Global services

Search the entire project:

  • All services with providedIn: 'root' — lists which ones they are and whether they should be local to a feature.
  • Counts how many global providers there are in total.
  • Identifies globally registered business services when they should be within your feature.
  • Search for services in libs/services/ that clearly belong to a specific domain.

POINT 4 — Global configuration (app.config.ts or app.module.ts)

Find the root configuration file of each app and analyze:

  • What is registered globally that should not be?
  • Are the feature modules loaded with lazy loading or are they imported directly?
  • Are there business services in the global config?
  • How many providers are registered globally in total?

REPORT FORMAT

For each point use this exact structure:

✅ What's good

(concrete list with real code examples)

⚠️ Signs to check

(concrete list with file path and why it is a sign)

❌ Clear problems

(concrete list with file path, problem and actual impact)

In the end it includes:

Executive Summary

A table with the 4 points, a status emoji (✅ ⚠️ ❌) and a conclusion line per point.

Prioritized next steps

List of maximum 5 specific actions ordered by impact, with the exact path of the file or folder to touch.

Be direct. Don't explain what a feature or theory is. Just analyze this specific project and give real findings.

Angular, AI and systems explained from real decisions. Each article gives you something you can apply directly.

SEO in Angular Without Smoke: What to Fix Before Adding SSR