Skip to content

Architecture

StackRivet is a modular monolith: one deployable Spring Boot application, split into modules with clear, enforced boundaries. It’s not a microservice mesh, and it’s not a loose admin template.

That lets a small team (or an AI coding tool) ship quickly without giving up the boundaries that keep a real enterprise project maintainable.

flowchart LR
  Web["Vue 3 Admin"]
  CLI["CLI"]
  API["REST / OpenAPI"]
  subgraph Backend["Backend modules"]
    Sec["Auth and RBAC"]
    Sys["System"]
    Asset["Asset Service"]
    Gen["Code Generator"]
    Task["Async Tasks"]
  end
  DB[("MySQL / PostgreSQL")]
  Store["S3 / OSS / Local"]
  Redis[("Redis")]
  Web --> API
  CLI --> API
  API --> Sec
  API --> Sys
  API --> Asset
  API --> Gen
  API --> Task
  Sec --> DB
  Sys --> DB
  Gen --> DB
  Task --> DB
  Asset --> Store
  API -.-> Redis
GoalHow the architecture serves it
Start a project in ~10 minutesOne process, one docker compose, one mvn spring-boot:run
Generate a working module in ~30 minutesThe code generator writes backend, frontend, menu, permissions, OpenAPI and tests together
Keep AI inside the linesModule boundaries, layering and test rules are written down and machine-readable
Stay upgradeable after customizationModule manifests, Flyway migrations and template versions track what changed
Scale without a rewriteClear module seams mean you can extract a service when scale or ownership requires it

You get the speed of a single codebase, with clear seams to split out a service once scale or team ownership justifies it.

The backend is 16 Maven modules. The full list and responsibilities are in Project Structure; what matters architecturally is the dependency direction between them.

These rules are not suggestions — they are enforced by ArchUnit tests in stackrivet-app, so a violation fails the build:

  1. stackrivet-common depends on no business module. It only holds shared primitives: R<T>, PageR<T>, TraceIdHolder, BusinessException, base entities.
  2. stackrivet-security does not depend on stackrivet-system or its sr_sys_* tables. The user/role data it needs for authentication comes through the SystemUserDirectory SPI defined in common. Security stays decoupled from the system module’s storage.
  3. stackrivet-asset never references business tables. It relates to your data only through bizType, refId and a permission SPI — so the file layer doesn’t get tangled into every feature’s schema.
  4. stackrivet-generator does not write core source directly. All output goes through templates and the module manifest, which is what keeps generated code reviewable and upgradeable.
  5. Demo/example modules are never a dependency of a core module. stackrivet-demo consumes public APIs; it cannot pollute the core in reverse.

The result is an acyclic dependency graph with security, system, audit, asset and the generator as independent, swappable modules.

Every business module follows the same four layers, and requests flow in one direction only:

Controller → Application / Service → Domain → Infrastructure / Mapper
LayerResponsibilityMust not
ControllerParam validation, permission annotations, OpenAPI annotations, response wrappingContain business rules; call a Mapper directly
Application / ServiceUse-case orchestration, transactions, permission context, audit eventsReturn database entities to the client
DomainDomain rules, state transitions, value objectsDepend on web or persistence frameworks
Infrastructure / MapperSQL, persistence, external adaptersBypass the Service to reach the Controller
DTO / VORequest and response modelsReuse Entities across the boundary

The entity never leaves the persistence boundary — DTOs and VOs cross it instead. The code generator is held to the same rules: it will not emit a “Controller calls Mapper directly” shortcut.

The architecture is default-deny:

  • Unauthenticated requests get 401; unauthorized requests get 403.
  • Every protected endpoint declares its permission; generated modules ship with permission annotations, not without.
  • Five permission layers cover the surface: menu (navigation), button (UI actions), API (server entry), data (query scope) and asset (private file access).

See Security & RBAC for the full model.

  • Both MySQL 8.4 LTS and PostgreSQL 18.4 are supported in 1.0.
  • All schema changes go through Flyway — no manual production changes that get documented afterwards.
  • Tables use the sr_ prefix so they don’t collide when StackRivet lives inside a customer’s database.
  • Core tables carry id, created_at, updated_at; auditable tables add created_by / updated_by; multi-tenant-ready tables carry tenant_id; soft-deletable tables carry deleted / deleted_at.

Every request carries a traceId, surfaced in the unified exception response so an error in the UI can be traced to a server log. Actuator, Micrometer and an optional OpenTelemetry profile are built in, and stackrivet doctor diagnoses common environment problems (JDK, Node, Docker, database, object storage, port conflicts).

The same application runs from a laptop to an HA cluster:

ShapeTopology
Local devVite dev server + Spring Boot + Docker MySQL/PostgreSQL + MinIO
Small-team productionNginx → static admin + Spring Boot app → managed database + S3/OSS
EnterpriseLoad balancer → multiple app nodes → HA database + enterprise object storage + OIDC/SAML/LDAP

Because the app aims to be stateless (uploads go straight to object storage, heavy work runs as async tasks, lists are paginated), scaling out is adding nodes behind a load balancer — not re-architecting.