Registry architecture
GitLab has several registry applications. Given that they all leverage similar UI, UX, and business logic, they are all built with the same architecture. In addition, a set of shared components already exists to unify the user and developer experiences.
Existing registries:
- Package registry
- Container registry
- Terraform Module Registry
- Dependency Proxy
Frontend architecture
Component classification
All the registries follow an architecture pattern that includes four component types:
- Pages: represent an entire app, or for the registries using vue-router they represent one router route.
- Containers: represent a single piece of functionality. They contain complex logic and may connect to the API.
- Presentationals: represent a portion of the UI. They receive all their data with
props
or throughinject
, and do not connect to the API. - Shared components: presentational components that accept a various array of configurations and are shared across all of the registries.
Communicating with the API
The complexity and communication with the API should be concentrated in the pages components, and in the container components when needed. This makes it easier to:
- Handle concurrent requests, loading states, and user messages.
- Maintain the code, especially to estimate work. If it touches a page or functional component, expect it to be more complex.
- Write fast and consistent unit tests.
Best practices
- Use
provide
orinject
to pass static, non-reactive values coming from the app initialization. - When passing data, prefer
props
over nested queries or Vuex bindings. Only pages and container components should be aware of the state and API communication. - Don’t repeat yourself. If one registry receives functionality, the likelihood of the rest needing it in the future is high. If something seems reusable and isn’t bound to the state, create a shared component.
- Try to express functionality and logic with dedicated components. It’s much easier to deal with
events and properties than callbacks and asynchronous code (see
delete_package.vue
). - Leverage startup for GraphQL calls.
Shared components library
Inside vue_shared/components/registry
and packages_and_registries/shared
, there’s a set of
shared components that you can use to implement registry functionality. These components build the
main pieces of the desired UI and UX of a registry page. The most important components are:
-
code-instruction
: represents a copyable box containing code. Supports multiline and single line code boxes. Snowplow tracks the code copy event. -
details-row
: represents a row of details. Used to add additional information in the details area of thelist-item
component. -
history-item
: represents a history list item used to build a timeline. -
list-item
: represents a list element in the registry. It supports: left action, left primary and secondary content, right primary and secondary content, right action, and details slots. -
metadata-item
: represents one piece of metadata, with an icon or a link. Used primarily in the title area. -
persisted-dropdown-selection
: represents a menu that stores the user selection in thelocalStorage
. -
registry-search
: implementsgl-filtered-search
with a sorting section on the right. -
title-area
: implements the top title area of the registry. Includes: a main title, an avatar, a subtitle, a metadata row, and a right actions slot.
Adding a new registry page
When adding a new registry:
- Leverage the shared components that already exist. It’s good to look at how the components are structured and used in the more mature registries (for example, the package registry).
- If it’s in line with the backend requirements, we suggest using GraphQL for the API. This helps in dealing with the innate performance issue of registries.
- If possible, we recommend using Vue Router and frontend routing. Coupled with Apollo, the caching layer helps with the perceived page performance.