Building Bulletproof Angular Apps with Bloc, NgRx, and Clean Architecture (Part 1/2)
“A scalable architecture is like a good joke — it only makes sense when it clicks everywhere.”
🏗️ Building Bulletproof Angular Apps with Bloc, NgRx, and Clean Architecture (Part 1/2)

“A scalable architecture is like a good joke — it only makes sense when it clicks everywhere.”
👋 Welcome, dev!
You’re here because you’ve had enough of the spaghetti. Components bloated with logic. Services that do everything. State floating around like loose change.
This blog series is your guide to writing Angular apps that are:
- 🚀 Scalable
- 🧼 Clean
- 🧪 Testable
- 😌 Maintainable
And fun to work on.
We’re going to introduce the Bloc(Business Logic Component) pattern, use NgRx like a pro, and wrap everything in the bliss of Clean Architecture.
By the end of Part 2, you’ll have a complete working example called Taskify — a smart little task manager with CRUD operations and an architecture ready for growth.
⚠️ The Problem: Why Angular Apps Rot Over Time
It starts simple. Then comes feature creep. Before you know it, every component is a god component.
Take this classic example:
@Component({ ... })
export class TodoComponent {
constructor(private http: HttpClient) {}
ngOnInit() {
this.http.get('/api/tasks').subscribe(...);
}
complete(task: Task) {
this.http.post('/api/complete', task).subscribe(...);
}
}
Looks innocent. But what’s wrong?
- ❌ Business logic in the UI
- ❌ Direct API calls in the component
- ❌ No scalable test coverage
- ❌ Unscalable, unreadable, un-reusable mess
And then the PM says: “Add sorting, filtering, bulk edit, offline mode, and sync with Slack.” 😱
This is when you realize… you need a real architecture.
🧼 Enter: Clean Architecture + Bloc Pattern + NgRx
“Think layers. Think responsibilities. Think contracts.” — Uncle Bob (probably)
❓ Why This Architecture?
Modern Angular apps deserve more than just working code — they need predictability, testability, and separation of concerns. This architecture checks all boxes:
- Bloc Pattern helps you orchestrate business logic in a reactive, composable way
- NgRx gives you powerful state management and side-effect handling
- Clean Architecture gives your code long-term maintainability and clear boundaries
Instead of cramming logic into components and letting services do everything, this structure gives every layer a role, every decision a place, and every bug a shorter path to fix.
🧠 Why Bloc? Why Not Just NgRx Alone?
NgRx is powerful — but writing clean feature logic with it alone often leads to:
- Scattered selectors and effects
- Feature coupling with global state
- Difficult-to-test logic tied to NgRx constructs
Enter Bloc, short for Business Logic Component (borrowed from Flutter). In Angular, it maps to a Facade + Use Cases, providing:
✅ Central point for feature orchestration ✅ Encapsulation of domain logic ✅ Easy unit testing (without mocking NgRx itself) ✅ Clear inputs (commands) and outputs (observables)
Bloc lets NgRx focus on state, while Bloc handles business decisions.
Here’s our three-headed hero:
🧠 Bloc: Business Logic Orchestration
Inspired by Flutter/Dart, it’s all about:
- Input (event)
- Processing (logic)
- Output (state)
In Angular, this maps beautifully with NgRx Effects and Facades.
🌪️ NgRx: Reactive State Management
You get:
- Predictable global state
- Unidirectional flow
- Time-travel debugging (DevTools FTW)
- Clean separation of UI and logic
🧱 Clean Architecture: Separation of Concerns
Break your app into layers:
1. Core Layer (🧠 Domain)
- Entities (models)
- Use Cases (business logic)
- Interfaces (abstract contracts)
2. Infrastructure Layer (🔌)
- Repositories (data access logic)
- API clients
- Mappers (API ↔ Domain)
3. Feature Layer (🎨)
- NgRx state (actions, reducers, effects)
- Facades (Bloc-style glue)
- UI (components)
🧱 Suggested Folder Structure
/src/app/
├── core/ # Pure logic — no Angular here
│ ├── models/
│ ├── use-cases/
│ └── interfaces/
├── infrastructure/ # API & repositories
│ └── repositories/
├── features/
│ └── task/
│ ├── application/ # Bloc-style Facade
│ ├── state/ # NgRx actions/effects/reducer
│ └── presentation/ # Standalone components
└── shared/ # UI libs, pipes, buttons, etc
🔄 Bloc + NgRx = A Match Made in State Heaven
Here’s the diagram :

