Skip to main content
The Angular 18 Archetype follows a clean, modular architecture based on separation of concerns and best practices outlined in Alberto Basalo’s file and folder structure guide.

Design principles

This architecture is built on several key principles:

Separation of concerns

Each layer has a specific responsibility, preventing code mixing and improving maintainability

Modularity

Components and services are organized into logical groups that can be easily understood and modified

Reusability

Shared functionality is centralized to avoid duplication across the application

Scalability

The structure supports growth by providing clear locations for new features and functionality

Three-layer architecture

The application is organized into three distinct layers:
1

Core layer

Contains app-wide singleton services, guards, interceptors, and layout components that are used throughout the entire application.Location: src/app/core/
The core layer is loaded once when the application starts and provides foundational services like authentication, error handling, and internationalization.
2

Shared layer

Houses reusable components, services, domain types, and UI elements that can be used across multiple features.Location: src/app/shared/
The shared layer promotes code reuse and consistency by centralizing common functionality like state management, utilities, and UI components.
3

Features layer

Contains feature-specific modules that implement business logic and user-facing functionality.Location: src/app/ (feature modules)
Feature modules are typically lazy-loaded for better performance and contain their own components, services, and routing.

Layer interaction

The layers interact following a clear dependency hierarchy:
Dependency rules:
  • Features can import from Shared and Core
  • Shared can import from Core
  • Core should not import from Shared or Features
  • This unidirectional flow prevents circular dependencies

Modern Angular features

This archetype leverages Angular 18’s latest features:

Standalone components

All components are standalone, eliminating the need for NgModules:
@Component({
  selector: 'lab-notifications',
  standalone: true,
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [],
  template: `...`
})
export class NotificationsComponent { }

Signals for state management

The application uses Angular signals for reactive state management:
export class AuthStore {
  #state: WritableSignal<UserAccessToken> = signal<UserAccessToken>(
    this.#localRepository.load('userAccessToken', NULL_USER_ACCESS_TOKEN)
  );
  
  isAuthenticated: Signal<boolean> = computed(() => this.accessToken() !== '');
}

Functional guards and interceptors

Auth logic uses modern functional patterns instead of class-based implementations:
export const authGuard: CanActivateFn = () => {
  if (environment.securityOpen) return true;
  const authStore = inject(AuthStore);
  if (authStore.isAuthenticated()) return true;
  const router = inject(Router);
  return router.createUrlTree(['/auth', 'login']);
};

Configuration

The application uses a centralized configuration approach:

Environment files

Environment-specific settings are managed through the @env/* path alias:
import { environment } from '@env/environment';

Path aliases

TypeScript path aliases simplify imports and improve code organization:
  • @ui/* → Shared UI components
  • @domain/* → Domain types and models
  • @state/* → State management stores
  • @services/* → Shared services
  • @api/* → API service layer
  • @env/* → Environment configuration
Always use path aliases instead of relative imports (e.g., ../../../shared/domain) to maintain clean and maintainable code.

Next steps

Folder structure

Explore the complete directory structure and naming conventions

Core layer

Learn about singleton services, guards, and interceptors

Shared layer

Discover reusable components and state management

Build docs developers (and LLMs) love