Angular components should focus on UI/presentation and user interactions, while services should hold reusable logic, business rules, and side effects (HTTP, caching, state). Clean separation keeps components thin, improves testability, and avoids duplicated logic across the app.
Use this Angular interview question to rehearse a quick answer, common mistake, follow-up, and production pitfall.
What responsibilities belong inside an Angular component vs a service?Frontend interview answer
This Angular interview question tests whether you can explain Angular component vs service: what belongs where, connect it to production trade-offs, and handle common follow-up questions.
- Angular component vs service: what belongs where explanation without falling back to memorized docs wording
- Components and Services reasoning, edge cases, and production failure modes
- How you would answer the most likely Angular interview follow-up
Overview
Use components for rendering + wiring UI events to actions. Use services for reusable logic and side effects (HTTP, caching, state, orchestration). A good component reads like a “view model composer”, not a mini back-end.
Area | Component (belongs here) | Service (belongs here) |
|---|---|---|
UI rendering | Template bindings, UI composition, structural directives, view-only formatting | No DOM/template concerns |
User interaction | Click handlers, form submit handlers, mapping UI events → intent/actions | Input validation rules that are reusable across screens |
State | Ephemeral UI state (open/closed, selected tab, local filters, loading flags for UI) | Shared/stateful app logic (cache, session data, cross-component state), facades |
Business rules | Minimal (only what’s needed to connect UI to rules) | Core rules (pricing logic, permission checks, normalization, mapping DTO→domain) |
Data access | Never call low-level APIs directly (avoid HttpClient usage in components) | HTTP calls, retries, error mapping, caching, request dedupe |
Reusability | Rare (components can be reusable, but should avoid app-specific business logic) | High (same service can be used by multiple components) |
Testing | Shallow: assert template bindings + event wiring + observable usage | Deep: unit test rules/side effects without rendering |
Rule of thumb
If the code depends on how something is shown (DOM, template state, view events) → component. If it depends on what something means (rules, data fetching, caching, orchestration) → service.
Worked example: duplicated business rule across two screens
If both a checkout page and an admin refund page need the same “can this order be refunded?” rule, that logic does not belong in either component. Each component should ask a service/facade for the answer and stay focused on rendering the result.
// ❌ Fat component: rule duplicated in multiple screens
export class OrdersPageComponent {
canRefund(order: Order): boolean {
return order.status === 'paid' && !order.locked && order.total > 0;
}
}
// ✅ Move the reusable rule behind a service boundary
@Injectable({ providedIn: 'root' })
export class OrdersPolicyService {
canRefund(order: Order): boolean {
return order.status === 'paid' && !order.locked && order.total > 0;
}
}
export class OrdersPageComponent {
constructor(private ordersPolicy: OrdersPolicyService) {}
}
// user.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable, shareReplay } from 'rxjs';
export interface UserDto { id: string; name: string; }
export interface User { id: string; displayName: string; }
@Injectable({ providedIn: 'root' })
export class UserService {
private users$?: Observable<User[]>;
constructor(private http: HttpClient) {}
getUsers(): Observable<User[]> {
// Cache + dedupe requests for this session
this.users$ ??= this.http.get<UserDto[]>('/api/users').pipe(
// Business mapping belongs here (or a dedicated mapper)
// so multiple components get consistent data.
// Keep mapping pure.
// DTO -> domain model:
// (If mapping grows, extract to a pure function.)
shareReplay({ bufferSize: 1, refCount: true })
) as unknown as Observable<User[]>;
return this.users$;
}
}
// user-list.component.ts
import { Component, ChangeDetectionStrategy } from '@angular/core';
import { Observable, map, startWith } from 'rxjs';
import { UserService, User } from './user.service';
@Component({
selector: 'app-user-list',
templateUrl: './user-list.component.html',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class UserListComponent {
// UI state belongs here
readonly search = '';
// Data stream comes from a service
readonly users$: Observable<User[]> = this.userService.getUsers();
// UI-only derived state can be computed in the component
// (keep it cheap; heavy transforms should be in a service/facade)
readonly viewModel$ = this.users$.pipe(
map(users => ({
users,
total: users.length
})),
startWith({ users: [], total: 0 })
);
constructor(private userService: UserService) {}
onRefreshClick(): void {
// UI intent: trigger refresh.
// If refresh needs side-effects (invalidate cache), expose that from the service.
// e.g. this.userService.invalidateUsersCache();
}
}
Common mistake | Why it hurts | Fix |
|---|---|---|
HttpClient calls inside components | Duplicates logic, makes components hard to test and reuse | Move data access to a service/facade |
Business rules in templates (complex expressions) | Hard to read, hard to test, runs every CD cycle | Compute in TS (component for UI-only, service for reusable rules) |
Services manipulating the DOM | Breaks separation, complicates SSR/testing | Keep DOM work in components/directives; services stay UI-agnostic |
“God component” that owns everything | Becomes unmaintainable; PRs become risky | Extract services + presentational components; keep the container thin |
Extra nuance: service scope
Most services are singletons (providedIn: 'root'). If you need per-component instance state (e.g., a wizard session), provide the service at the component level via providers: [WizardStateService] so each component instance gets its own copy.
Keep components focused on UI + event wiring + lightweight view-model shaping. Put reusable logic, side effects, data access, and business rules into services (or facades). This separation improves testability, reuse, and long-term maintainability.
Use this as one explanation rep, then continue with the Angular interview questions cluster or a guided prep path.