🛠️ Setting the Stage: The App — Taskify
We’ll build a smart little task manager:
- ✅ Add task
- ✏️ Update task
- ❌ Delete task
- 📋 View tasks
Let’s dive into the first half of the implementation — the Domain and Infrastructure layers.
💡 Part 1: Core and Infrastructure Layers
🔹 Task Model & Interfaces
// core/models/task.model.ts
export interface Task {
id: string;
title: string;
completed: boolean;
}
// core/interfaces/task-repository.interface.ts
export interface ITaskRepository {
getAll(): Observable<Task[]>;
add(task: Task): Observable<Task>;
update(task: Task): Observable<Task>;
delete(id: string): Observable<void>;
}
🔹 Use Cases (Pure Logic)
export class GetTasksUseCase {
constructor(private repo: ITaskRepository) {}
execute(): Observable<Task[]> {
return this.repo.getAll();
}
}
export class AddTaskUseCase {
constructor(private repo: ITaskRepository) {}
execute(task: Task): Observable<Task> {
return this.repo.add(task);
}
}
// Same for UpdateTaskUseCase and DeleteTaskUseCase
🔌 Repository Implementation
@Injectable()
export class TaskRepository implements ITaskRepository {
constructor(private http: HttpClient) {}
getAll(): Observable<Task[]> {
return this.http.get<Task[]>('/api/tasks');
}
add(task: Task): Observable<Task> {
return this.http.post<Task>('/api/tasks', task);
}
update(task: Task): Observable<Task> {
return this.http.put<Task>(`/api/tasks/${task.id}`, task);
}
delete(id: string): Observable<void> {
return this.http.delete<void>(`/api/tasks/${id}`);
}
}
You’ve now got your business logic and data fetching completely decoupled from the UI and state. 🎉
✅ What You’ve Built So Far
- Core / Models :
Taskinterface → Represents the domain entity. - Core / UseCases :
AddTaskUseCase,UpdateTaskUseCase, etc. → Contains business logic — pure and testable. - Interfaces:
ITaskRepository→ Defines the contract for data access — abstract, no implementation. - Infra:
TaskRepository→ Implements theITaskRepositoryand connects to the actual data source (API, DB, etc).
Next up? NgRx store, effects, reducer, and facade, plus UI layer in Part 2.
✌️ Up Next: Part 2 — From Actions to UI
In the next post, we’ll:
- Build NgRx actions, reducers, and effects
- Write a Bloc-style TaskFacade
- Create a clean presentational component
- Add testing strategies
- Drop some 🔥 pro tips
Until then — clean code, happy state, and may your reducers be pure. 🧼
메타데이터
- post_id
- 2cc1daff4e3b
- slug
- building-bulletproof-angular-apps-with-bloc-ngrx-and-clean-architecture-part-1-2-2cc1daff4e3b
- url
- https://medium.com/@bhavanpatel/building-bulletproof-angular-apps-with-bloc-ngrx-and-clean-architecture-part-1-2-2cc1daff4e3b
- canonical_url
- https://medium.com/@bhavanpatel/building-bulletproof-angular-apps-with-bloc-ngrx-and-clean-architecture-part-1-2-2cc1daff4e3b
- author_url
- https://medium.com/@bhavanpatel
- status
- ok
- fetched_at
- 2026-07-06 21:57:15