Angular: Keeping it Fat, Dumb, and Happy
An architectural approach to better Angular applications: this article shows you how to keep your Templates declarative and dumb, your Components thin and smart, and your Services fat and happy.

An architectural approach to better Angular applications
This article provides an architectural approach to building your Angular applications. This will definitively answer your questions of how to structure your Angular application so the code is readable, testable, and maintainable.
This article is based on a talk that I gave at the 2019 RVA JavaScript Conference in Richmond, Virginia. You can watch it here:
To get the most out of this article you may want to watch the video while you follow along with the text in the article.
All the code examples in the talk and article are in this GitHub repo.
Defining Terms
We will be looking at Angular Templates, Components, and Services. Specifically, by these terms I mean:
Templates
The HTML part of the component
Components
The TypeScript class part of the component
Services
Injectable classes
Core Principles
These core principles will be our guide:
- Templates: Declarative and Dumb
- Components: Smart and Thin
- Services: Fat and Happy (Specific)
We will look at examples of what we mean by and how to implement these principles in our Angular code.
Why?
In my own experience, following these principles has made my code more readable, testable, and maintainable.
Specifically, this consistent approach makes our code more readable to others, especially those that are familiar with the approach.
Our code is more testable, because Services are easy to test.
It helps us keep our code DRY (Don't Repeat Yourself). Code in Services is easy to use in multiple components.
It helps us leverage Angular features like the async
pipe.
It reduces coupling because we avoid referencing Services directly from our Templates.
OK, let's get started with a discussion of how to approach our Templates.
Templates
Templates are the Lloyd Christmas of Angular.

Templates should be Declarative and Dumb.
Declarative Templates
Your Templates should be Declarative and NOT Imperative.
Declarative code says WHAT to do
Imperative code says HOW to do it
Declarative Templates define what to show. But, they avoid defining exactly how it is shown.
Avoid complicated logic
Declarative Templates avoid complicated logic such as expressions with && and ||. Move complicated expressions into functions in your Components.
Small and simple
Break your HTML up into smaller and simpler Templates. However, be careful that the splitting doesn't just add complexity.
*ngIf
The top template is the only one that should need to worry about the whether the object actually is loaded. Sub-components should usually assume they have a valid object when they are displayed.
<div *ngIf="selectedTask">
<rva-task-detail [task]="selectedTask" (done)="done($event)">
</rva-task-detail>
<rva-task-history [task]="selectedTask">
</rva-task-history>
</div>
In the example above, note that *ngIf
is handled by the parent Template so the rva-task-detail
and rva-task-history
components can just display without checking to see if selectedTask
is valid.
Declarative Template code example
Let's look at some Template code to see the difference between a declarative versus imperative approach.
In one of my projects I had to create a search filter for processes based on their state. Basically, I needed a set of buttons that you could turn on and off to set some flags for the search criteria.

Now I could have just created a specific component with the five buttons, with each button referencing a field in the JSON object. But like I always say:
Why just engineer something when you can OVER-engineer it?
It reminds me of that hilarious xkcd strip where the engineer designs a generic system to pass the salt:

So, I created a generic component that takes a JSON object with a set of Boolean flags and displays a button for each one.
Imperative Template example (Bad)
The imperative version of the Component looks a little like this:
@Component({
selector: 'rva-imperative-flags',
template: `
<div class="btn-group-toggle">
<rva-flag-checkbox
*ngFor="let state of Object.keys(flags)"
[(flag)]="flags[state]"
[label]="state"
>
</rva-flag-checkbox>
</div>
`,
})
export class ImperativeFlagsComponent {
@Input() flags: any;
public Object = Object;
}
Our component uses *ngFor
to display one rva-flag-checkbox
for each key in the flags object.
You can see that this imperative version of the Template explicitly says HOW to get the list of keys by using the static Object.keys()
function. We also have to add the Object
class as a public reference in our Component so the Template can use it.
Declarative Template example (Good)
Let's see if we can improve on this and make our Template declarative:
@Component({
selector: 'rva-declarative-flags',
template: `
<div class="btn-group-toggle">
<rva-flag-checkbox
*ngFor="let state of keys(flags)"
[(flag)]="flags[state]"
[label]="state"
>
</rva-flag-checkbox>
</div>
`,
})
export class DeclarativeFlagsComponent {
@Input() flags: any;
public keys(obj: any) {
return Object.keys(obj);
}
}
Now our template is just saying WHAT it wants. It wants the keys to the flags
object and it is up to the Component to figure out HOW to get it.
However, maybe we can improve on this a bit more. Because if you're like me, you're probably thinking:
I've never met a piece of code that I didn't want to refactor.
Even more declarative Template example (Better)
We can make our Template even more declarative:
@Component({
selector: 'rva-declarativer-flags',
template: `
<div class="btn-group-toggle">
<rva-flag-checkbox
*ngFor="let state of states()"
[(flag)]="flags[state]"
[label]="state"
>
</rva-flag-checkbox>
</div>
`,
})
export class DeclarativerFlagsComponent {
@Input() flags: any;
public states() {
return Object.keys(this.flags);
}
}
Now our Template just directly asks for the states. It doesn't need to know anything about the flags
object.
This introduces a principle that we will see later when we discuss Components:
Component functions should be specific.
Dumb Templates
A Template is Dumb when it doesn't know anything about any other part of the application except for:
- Its Component
- Its sub-component Templates
Specifically, Templates should not directly access the Services in the Component. If a Templates needs something from a Service, it should get it from a Component function. In fact, the component services should almost always be private. Let's make that another one of our guiding principles:
Services should almost always be private in the Component constructor.
Let's look at some code examples to see the difference between smart and dumb Templates.
Smart Template example (Bad)
Here is an example of a Template trying to be smart:
@Component({
selector: 'rva-bad-foo',
template: `
<p>{{fooService.getFoo()}}</p>
`,
})
export class BadFooComponent {
constructor(
public fooService: FooService
) { }
}
In this example the Template needs to know about both the Service and the correct function to call on FooService
. Just imagine how complex this could get if the getFoo()
function had parameters.
Also, notice that we had to make FooService
public.
Dumb Template example (Good)
In this example our Template is dumb and our Component is smart:
@Component({
selector: 'rva-good-foo',
template: `
<p>{{getFoo()}}</p>
`,
})
export class GoodFooComponent {
constructor(
private fooService: FooService
) { }
public getFoo(): string {
return this.fooService.getFoo();
}
}
Our dumb Template just knows that it needs to call the getFoo()
function. Now we can keep the FooService
private.
Furthermore, our dumb component reduces coupling in our application:

Notice that the smart Template is 50% more tightly coupled than the dumb Template.
Remember:
When it comes to Templates:
dumber is better.
So, we have discussed Templates now let's move on to Components.
Components
Components are the rock climbers of Angular.

Components should be Thin and Smart.
Or, if you prefer, you can say it like my friend Lars Gyrup Brink Nielsen:
Components should be lean, mean, view controlling machines.
- Lars Gyrup Brink Nielsen
Like me, Lars likes to talk about Angular Architecture. You can check out one of his talks here:
Smart Components
A smart Component knows and controls exactly what the current state of the Template is.

The Component determines exactly how to interact with the data model or Services based on user activity.
One way we keep our Components smart and our Templates dumb is by having very specific functions in the Component. You should prefer having many small specific functions in your Component rather than a few large generic functions. Let's look at an example:
Generic Component functions
In this example we have a simple Template that displays a Done button and a Remove button. Clicking on one of them should call the appropriate function on the Service.
@Component({
selector: 'rva-fat-buttons',
template: `
<button (click)="handle(action.Done)">Done</button>
<button (click)="handle(action.Remove)">Remove</button>
`,
})
export class FatButtonsComponent {
@Input() task: Task;
public action = Action;
constructor(
private taskService: TaskService
) { }
handle(action: Action) {
if (action === Action.Done) {
this.taskService.completeTask(this.task);
} else if (action === Action.Remove) {
this.taskService.removeTask(this.task);
}
}
}
OK, this code isn't terrible. It has a little bit of a Flux Reducer feel to it. But, we can probably simplify this code to make it more readable.
Specific Component functions
In this refactor of the code we change our single generic function into two specific functions.
@Component({
selector: 'rva-thin-buttons',
template: `
<button (click)="done()">Done</button>
<button (click)="remove()">Remove</button>
`,
})
export class ThinButtonsComponent {
@Input() task: Task;
constructor(
private taskService: TaskService
) { }
done() {
this.taskService.completeTask(this.task);
}
remove() {
this.taskService.removeTask(this.task);
}
}
Notice how this simplifies the code not only in our Component but even in the template.
This exemplifies a principal of Component functions:
Component functions should be specific, NOT generic
Thin Components
So, we discussed the Smart aspect of Components. Now let's discuss what we mean when we say our Components should be Thin.
A Component is Thin when it includes only the code necessary to:
- Manage the view
- Invoke Services
Processing code should be moved into Services.
Another tip for keeping your Components thin is the principal:
Components should not inject HttpClient service.
Wrap any Web Service HTTP calls in a back end data Service.

Thin Components: Testable
One reason to move processing code into Services is that they are typically easier to test than Components. A private function in a Component often becomes a public function in a Service making it easy to test.
These public functions are:
- Easier to test
- Easier to mock
Smarter = Fatter?
At this point you may be asking, "But, doesn't making my component smarter sometimes make it fatter?"
Well yes, sometimes.
There will be times where you will have to decide between some conflicting principals:
- Handle it in the Component (smarter component)
- Handle it in a Service (thinner component)
So how do we decide?
Well the answer is: Like a Boss!

You are probably thinking something like, "OK, cool gif. But what does that even mean?"
When you need to make a decision between doing something in the Component or in the Service, think about your Component as being a smart but rather lazy boss.
The Boss:
- knows exactly what needs to be done in each situation
- makes sure it happens
- delegates actually doing it
So here is another principal for you:
Component: Code that DECIDES
Service: Code that PROCESSES
Let's look at an example of this principal in action:
export class TasksContainerComponent implements OnInit {
public tasks$: Observable<Task[]>;
public selectedTask: Task;
constructor(
private taskService: TaskService,
private taskMultiService: TaskMultiService,
) { }
ngOnInit() {
if (this.taskMultiService.isMulti()) {
this.tasks$ = this.taskMultiService.getTasks();
} else {
this.tasks$ = this.taskService.getTasks();
}
}
public select(task) {
this.selectedTask = task;
}
}
Notice that the code in ngOnInit()
is Code that DECIDES and so it belongs here in the Component.
Most developers that I have seen have a tendency to leave too much code in the Component. Since I have been drinking the Kool-Aid longer, I am often tempted to move too much code into the Service.

This reminds me of an excellent quote from Albert Einstein that is applicable to this situation.

"Everything should be made as simple as possible, but no simpler."
- Albert Einstein
We can paraphrase Einstein to give us a principal for our Components:
Components should be as thin as possible, but no thinner.
Let's move on to look at Services.
Services
Services are the sumo wrestlers of Angular.

Services should be Fat and Happy.
And, Services are happy when they are very specific.
Fat Services
By Fat, I mean that business logic goes into your Services. When in doubt whether to put code into a Service or a Component, put it in the Service. Fatten up that Service and starve your Component.
You may even have a Service that only provides functions for a specific Component. That's OK. These classes that encapsulate presentation related logic are often referred to as Presenters. Typically, these Presenter Services should be in the component specific provider. You can locate the files for this Service in the Component folder.
Happy Services
Services are happy when they can focus on:
- Doing one thing.
- Doing it well.
Like a Sumo wrestler, Services can get very big. However in spite of their size, they are really good at only one specific type of fighting.
Happy Services are SOLID
The theory of SOLID principles was introduced in the paper Design Principles and Design Patterns by Martin. Although, the SOLID acronym was introduced later by Michael Feathers.
Specifically, I want to focus on two of these principles:
Single Responsibility Principle:
A class should have only one single responsibility.
Interface Segregation Principle:
Many client-specific interfaces are better than one general-purpose interface. So don't be afraid of having many small Services.
State-full or Stateless Services?
I sometimes hear the question:
"Should Services be state-full or be stateless?"
The answer is: Yes
What I mean is that your Services should either be specifically about managing state or be completely stateless.
State-full Services
You should use a State-full Service to manage your application state or any other state for that matter. Some examples are:
- A Presenter that helps manage the state of a Component
- An NgRx Store
- An application state Service
Stateless Services
However, if your Service is a set of functions used for processing, keep it Stateless. Stateless Services handle things like:
- Processing
- Data mapping
- HTTP Calls
- Etc.
Favor pure functions in your Stateless Services. Pure functions are functions that:
- Given the same input, always return the same output.
- Don't produce side effects.
Avoid building state into processing Services like REST API wrappers. For example, let's say our application uses a REST API to get information from a back-end data store. In our application we should create a Stateless Service that provides functions for us to call the REST API. Now the Service probably needs some information such as the server URL or other connection information. You may be tempted to create an initialize() function or inject some connection object into the service. Don't do it. Keep that Service stateless and pass that connection object into the functions that need it.
Let's look at an example:
State-full Service Example
In this example we have a Service with an initialize function that takes a Server object with a connection URL.
export class StatefullService {
constructor(
private http: HttpClient
) { }
private server: Server;
public initialize(server: Server) {
this.server = server;
}
public getItems(): Observable<Item[]> {
return this.http.get<Item[]>(this.server.url);
}
}
Note that we now have to remember to call initialize()
first. Also, our Service really should have some error handling code to check if this.server
is undefined
and let the developer know.
Stateless Service Example
In this example we will keep the Service stateless by requiring the Server connection object to be provided as a parameter.
export class StatelessService {
constructor(
private http: HttpClient
) { }
public serverFactory(url: string): Server {
return new Server(url);
}
public getItems(server: Server): Observable<Item[]> {
return this.http.get<Item[]>(server.url);
}
}
You will note that we don't have to remind the developer to invoke the serverFactory()
function. The compiler forces it to be called because that's where we get the Server
object.
I had this exact situation in some of my code and it made supporting a multi-server system very easy.
The bottom line is that Services should be either:
- Completely stateless
- Completely about managing state
Services: Last Words
Remember these principles that relate to services:
Services should almost always be private in the Component constructor.
Components decide what gets done, but Services actually do it.
Services should do one thing and do it well.
Summary
Let's wrap this up with a summary of our guiding principles:
Templates

Declarative and Dumb like Lloyd Christmas
Components

Smart and Thin like rock climbers
Services

Fat but very Specific like sumo wrestlers
If you enjoyed this article, please give the video on YouTube a thumbs up.
Thanks!