| name | angular-clean-architecture |
| description | Scaffolds and extends Angular 18+ standalone features using Clean Architecture with DDD layering (presentation/application/domain/infrastructure), custom signal-based stores, facade pattern, and ports/adapters dependency inversion. Use when creating new Angular features/domains, adding use cases/facades/stores/ports/adapters, refactoring legacy NgModule/NgRx code toward clean architecture, or working with cross-domain communication via context registry. |
Angular Clean Architecture + DDD
When To Activate
- Scaffolding a new Angular feature/domain following Clean Architecture
- Adding a use case, facade, store, port, or adapter to an existing domain
- Creating standalone components that consume store state via facades
- Refactoring a legacy/mixed module (NgModule/NgRx) toward Clean Architecture
- Moving business logic out of components into facades/use-cases
- Adding or updating cross-domain communication via the context registry
- Standardizing feature state management with BaseStore
- Working with the custom BaseStore, ResourceState, or store operators/decorators
Architecture Anchors (Verify Before Coding)
Before writing code, verify these anchors exist in the active branch:
| Anchor | What to look for |
|---|
| Reference clean module | At least one domain following the full clean architecture structure (application/domain/infrastructure/presentation) |
| Base store | A BaseStore class with signal-based state management and ResourceState<T> |
| Store loading operator | An RxJS operator (e.g., handleStoreLoading) bridging Observables to store updates |
| Context registry | A registry mapping cross-domain providers (e.g., contextProvidersFor()) |
If one or more anchors are missing:
- Do NOT invent imports or APIs
- Continue with best-effort refactor using existing patterns
- Report the mismatch explicitly in your final message
Hard Rules
- Never use
any — use unknown if the type is truly unknown
- Never inject
UseCase classes directly into components — always go through a Facade
- Never inject stores directly into components — facades expose store signals
- Components must depend on facades for ALL domain interactions
- Keep domain and application logic independent from Angular UI details
- Use typed models for state, DTOs, and context payloads — no untyped objects
- Domain layer has ZERO infrastructure or framework dependencies
- Use
inject() for all dependencies, never constructor params
Architecture Overview
src/app/<feature-area>/
├── <domain>/
│ ├── application/ # Application layer (orchestration)
│ │ ├── facades/ # Public API for components
│ │ ├── use-cases/ # Single-responsibility business operations
│ │ └── store/ # Signal-based state management
│ │
│ ├── domain/ # Domain layer (pure business logic, ZERO infra deps)
│ │ ├── models/ # Immutable TypeScript types
│ │ ├── ports/ # Abstract classes (dependency inversion)
│ │ ├── rules/ # Pure validation functions & constants
│ │ └── mappers/ # Pure data transformation functions
│ │
│ ├── infrastructure/ # Infrastructure layer (external concerns)
│ │ ├── adapter/ # Port implementations
│ │ ├── api/
│ │ │ ├── endpoints/ # HTTP client wrappers
│ │ │ ├── request/ # API request DTOs
│ │ │ └── response/ # API response DTOs
│ │ └── <domain>-infrastructure.providers.ts
│ │
│ ├── presentation/ # Presentation layer (UI)
│ │ ├── list/ # Feature pages
│ │ ├── details/
│ │ ├── create/
│ │ ├── edit/
│ │ └── forms/ # Reusable form components
│ │
│ ├── routes.ts # Lazy-loaded route definitions
│ ├── routes.constants.ts # Route path constants
│ ├── <domain>-service.providers.ts # All DI bindings for this domain
│ └── public-api.ts # Barrel exports for cross-domain use
Dependency Rules (CRITICAL)
Component --> Facade --> UseCase --> [Port] <-- Adapter --> Endpoint --> HttpClient
| | | ^ |
Presentation Application Application Domain Infrastructure
- Domain has ZERO infrastructure dependencies — only defines ports (abstract classes), models (types), rules (pure functions), and mappers.
- Application depends on Domain only — facades orchestrate use cases and stores; use cases call ports.
- Infrastructure depends on Domain only — adapters implement ports using HTTP clients and API services.
- Components NEVER inject use cases directly — always inject facades.
- Components NEVER inject stores directly — facades expose store signals.
- Cross-domain communication uses the Context Registry — never import another domain's internals.
Store System
The store uses a custom BaseStore with signal-based state and ResourceState<T> wrapping.
For full store details: See store-system.md — covers ResourceState<T>, BaseStore API, KeyedResourceData, handleStoreLoading/handleKeyedStoreLoading operators, @AutoStartLoading and @AppCache decorators.
Layer Implementation Templates
Condensed patterns for each layer. For full templates with code: See layer-templates.md.
| Layer | Key Conventions |
|---|
| Domain Model | Use type (not interface/class), optional ?: props, group by entity |
| Domain Port | abstract class with abstract methods returning Observable<T>, Port suffix, one per operation (ISP) |
| Domain Rules | Pure functions, as const constants, zero framework deps |
| Domain Mappers | Pure mapXToY functions, Partial<T> returns, null-safe |
| Use Case | @Injectable() (no providedIn), inject ports via inject(), single responsibility |
| Facade | @Injectable() (no providedIn), inject store + use cases, expose signals via getters, @AppCache + @AutoStartLoading + handleStoreLoading for actions |
| Adapter | implements port, inject endpoint, transform DTOs to domain models |
| Endpoint | HttpClient + UrlBuilder + PaginatedRequestBuilder |
| Infra Providers | Function returning Provider[], bind ports to adapters |
| Service Providers | Aggregates facades + use cases + infra + contextProvidersFor() |
| Routes | loadComponent lazy loading, route-level providers, functional guards |
| Public API | Barrel exports: models, ports, adapters, store, provider functions |
| Component | Standalone, OnPush, inject() only, facade-only injection, signal()/computed()/effect(), input()/output() signals, SCSS, project selector prefix |
Cross-Domain Communication
Uses a ContextRegistry with contextProvidersFor(). For full patterns: See cross-domain.md.
Testing Patterns
For full testing patterns by layer: See testing-patterns.md.
| Test Target | Mock | Verify |
|---|
| Facade | Store + use cases | Orchestration logic |
| Component | Facade only | Rendering + event delegation |
| Use case | Ports | Delegation + business logic |
| Adapter | Endpoints | DTO-to-model mapping |
Target 80%+ coverage.
Naming Conventions
| Artifact | Pattern | Example |
|---|
| Store class | <Domain>Store | CustomersStore |
| Store enum | <Domain>StoreEnum | CustomersStoreEnum |
| Store state type | <Domain>State | CustomersState |
| Facade | <Domain>Facade | CustomersFacade |
| Use case | <VerbNoun>UseCase | GetCustomersUseCase |
| Port (abstract) | <VerbNoun>Port | GetCustomersPort |
| Adapter | <VerbNoun>Adapter | GetCustomersAdapter |
| Endpoint | <Domain>Endpoint | CustomersEndpoint |
| Component | <prefix>-<feature>-<name> | app-customers-list |
| Service providers fn | <domain>ServicesProviders() | customersServicesProviders() |
| Infra providers fn | <domain>InfrastructureProviders() | customersInfrastructureProviders() |
| Context providers | <DOMAIN>_CONTEXT_PROVIDERS | CUSTOMERS_CONTEXT_PROVIDERS |
| Route constants | routes.constants.ts | N/A |
| Public API | public-api.ts | N/A |
| Spec files | <name>.spec.ts | customers.facade.spec.ts |
| Model types | <Entity> (PascalCase type) | Customer, CustomerFilters |
| Business rules | <domain>-<concern>.rule.ts | customer-fields.rule.ts |
| Mappers | <source>-mapper.ts | enterprise-mapper.ts |
Implementation Playbook (Add a New Feature)
Follow these steps in order. Full templates for each step in layer-templates.md.
- Define domain models in
domain/models/
- Define domain ports in
domain/ports/ — abstract class, Observable<T> returns
- Add business rules in
domain/rules/ (if needed)
- Implement infrastructure adapter in
infrastructure/adapter/
- Create API endpoint in
infrastructure/api/endpoints/
- Register infrastructure providers — bind ports to adapters
- Create use case in
application/use-cases/
- Define store in
application/store/ — extend BaseStore
- Create facade in
application/facades/ — wire store + use cases
- Create service providers — aggregate all DI bindings
- Create routes with lazy-loaded components and route-level providers
- Create standalone components — facade-only injection, signals, OnPush
- Create public API barrel exports
- Write tests (see testing-patterns.md)
- Register context if cross-domain access needed (see cross-domain.md)
Mixed-to-Clean Refactor Workflow
For full migration guide: See migration-guide.md.
Summary: Analyze current module → Introduce facade boundary → Extract use cases/ports → Isolate infrastructure → Align store to BaseStore → Replace direct domain coupling with context registry → Validate.
Legacy Patterns (What NOT to Replicate)
| Legacy Pattern | New Pattern |
|---|
| NgRx actions/effects/reducers | Custom BaseStore + handleStoreLoading |
StoreModule.forFeature() | BaseStore with providedIn: 'root' |
Direct Store.dispatch() in components | Facade methods |
Direct Store.select() in components | Facade getter returning store signal |
| Services with BehaviorSubject state | BaseStore with ResourceState |
| Constructor injection | inject() function |
| NgModules | Standalone components + route providers |
@Input() / @Output() decorators | input() / output() signal functions |
Checklist: Adding a New Feature
Review Checklist (Before Finalizing)
Expected Output Style for Agent Responses
When completing work on this codebase, include in your final message:
- What changed and why — brief summary of modifications
- Which layer boundaries were enforced — e.g., "presentation depends only on facade, domain has zero infra deps"
- Which files show facade/store/context integration — key file paths demonstrating the patterns
- Any unresolved architecture mismatches — if anchor files were missing or legacy patterns couldn't be fully migrated, report explicitly