---
url: "https://vueda.dev/v3/core-concepts/architecture-overview.md"
title: "Architecture Overview"
---

# Architecture Overview

VUEDA is a framework for admin-style [CRUD](/v3/reference/glossary.md#crud) applications. A Django server defines models, serializers, and viewsets. A Vue client reads those definitions at runtime as [Model Info](/v3/reference/glossary.md#model-info) and builds routes, forms, and views from them. An application starts from a working CRUD surface with no per-model client code, and you customize the views that need it.

Authority splits along the network boundary. The server decides data integrity, permissions, and the shape of the metadata. The client decides rendering, form state, and which routes to open. This page describes the layers on each side, the Django apps and infrastructure they need, and which pages describe each topic in depth.

## Server Responsibility Layers

The server has four responsibility layers. Each layer builds on the ones below it.

### Domain infrastructure

`vueda.core` defines the base classes that a domain module builds on: [VuedaModel](/v3/reference/api/py/vueda.core.models/VuedaModel.md), [VuedaSerializer](/v3/reference/api/py/vueda.core.serializers/VuedaSerializer.md), and [VuedaViewSet](/v3/reference/api/py/vueda.core.viewsets/VuedaViewSet.md). A module that extends them gets VUEDA's request handling without further setup. [NoExtraFieldsSerializerMixin](/v3/reference/api/py/vueda.core.serializers/NoExtraFieldsSerializerMixin.md) rejects unknown fields in a write, and [NoExtraFieldsForViewSetMixin](/v3/reference/api/py/vueda.core.viewsets/NoExtraFieldsForViewSetMixin.md) rejects unknown query parameters. [Filtering and Ordering Semantics](/v3/core-concepts/filtering-and-ordering-semantics.md) describes the rejection rules.

`VuedaViewSet` also filters `list` results by row-level permissions, allows only declared [expands](/v3/reference/glossary.md#expand), and adds bulk delete with a [dry run](/v3/reference/glossary.md#dry-run). VUEDA's validation code raises [VuedaValidationError](/v3/reference/api/py/vueda.core.exceptions/VuedaValidationError.md), whose payload the client maps onto form fields. [Field and Expand Semantics](/v3/core-concepts/field-and-expand-semantics.md), [Row-Level Permission Filtering](/v3/core-concepts/row-level-permission-filtering.md), and [Error and Validation Contract](/v3/core-concepts/error-and-validation-contract.md) describe each behavior. [DRF Ecosystem Compatibility Boundaries](/v3/core-concepts/drf-ecosystem-deviations.md) lists where these defaults depart from plain DRF.

[core.default\_settings.get\_defaults](/v3/reference/api/py/vueda.core.default_settings.md#get_defaults) builds a project's Django settings from its config file or environment. It sets the installed apps, the DRF defaults, the database and cache, and the wire names of VUEDA's query parameters. Some of its defaults are security-sensitive, and a project can override any of them. [Configuration Surface and Defaults](/v3/core-concepts/configuration-surface-and-defaults.md) describes each group of settings and the client's own configuration surface.

### Metadata and discovery

`vueda.info` publishes model info for each registered model. It derives fields, actions, [expands](/v3/reference/glossary.md#expand), filters, ordering, and permissions from the model, serializer, viewset, and filterset. The server filters the metadata for the requesting user, so two users can see different actions for the same model. [Server-Client Metadata Contract](/v3/core-concepts/server-client-metadata-contract.md) describes each section of model info and the key names on each side. [Convention Over Configuration](/v3/core-concepts/architecture-overview.md#convention-over-configuration) describes how a model gets metadata.

### Stateful lifecycle

`vueda.workflow` gives a model a state machine. The server enforces each [transition](/v3/reference/glossary.md#transition) and publishes the user's [permitted transitions](/v3/reference/glossary.md#permitted-transitions) in metadata, so a model's actions change with the object's state. State permission rules can also grant or deny ordinary actions per state. [Workflow as a Permission Overlay](/v3/core-concepts/workflow-permission-overlay.md) describes those rules and how they combine with model permissions.

`vueda.vdq` is the [VUEDA Dispatch Queue](/v3/reference/glossary.md#vdq-vueda-dispatch-queue). It stores each outbound email or SMS as a [Queue Item](/v3/reference/glossary.md#queue-item) with a workflow state, and a Celery worker sends it. Provider callbacks record the delivery result on the same item. [VDQ and Background Work Model](/v3/core-concepts/vdq-and-background-work.md) describes the queue item lifecycle and its transaction boundaries. Both apps are optional; [Django App Boundaries](/v3/core-concepts/architecture-overview.md#django-app-boundaries) gives the supported combinations.

### Cross-cutting concerns

`vueda.user` handles sign-in, sessions, and [two-factor authentication](/v3/reference/glossary.md#two-factor-authentication). With VDQ installed, its adapter sends notification email and SMS as queue items. Without VDQ, it sends the same messages during the request.

`vueda.history` keeps [Model History](/v3/reference/glossary.md#model-history) for every [VUEDA Model](/v3/reference/glossary.md#vueda-model) through PostgreSQL triggers. A model opts out or excludes fields through its [Feature Policy](/v3/reference/glossary.md#feature-policy). `VuedaViewSet` carries the history endpoint and `VuedaSerializer` publishes each object's revision, so a domain module needs no history-specific base class.

`vueda.release` serves the application's release notes.

## Django App Boundaries

The server ships seven Django apps, and `get_defaults()` puts all seven in `VUEDA_APPS`. Every supported configuration installs five required apps: `vueda.core`, `vueda.info`, `vueda.user`, `vueda.release`, and `vueda.history`. `get_defaults()` raises [Django: ImproperlyConfigured](https://docs.djangoproject.com/en/6.1/ref/exceptions/#django.core.exceptions.ImproperlyConfigured) when `VUEDA_APPS` omits `vueda.history`, because the migrations of every app with a tracked model depend on pghistory. `vueda.workflow` and `vueda.vdq` are optional. VDQ needs workflow, because a queue item's lifecycle is a workflow state machine, and `vueda.vdq` raises `ImproperlyConfigured` at startup without it.

| Configuration                                    | Supported |
| ------------------------------------------------ | --------- |
| Required apps alone                              | Yes       |
| Required apps and `vueda.workflow`               | Yes       |
| Required apps, `vueda.workflow`, and `vueda.vdq` | Yes       |
| Required apps and `vueda.vdq`                    | No        |

Installing a feature app makes the feature available, and each model's feature policy decides whether the model takes part. History tracks every VUEDA model unless it opts out, workflow applies only to models that opt in, and [Model Feature Policy](/v3/core-concepts/model-feature-policy.md) describes both declarations. [Purge Model History](/v3/guides/purge-model-history.md) describes the storage that history accumulates and how to remove old rows.

## Client Responsibility Layers

The client is a Vue single-page application. It derives a default UI from model info, and you override the parts that need it. It has five layers.

### Metadata consumption

Pinia stores fetch and cache the server's metadata. [stores/storeModelInfo](/v3/reference/api/js/stores/storeModelInfo.md) holds model info per model. [stores/storeModelConfig](/v3/reference/api/js/stores/storeModelConfig.md) merges it with the project's [Model Config](/v3/reference/glossary.md#model-config) overrides into one config per view. These are [auth-scoped stores](/v3/reference/glossary.md#auth-scoped-stores), so the client reads a model's metadata once and refetches it after a page reload or a change of user. [Reactive Data Flow](/v3/core-concepts/reactive-data-flow.md) describes the store lifecycles, their cache keys, and the composables that read them.

### Routing and gating

Every model shares one set of [CRUD Routes](/v3/reference/glossary.md#crud-routes). Before a view mounts, [Route Admission](/v3/reference/glossary.md#route-admission) loads the model's metadata through [requireModelInfo](/v3/reference/api/js/router/guards/functions/requireModelInfo.md). It opens the route only for an action the server lists for the user, or for one of the user's permitted transitions. [Routing and View Resolution Model](/v3/core-concepts/routing-and-view-resolution-model.md) describes the guard chain, what happens when a guard rejects a route, and how the view is chosen.

### UI generation

Composables turn each field's metadata into a form model. Every field renders as [FormField](/v3/reference/api/vue/FormField.md), which wraps a [Widget](/v3/reference/glossary.md#widget) chosen from type mapping tables. Model config overrides and component props change the field, widget, or props for the views that need it. [Contract-First Dynamic UI](/v3/core-concepts/contract-first-dynamic-ui.md) describes field and widget resolution and the precedence between metadata, model config, and props.

### Data operations

The list and object composables from reactive-helpers send no requests themselves. Each operation calls a [CRUD Adapter](/v3/reference/glossary.md#crud-adapter), and VUEDA's adapters send requests to its REST API. A generated project registers them at startup, and you can replace any adapter for every instance or for one. [CRUD Adapter Layer](/v3/core-concepts/crud-adapter-layer.md) describes the registries, what each adapter sends, and the contract that a replacement meets.

The adapters return a [Cancellable Promise](/v3/reference/glossary.md#cancellable-promise). The list and object instances cancel a running request when its parameters change or its component goes away, and a cancelled run does not record an error. Store requests carry no `cancel()`. [Cancellable Network Operations](/v3/core-concepts/cancellable-network-operations.md) describes what a cancel aborts and what it only ignores.

### Rendering

View components ([ViewList](/v3/reference/api/vue/ViewList.md), [ViewCreate](/v3/reference/api/vue/ViewCreate.md), [ViewRead](/v3/reference/api/vue/ViewRead.md), and [ViewUpdate](/v3/reference/api/vue/ViewUpdate.md)) render the generated forms with VUEDA's own controls, built on Reka UI. [ViewActionRouter](/v3/reference/api/vue/ViewActionRouter.md) picks the view for each route through [Action View Resolution](/v3/reference/glossary.md#action-view-resolution), which also finds a project's own view by naming convention.

## Runtime Topology

The web process serves HTTP requests: CRUD, metadata, workflow transitions, authentication, and provider callbacks. `get_defaults()` turns on [Django: DATABASE-ATOMIC\_REQUESTS](https://docs.djangoproject.com/en/6.1/ref/settings/#std-setting-DATABASE-ATOMIC_REQUESTS), so each request runs in one transaction, and an exception in a DRF view rolls back its writes. [Configuration Surface and Defaults](/v3/core-concepts/configuration-surface-and-defaults.md) gives the full transaction rule.

The worker process runs Celery tasks: email delivery, SMS delivery, and periodic status checks. It shares the web process's database, cache, and Django settings, but it has no request, user, or middleware. The worker moves queue items between states with a [Fast Transition](/v3/reference/glossary.md#fast-transition), which skips permission checks.

The browser client reaches the server only through its REST endpoints. It has no access to the database, cache, or worker. [Issue #246](https://github.com/arrai-innovations/vueda/issues/246) tracks production deployment documentation for these processes.

## Architectural Dependencies

Every VUEDA deployment needs PostgreSQL and a shared cache. Whether a deployment needs a broker and message providers depends on the apps and features that the project uses.

**PostgreSQL** is the only supported database. History records changes through PostgreSQL triggers (pgtrigger and pghistory), and list search uses full-text search and trigram similarity. VUEDA also relies on array fields, range fields, GIN indexes, and generated columns.

**A shared cache** holds sessions and rate-limit state for every web and worker process. `CACHE_URL` names the instance. [Configure the Cache and Sessions](/v3/guides/configure-cache-and-sessions.md) describes what the cache holds, the supported backends, and the deployment checks.

**A message broker**, such as RabbitMQ or Redis, is needed when `vueda.vdq` is installed. It carries VDQ's tasks from the web process to the worker. [vdq.schedulers.schedule\_queue\_item](/v3/reference/api/py/vueda.vdq.schedulers.md#schedule_queue_item) publishes each task after the transaction commits. When the publish fails, the queue item moves to `errored` with the exception in its `result`.

**Email and SMS providers** are needed only when the application sends messages. Email goes through the Django mail backend that the project configures, such as an Anymail backend, and SMS goes through Twilio.

## Convention Over Configuration

[Canonical Registration](/v3/reference/glossary.md#canonical-registration) decides whether a model takes part in the framework. A model registered with [info.registration.register](/v3/reference/api/py/vueda.info.registration.md#register) gets model info, routes, forms, permission gating, and action availability. A model registered with [info.registration.register\_serializer](/v3/reference/api/py/vueda.info.registration.md#register_serializer) gets partial metadata, as [Serializer-Only Registration](/v3/reference/glossary.md#serializer-only-registration) describes. A model that is not registered gets none of these. [Canonical Registration and Model Discovery](/v3/core-concepts/canonical-registration-and-discovery.md) describes the registration states, when to register, and the failures a missing registration causes.

The client holds no list of models, fields, or actions, so a change to the server contract needs no client code change.

Registration checks only the serializer's model, so a registered viewset that does not extend `VuedaViewSet` still appears in model info. That viewset loses VUEDA's viewset behavior: query parameter validation, row-level `list` filtering, bulk actions, and the history endpoint. DRF applies the default permission class, [ObjectPermissions](/v3/reference/api/py/vueda.core.permissions/ObjectPermissions.md), to any view that does not set its own.

## The Authorization Boundary

The server authorizes every request on its own, whatever the metadata said. Model info reflects the user's permissions, and the client uses it to decide which routes open and which controls appear. A route the client does not open or a button it hides is a [Client Affordance](/v3/reference/glossary.md#client-affordance). A user who calls the API directly still meets every server check. Leaving an optional app out changes what the client can show, for example no transition controls without workflow, and it grants or removes no permission. [Authorization vs UI Semantics](/v3/core-concepts/authorization-vs-ui-semantics.md) describes where the two sides get their action sets, and [Permission Model](/v3/core-concepts/permission-model.md) describes the server's permission layers.
