liveBook · Manning

Repository pattern

As explained in Pro Angular 16

A repository is an abstraction that mediates access to data while hiding how that data is obtained from application components.

A repository sits between a data source and the application components that need its data. The data source obtains product records; the repository distributes them and provides operations on them, so components need not know how the records were obtained. An Angular data model can pair a descriptive Product class with a data source and a repository. section 5.3.3 section 9.6.1

Start with the operations callers need. A product repository can return every product, find one by ID, save a new or existing product, and delete one. Its first data source can simply hold an in-memory array; the repository gives application code a place to access and manipulate that data. section 9.6.1

Table 9.8. The methods defined in the repository
Name
Description
getProducts
This method returns an array containing all the Product objects in the model.
getProduct
This method returns a single Product object based on its ID.
saveProduct
This method updates an existing Product object or adds a new one to the model.
deleteProduct
This method removes a Product object from the model based on its ID.

The method table gives a compact starting interface for product lookup and changes. section 9.6.1

Signals offer another way to expose repository data. A product repository can publish the data source’s products signal and derive a categories signal with computed. The category calculation maps products to category names, substitutes “(None)” for missing categories, removes duplicates, and sorts the result. It is recomputed when the underlying products signal changes. section 5.3.3

Listing 5.13. The contents of the product.repository.ts file in the src/app/model folder
import { Injectable, Signal, computed } from "@angular/core";
import { Product } from "./product.model";
import { StaticDataSource } from "./static.datasource";
 
@Injectable()
export class ProductRepository {
    products: Signal<Product[]>;
    categories: Signal<string[]>;
 
    constructor(private dataSource: StaticDataSource) {
        this.products = dataSource.products;
        this.categories = computed(() => {
            return this.dataSource.products()
                .map(p => p.category ?? "(None)")
                .filter((c, index, array) => 
                    array.indexOf(c) == index).sort();
        })
    }
 
    getProduct(id: number): Product | undefined {
        return this.dataSource.products().find(p => p.id == id);
    }
}

The complete ProductRepository shows constructor injection, lookup by ID, and a computed categories signal in one file. section 5.3.3

If callers should read products but not change them directly, keep a writable signal private and expose its read-only form. Route changes through repository methods instead. Components can then derive their own computed values from the exposed signal; removing a product updates values affected by the changed data. section 10.4.4 section 20.2.2

The data source can change without moving data-loading details into components. For an asynchronous REST source, inject RestDataSource, subscribe to its getData result, and set the repository’s products signal when records arrive. This replaces the earlier constructor step that set the signal from a static source. section 20.2.2 section 23.3.3

Listing 23.7. Using the new data source in the repository.model.ts file in the src/app/model folder
import { Injectable, Signal, computed, signal } from "@angular/core";
import { Product } from "./product.model";
//import { StaticDataSource } from "./static.datasource";
import { RestDataSource } from "./rest.datasource";

@Injectable()
export class Model {
    private products = signal<Product[]>([]);
    private locator = (p: Product, id?: number) => p.id == id;

    constructor(private dataSource: RestDataSource) {
        //this.products.set(this.dataSource.getData());
        this.dataSource.getData()
            .subscribe(data => this.products.set(data));
    }

    // ...methods omitted for brevity...
}

The focused edit shows the repository switching to a REST data source and updating its products signal when the request returns. section 23.3.3

How Pro Angular 16 covers it

How deep it goes
Builds product repositories with lookup and change operations, then elaborates signal exposure, computed categories, and asynchronous loading.
Examples
Uses product arrays, an ID lookup, derived categories, a product-count computation, and a REST-backed replacement for static data.
What it assumes
Introduces the model through a Product class and simple data source before using Angular signals and component computations.
What you can do after
Build it from scratch with product operations and controlled signal access, then connect it to a static or REST data source.
How it connects
Connects data sources, dependency injection, Angular signals, computed signals, components, and REST data sources.

Also explained in

Compare how 9 books explain repository pattern →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Pro Angular 16. The section links point to the parts of the book it draws on.