Skip to main content

Best Practices and Patterns

Level: Intermediate

ℹ️ What You'll Learn
  • Architecture patterns
  • Code organization
  • Performance optimization
  • Security best practices
  • Testing strategy

Architecture Pattern

src/
├── app/
│ ├── core/ # Singleton services
│ │ ├── auth.service.ts
│ │ ├── http.interceptor.ts
│ │ └── guards/
│ ├── shared/ # Shared components
│ │ ├── navbar/
│ │ ├── footer/
│ │ └── pipes/
│ ├── features/ # Feature modules
│ │ ├── students/
│ │ ├── exams/
│ │ └── fees/
│ └── app.component.ts

Core = services used once. Shared = reusable components. Features = feature modules.

Smart/Dumb Components

// Smart component (container)
@Component({
selector: 'app-student-container',
template: `
<app-student-list
[students]="students$ | async"
(delete)="onDelete($event)">
</app-student-list>
`
})
export class StudentContainerComponent {
students$ = this.service.getStudents();

constructor(private service: StudentService) {}

onDelete(id: number) {
this.service.deleteStudent(id).subscribe();
}
}

// Dumb component (presentational)
@Component({
selector: 'app-student-list',
template: `
<tr *ngFor="let student of students">
<td>{{ student.name }}</td>
<button (click)="onDelete(student.id)">Delete</button>
</tr>
`
})
export class StudentListComponent {
@Input() students!: any[];
@Output() delete = new EventEmitter<number>();

onDelete(id: number) {
this.delete.emit(id);
}
}

Smart = data, logic. Dumb = UI only.

Unsubscribe Pattern

export class StudentListComponent implements OnInit, OnDestroy {
private destroy$ = new Subject<void>();
students: any[] = [];

constructor(private service: StudentService) {}

ngOnInit() {
this.service.getStudents()
.pipe(takeUntil(this.destroy$))
.subscribe(data => this.students = data);
}

ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
}

takeUntil auto-unsubscribes.

Performance Tips

// Use OnPush change detection
@Component({
changeDetection: ChangeDetectionStrategy.OnPush
})

// Lazy load routes
{
path: 'students',
loadChildren: () => import('./students').then(m => m.ROUTES)
}

// TrackBy in ngFor
<tr *ngFor="let student of students; trackBy: trackByStudentId">

trackByStudentId(index: number, student: any) {
return student.id;
}

// Use async pipe
students$ = this.service.getStudents();
<tr *ngFor="let student of students$ | async">

Security Best Practices

// Always validate input
constructor(private sanitizer: DomSanitizer) {}

safeHtml = this.sanitizer.sanitize(SecurityContext.HTML, userInput);

// Don't store secrets in code
// Use environment variables
apiUrl = import.meta.env.NG_APP_API_URL;

// HTTPS only
// Protect against XSS
// CSRF tokens in requests

Testing Strategy

// Unit test services
// Integration test components
// E2E test user flows

describe('StudentService', () => {
it('should load students', () => {
// Unit test
});
});

describe('StudentListComponent', () => {
it('should display students', () => {
// Integration test
});
});

Code Style

// ✓ Good
private students$ = this.service.getStudents();

// ✗ Avoid
private students = this.service.getStudents();

// ✓ Use observables
students$ = this.service.getStudents();

// ✗ Avoid manual subscription
students: any[];
this.service.getStudents().subscribe(data => this.students = data);

SMS Best Practices

  • Feature modules per domain (Student, Exam, Fee)
  • Core services for API/Auth
  • Smart/Dumb component split
  • Lazy load feature modules
  • Unsubscribe pattern
  • OnPush change detection
  • TrackBy in loops
  • Async pipe

Key Takeaways

  • Architecture = clear structure
  • Smart/Dumb = separation of concerns
  • Unsubscribe = memory leaks prevention
  • Performance = lazy load, OnPush, TrackBy
  • Security = validate input, HTTPS
  • Testing = unit, integration, E2E
💡 Backend Developer Tip

Architecture patterns similar to layered architecture in .NET. Core services like repositories, features like controllers/services.

⚠️ Anti-Patterns
  1. Everything in app.component — No organization
  2. Subscribing in component — Use async pipe
  3. No lazy loading — Large bundles
  4. Manual unsubscribe — Memory leaks
  5. Logic in templates — Hard to test
🤖Use AI to Learn Faster

Use ChatGPT, Claude, or Copilot to go deeper on Best Practices. Try these prompts:

  • "Why use Smart/Dumb component pattern?"
  • "How do you prevent memory leaks?"
  • "What's benefit of OnPush change detection?"
  • "Quiz me on Angular patterns"

💡 Tip: After reading this article, paste your own code into AI and ask "What could go wrong here and why?" — fastest way to find edge cases and deepen understanding.

nexcoding.in