How to Structure Maintainable Angular Project

Michal Bujalski
The Startup
Published in
3 min readOct 10, 2020
Photo by Sven Mieke on Unsplash

Although Angular by design handles project complexity well it’s often hard to preserve good organisation as the project develops and as new functionalities come into place and old need to be altered.

When choosing a project structure we want to:

  • compartmentation of services
  • components should have as little service injected as possible
  • components should have as little logic as possible
  • use Clean Architecture elements
  • keep to SOLID principles

The concept

I’m assuming you are already using a well organised project structure — if not please read this article.

I am not saying that this is a unique approach — I’d call it setting a few rules how to organise dependencies.

Basically we want to have three layers of our project structure. It roughly corresponds to layers in Clean Architecture. Components, ApiServices and Stores represent low level parts and our Component Service is our domain layer.

Low level services.

These service touch low level apis and implement very specific tasks. This should include services that:

  • handle api calls
  • handle persisting data
  • browser apis
  • use other low level apis (e. g. Geolocation API)

Component-services

These should be created per component. Ideally they should not use any low level dependencies (e. g. routing, http clients). Try to inject only your abstractions.

It’s main purpose is to provide all the necessary data for components, and handle any actions from ui. Basically the idea is to take data from sources, manipulate it and return models that are ready to be displayed so that UI does as little work as possible.

Components

Should have minimum logic or only UI logic. Avoid manipulating your business data directly here — such manipulation should be done in component-service

Example

This simple project with one component implements three features:

  • fetching data from api
  • persisting fetched data
  • search done according to user-input query

ProductsStoreService and ProductsApiService are our low level components and ProductsListComponent is our domain layer containing all business logic. Component itself is only responsible for displaying and passing user input to service

Gotchas

  • Try to inject only one component-service per component. This is not strict rule — generally it’s better to have injected 2 or 3 componentt-services, than have just one huge class.
  • Low level services can have global scope and can be reused in different places. You will probably need to reuse things like data stores and api calls in other parts of your app.
  • Component-services should have component scope. Generally they should handle only logic per-one component lifecycle.

Benefits

  • because your component-services contain core logic and have only abstractions injected they are easily testable without with plain jasmine and without TestBed
  • a common template for most of screens
  • need to make changes in the api call ? Need to change the way to store your date in local storage instead of class property? No problem — just edit the services responsible without any impact of other services and components
  • keeps your components small and (ideally) simple

That’s it! Tried to keep it as simple as possible. Hope you find this usefull.

--

--