Are CRUD Services Necessary in your Angular Application?

John Youngers
Youngers Consulting
3 min readMar 24, 2018
Angular Styleguide

A long, long time ago, in the heyday of AngularJS, the way to communicate with your API was to wrap the calls in a Service: you could then inject the service into your component, and mock the service out in your component tests. All was well.

Now that we have Angular Modules and Typescript Modules, the question is: are these CRUD services needed anymore?

Most applications are setup such that there’s an Angular Module per website area (the high level menu links). If you have a Todos application containing the modules DashboardModule, SearchModule, AdministrationModule, where each has the need to call some sort of getTodos() function that pulls your data from the API, if you set this function up in a service, which module does the service belong in? What module does the Todo interface/class belong in? Do you need to create a high level SharedModule or TodosModule that the other modules all reference?

What happens if this service maintains some sort of state (it shouldn’t, but let’s say it has an internal caching mechanism so that API calls are only made once), and you unknowingly set these modules up incorrectly: you may end up with multiple instances of the same service.

My solution to this problem was to move all of my application entity details into a common folder: nothing in this folder is specific to Angular in any way — it’s just a collection of interfaces and functions.

In our Todos example, we’d have something like this:

The model file contains the interface for the entity, along with any API functions we may need. In the spec file, we verify we’re calling the API correctly, and we can optionally include a getMockItems() like function if this is an entity that gets tested with often.

I’m generally not a fan of barrel/index files, but for this app/common folder I’ve decided to include one. This way as components need to pull in various entity interfaces or API functions, they can all come from the same import, and it also allows us to restructure the app/common folder as our application expands without having any external impacts. Along with the index.ts file, I’d also create a spec.ts file to group any getMockEntities() functions:

Using this structure there’s no longer a guessing game as to which module types/interfaces belong, or where their corresponding services should go.

At this point you may be saying, “the primary reason for having the services was for dependency injection and testing: if I’m not injecting the service, how can I properly test my components?”. You’ll have two options:

  1. These functions all likely will need to take in some sort of service that makes the actual API calls: you can instead mock that service out. This is cumbersome if your component makes several API calls — you’d need to order the API responses correctly in your spy.
  2. Although we’re not using Angular’s dependency injection, it’s still possible to mock these functions out by adjusting how we import them into our spec file:

If you’re using a Redux/NgRx pattern, it’s likely this isn’t as big of an issue: you may only be making these API calls in a single Effect. Even if that is the case, I’d still recommend pulling the call out into one of these functions to abstract away the details of how your API works: if you (for some terrible reason) need to make a large change to your API, you’ll know the only impact should be to files in your app/common folder, and you won’t need to peck around various .effect.ts files to do the refactor.

--

--