You’re viewing Umbraco CMS version 17 RC Documentation. Content may change before the final release.

Repositories

Repositories provide a structured way to manage data operations in the Backoffice. They abstract the data access layer, allowing for easier reuse and scalability.

Repositories create separation between domain logic and data access. By providing a known interface for data requests, we can reuse UI components across different domains. For example, we have a generic UX flow for deleting an entity. By supplying this flow with a repository that has a known interface for deletion, we can use the same UX flow to delete any entity. The same applies to Trees, Collections, Workspaces, and more.

Additionally, repositories can utilize different data sources depending on the application's state. These sources may include:

  • A REST API

  • Offline storage

  • A local cache

  • And more.

This abstraction ensures that consumers don’t need to worry about how to access data. The repository serves as the Backoffice’s entry point for requesting new data. As a result, we achieve a loosely coupled connection between consumers and data storage procedures, effectively hiding complex implementations.

  • Repository: defines what data operations are available (get, add, update, delete).

  • Data Source: defines how data is fetched or stored.

Data flow with a repository

A repository must be instantiated where it is used. It should take an UmbController as part of the constructor. This ensures that any contexts consumed in the repository are scoped correctly.

A repository can be initialized directly from an element, but will often be instantiated in a context, like the Workspace Context.

The data flow when using a repository can be illustrated as follows:

(Data Source) -> Repository -> (Controller/Context) -> Element

Using an existing Repository

Often, you will find that data is already available and observable in a context. In that case, subscribing to the context state will be the right approach to take. This way, you will receive all runtime updates that occur to the data throughout the session.

If the needed data isn’t available in a context, use a repository to request the data. This will give you the correct data no matter the current application state.

In the example below, we instantiate the UmbDocumentItemRepository directly in a custom element to request Document Item data by its unique key.

import { LitElement} from '@umbraco-cms/backoffice/external/lit';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
import { UmbDocumentItemRepository } from '@umbraco-cms/backoffice/document';

// Simplified element
class MyElement extends UmbElementMixin(LitElement) {
  ...
  #documentItemRepository = new UmbDocumentItemRepository(this);

  firstUpdated() {
    const { data, error } = await this.#documentItemRepository.requestItems(['some-unique-key', 'another-unique-key']);
    console.log('Response', data, error);
    // Render data in the element
  }
  ...
}

Alternatively, you can instantiate the repository in a controller or context, store the data in a state, and then observe that state in your element. This is often the preferred approach as it allows for better separation of concerns and reusability across different components.

import { UmbArrayState } from '@umbraco-cms/backoffice/observable-api';
import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api';
import { UmbDocumentItemRepository } from '@umbraco-cms/backoffice/document';

export class MyController extends UmbControllerBase {

  #items = new UmbArrayState<DocumentItemModel>([]);
  items = this.#items.asObservable();

  #documentItemRepository = new UmbDocumentItemRepository(this);

  constructor(host: UmbControllerHost) {
		super(host);
		this.#load();
	}

  async #load() {
    const { data, error } = await this.#documentItemRepository.requestItems(['some-unique-key', 'another-unique-key']);
    console.log('Response', data, error);
    this.#items.setValue(data ?? []);
    // The items state can now be observed by any element initializing this controller
  }
}

Register a custom Repository

By registering your repository in the Extension Registry, you make it available to use in different extension kinds that require a repository alias.

Some of the common repository interfaces are:

See the example below of how to register a custom repository:

import { umbExtensionsRegistry } from "@umbraco-cms/backoffice/extension-registry";
import { UmbRepositoryBase } from "@umbraco-cms/backoffice/repository";
import type { UmbTreeRepository } from "@umbraco-cms/backoffice/tree";

class MyEntityTreeRepository extends UmbRepositoryBase implements UmbTreeRepository {
  // Implement repository methods here
}

const repositoryManifest = {
  type: "repository",
  alias: "My.Repository.EntityTree",
  name: "My Entity Tree Repository",
  api: MyEntityTreeRepository,
};

umbExtensionsRegistry.register(repositoryManifest);

Last updated

Was this helpful?