Files
SYSTEM/docs/_archive/v1.0/architecture.md
2025-09-01 01:52:06 +03:00

22 KiB

SYSTEM Fullstack Architecture Document

1. Template and Framework Selection

  • Framework: The project is built on Nuxt 4, as specified in the provided repomix-output.xml.
  • UI Library: The core component library is PrimeVue, with the Aura theme preset. This choice is confirmed in nuxt.config.ts.
  • Styling: A hybrid approach using Tailwind CSS for utility-first styling and SCSS for global styles, variables, and complex component styling is adopted.
  • Starter Template: The project is based on the custom template provided by the user. No external starter templates are used. This gives us full control but requires us to define the structure meticulously.

Change Log

Date Version Description Author
2025-08-31 1.0 Initial Frontend Architecture draft Architect (Winston)

2. Frontend Tech Stack

This table reflects the choices made in the PRD and the user's template, solidifying the frontend technology stack.

Category Technology Version Purpose & Rationale
Framework Nuxt ^4.0.3 The core application framework, providing SSR, file-based routing, and a robust module ecosystem.
UI Library PrimeVue ^4.3.7 The primary component library. Chosen for its rich set of components and powerful theming capabilities.
Theme PrimeUI Aura ^1.2.3 The default theme preset for PrimeVue, providing a modern and clean aesthetic out of the box.
Styling Tailwind CSS ^0.6.1 (primeui) For utility-first styling, rapid prototyping, and responsive design.
Styling SCSS (Sass) (via sass dev dep) For structured styling, variables, mixins, and managing global styles and theme overrides.
i18n @nuxtjs/i18n 10.0.6 For future-proof internationalization, as required by the PRD.

3. Project Structure

Based on the Nuxt 4 documentation and the need to manage SCSS files effectively, the following directory structure will be adopted within the app/ directory.

app/
├── assets/
│   └── scss/
│       ├── base/
│       │   ├── _reset.scss       # Optional: for CSS resets
│       │   └── _typography.scss  # Base typography styles
│       ├── components/
│       │   └── _buttons.scss     # Example: Custom button styles
│       ├── layout/
│       │   └── _main.scss        # Main layout styles
│       ├── themes/
│       │   └── _aura-overrides.scss # Specific overrides for the Aura theme
│       ├── utils/
│       │   ├── _variables.scss   # Global SCSS variables (colors, spacing)
│       │   └── _mixins.scss      # Global SCSS mixins
│       └── main.scss             # Main entry file that imports all other SCSS partials
│
├── components/
│   ├── common/
│   │   └── AppHeader.vue         # Example of a globally used component
│   └── specific/
│       └── CellDetailView.vue    # Example of a view-specific component
│
├── composables/
│   ├── useCitizen.ts             # Composable for managing Citizen identity
│   └── useLedger.ts              # Composable for interacting with The Main Ledger
│
├── layouts/
│   └── default.vue               # The default layout for the application
│
├── pages/
│   ├── index.vue                 # Dashboard / Main View
│   └── cell/
│       └── [id].vue              # Cell Detail View (Dynamic Route)
│
├── plugins/
│   └── primevue.ts               # Plugin to register PrimeVue components/services if needed
│
└── app.vue                       # Main application entry point

To make this SCSS structure work seamlessly, we will update nuxt.config.ts to automatically import our main SCSS file globally.

nuxt.config.ts modification:

export default defineNuxtConfig({
  // ... other configs
  css: ['~/assets/scss/main.scss'],
  // ... other configs
})

app/assets/scss/main.scss content:

// 1. Utilities (Variables, Mixins)
@use 'utils/variables';
@use 'utils/mixins';

// 2. Base Styles
@use 'base/reset';
@use 'base/typography';

// 3. Layout Styles
@use 'layout/main';

// 4. Component Styles
@use 'components/buttons';

// 5. Theme Overrides
@use 'themes/aura-overrides';

4. Component Standards

To ensure consistency and maintainability, all Vue components created in this project must adhere to the following standards.

Component Template (.vue files)

All components should follow the standard Vue 3 <script setup> syntax. This is the recommended modern approach by both Vue and Nuxt documentation.

Here is a minimal template for a new component:

<script setup lang="ts">
// 1. IMPORTS
// e.g., import { ref } from 'vue';
// e.g., import Button from 'primevue/button';

// 2. PROPS & EMITS
// e.g., const props = defineProps<{
//   citizenId: string;
// }>();
// e.g., const emit = defineEmits(['updated']);

// 3. STATE & COMPOSABLES
// e.g., const count = ref(0);
// e.g., const { citizen } = useCitizen();

// 4. COMPUTED PROPERTIES & WATCHERS
// ...

// 5. LIFECYCLE HOOKS
// ...

// 6. METHODS
// e.g., function signTransaction() { ... }
</script>

<template>
  <!-- 
    Component template. Use PrimeVue components and Tailwind CSS classes here.
    e.g., <div class="p-4 border-round-md"> ... </div>
  -->
</template>

<style scoped lang="scss">
/* 
  Component-specific SCSS goes here.
  Global variables/mixins are available thanks to nuxt.config.ts.
  e.g., .my-component { color: var(--primary-color); }
*/
</style>

Naming Conventions

  • Components: PascalCase. Files should match the component name (e.g., CellDetailView.vue).
  • Composables: camelCase with a use prefix (e.g., useLedger.ts).
  • SCSS Variables: $kebab-case (e.g., $primary-accent-color).

5. State Management

  • Approach: For the MVP, we will rely primarily on Nuxt's built-in useState composable for simple, shared, server-side-renderable state. This is the most lightweight and idiomatic approach for Nuxt 4. We will avoid installing a heavy external library like Pinia or Vuex until the application's complexity demonstrably requires it.
  • Structure: Shared state composables will be located in the app/composables/ directory.

State Management Template (app/composables/useSomeState.ts)

import { useState } from '#app';

// Example: Managing the current Citizen's session info
interface CitizenSession {
  name: string;
  publicKey: string;
  // NOTE: The private key should NOT be stored in a composable.
  // It should be managed in a more secure, non-persistent way.
}

export const useCitizenStore = () => {
  const citizen = useState<CitizenSession | null>('citizen', () => null);

  const setCitizen = (newCitizen: CitizenSession) => {
    citizen.value = newCitizen;
  };

  const clearCitizen = () => {
    citizen.value = null;
  };



  return {
    citizen,
    setCitizen,
    clearCitizen,
  };
};

6. API Integration

Since the project is a monolith where the frontend and backend are served by the same Nuxt instance, we will use the built-in $fetch utility for all API calls. This is the recommended, isomorphic way to handle data fetching in Nuxt 4.

Service Layer Pattern

To keep components clean and organize API logic, we will use a "service layer" pattern. All API calls will be abstracted into functions within files in the app/services directory.

app/services/cellService.ts (Example Template)

// Using Nuxt's built-in $fetch
import { $fetch } from 'ofetch';

// Define types for API payloads and responses, ideally imported from a shared location
interface Cell {
  id: string;
  // ... other properties
}

interface CreateCellPayload {
  commonGood: string;
  // ... other properties
}

const BASE_URL = '/api/cells'; // Internal API endpoint

export const cellService = {
  /**
   * Fetches the details for a single Cell.
   * @param id The ID of the Cell.
   */
  async getCellById(id: string): Promise<Cell> {
    return await $fetch<Cell>(`${BASE_URL}/${id}`);
  },

  /**
   * Creates a new Cell.
   * @param payload The data for the new Cell.
   */
  async createCell(payload: CreateCellPayload): Promise<Cell> {
    return await $fetch<Cell>(BASE_URL, {
      method: 'POST',
      body: payload,
    });
  },

  // ... other methods like joinAsConsumer, confirmPayment, etc.
};

7. Routing

Routing will be handled by Nuxt's file-based router. All pages will be created as .vue files in the app/pages directory.

Route Configuration Example (app/pages/cell/[id].vue)

<script setup lang="ts">
import { useRoute } from 'vue-router';

const route = useRoute();
const cellId = route.params.id as string;

// Fetch cell data using the service
const { data: cell, pending, error } = await useAsyncData(
  `cell-${cellId}`,
  () => cellService.getCellById(cellId)
);
</script>

<template>
  <div v-if="pending">Loading...</div>
  <div v-else-if="error">Error loading Cell: {{ error.message }}</div>
  <div v-else-if="cell">
    <h1>{{ cell.commonGood }}</h1>
    <!-- Display cell details here -->
  </div>
</template>

8. Testing Requirements

Testing is critical for ensuring the integrity of the system.

  • Unit Tests: We will use Vitest for unit testing. Every composable and service function must have unit tests covering its logic.
  • Component Tests: We will use @vue/test-utils to test individual Vue components. Tests should verify that components render correctly based on props and that they emit events when interacted with.
  • Test Location: All test files will be located alongside the file they are testing, with a .spec.ts extension (e.g., useLedger.spec.ts).

1. Introduction

This document outlines the complete fullstack architecture for Project SYSTEM, including the backend systems, frontend implementation, and their integration. It serves as the single source of truth for AI-driven development, ensuring consistency across the entire technology stack. This unified approach combines backend and frontend concerns to streamline development.

Starter Template or Existing Project

  • Decision: The project is based on the custom Nuxt 4 starter template provided by the user. No external templates are used. The architecture will be built upon this existing foundation.

Change Log

Date Version Description Author
2025-08-31 1.0 Initial Fullstack Architecture draft Architect (Winston)

2. High Level Architecture

Technical Summary

Project SYSTEM is a monolithic, self-hosted web application built on the Nuxt 4 framework. The architecture is designed for efficiency and transparency, running on a Raspberry Pi. It features a Vue-based frontend interacting with a PostgreSQL database via Nuxt's server-side API routes. The core of the system is an immutable, event-sourced "Main Ledger" where every action is a cryptographically signed transaction, ensuring data integrity and auditability. Identity management is decentralized, based on public/private key pairs.

Platform and Infrastructure Choice

  • Platform: Self-hosted on a user-provided Raspberry Pi (Model 4+).
  • Key Services:
    • Application Server: Nuxt 4 running in Node.js mode.
    • Database: PostgreSQL.
    • Containerization: The entire stack will be managed and deployed via Docker Compose. This ensures a consistent and reproducible environment.
  • Deployment Host and Regions: Single host, located within the "Община's" private network.

Repository Structure

  • Structure: Monorepo.
  • Monorepo Tool: Not required for this level of simplicity. A single package.json at the root will manage all dependencies.
  • Package Organization: A standard Nuxt 4 project structure will be used, as detailed in the Frontend Architecture. Server-side logic will reside within the server/ directory.

High Level Architecture Diagram

graph TD
    subgraph "Browser"
        A[Citizen's Browser]
    end

    subgraph "Raspberry Pi (Docker Compose)"
        B[Nuxt 4 Server]
        C[PostgreSQL Database]

        B -- "Serves Frontend" --> A
        A -- "API Calls ($fetch)" --> B
        B -- "Reads/Writes" --> C
    end

    style B fill:#2D3748,stroke:#fff,stroke-width:2px,color:#fff
    style C fill:#4A5568,stroke:#fff,stroke-width:2px,color:#fff

3. Tech Stack (Revised)

Category Technology Version Purpose & Rationale
Fullstack Framework Nuxt ^4.0.3 Core framework for both frontend and backend (server routes).
Database ORM Prisma latest Next-generation ORM for type-safe database access and automated migrations.
Database PostgreSQL latest Robust, reliable SQL database managed by Prisma.
Deployment Docker Compose latest For containerizing and managing the application stack.
... (rest of the stack remains the same) ... ...

4. Database Architecture (Revised with Prisma)

The database schema will be defined declaratively using the Prisma schema language in a single source of truth file: prisma/schema.prisma. Prisma will be responsible for generating SQL migrations and providing a type-safe client for all database interactions.

Prisma Schema (prisma/schema.prisma)

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

// User-friendly metadata associated with a public key.
model Registry {
  publicKey   String    @id
  citizenName String
  avatarUrl   String?
  isActive    Boolean   @default(true)
  createdAt   DateTime  @default(now())
}

// The core, immutable data model.
// Once a row is written, it is never updated or deleted.
model LedgerEntry {
  id                 BigInt    @id @default(autoincrement())
  previousHash       String?
  entryHash          String    @unique
  citizenPublicKey   String
  signature          String
  actionType         String
  payload            Json? // The actual data for the action, e.g., cell details
  createdAt          DateTime  @default(now())

  @@index([citizenPublicKey])
  @@index([payload], name: "idx_ledger_payload_entity") // Example for indexing specific payload keys
}

Migrations Workflow

Database migrations will be managed exclusively by Prisma Migrate.

  1. Development: After any change to schema.prisma, a new migration will be created and applied using the command:
    npx prisma migrate dev --name <descriptive_migration_name>
    
  2. Production: Migrations will be applied during the deployment process using:
    npx prisma migrate deploy
    

This ensures that the database schema is always in sync with the Prisma schema definition.


5. API Specification & Server Architecture

The application will not expose a public REST или GraphQL API. All communication between the frontend and backend will occur internally via Nuxt 4 Server Routes. This approach is secure, performant, and perfectly suited for a monolithic fullstack application.

Server Directory Structure (server/)

The server-side logic will be organized as follows:

server/
├── api/
│   ├── cells/
│   │   ├── index.post.ts       # Create a new Cell
│   │   └── [id]/
│   │       ├── index.get.ts    # Get Cell details
│   │       └── join.post.ts    # Join a Cell (as consumer/executor)
│   ├── citizens/
│   │   ├── index.post.ts       # Create a new Citizen (onboarding)
│   │   └── invite.get.ts       # Generate an invitation link
│   └── vault/
│       ├── index.post.ts       # Create a Vault account
│       ├── login.post.ts       # Login via Vault
│       └── recover.post.ts     # Initiate/approve social recovery
├── middleware/
│   └── auth.ts                 # Middleware to check Citizen authentication
└── utils/
    ├── prisma.ts               # Exports a singleton Prisma Client instance
    └── crypto.ts               # Cryptographic helper functions

Core API Endpoints (MVP)

Endpoint Method Description Request Body Response
/api/citizens POST Creates the first Citizen or a new one via invite. { name, publicKey } Citizen
/api/citizens/invite GET Generates a single-use invitation link. (none) { inviteUrl }
/api/cells POST Creates a new "Cell". { commonGood, executors } LedgerEntry
/api/cells/[id] GET Retrieves the full state of a "Cell". (none) CellState
/api/cells/[id]/join POST Joins a "Cell" as Executor or Consumer. { role, item? } LedgerEntry
/api/cells/[id]/confirm POST Confirms payment from a Consumer. { consumerKey } LedgerEntry

Core Workflow: The Cryptographic Signing Process

This sequence diagram illustrates the fundamental process for every action in the system.

sequenceDiagram
    participant Browser as Citizen's Browser
    participant NuxtServer as Nuxt Server / API
    participant Prisma
    participant Ledger as The Main Ledger (DB)

    Browser->>Browser: 1. User performs action (e.g., clicks "Create Cell")
    Browser->>Browser: 2. Prepare `payload` (e.g., { commonGood: "Суп" })
    Browser->>Browser: 3. Get `previousHash` from last Ledger entry
    Browser->>Browser: 4. Create `entryHash` from payload + previousHash
    Browser->>Browser: 5. Sign `entryHash` with private key to get `signature`
    Browser->>NuxtServer: 6. POST /api/cells (sends payload, publicKey, signature)

    NuxtServer->>NuxtServer: 7. Receive request
    NuxtServer->>NuxtServer: 8. Validate signature using sender's `publicKey`
    alt Signature is Invalid
        NuxtServer-->>Browser: 9a. Return 401 Unauthorized
    else Signature is Valid
        NuxtServer->>Prisma: 9b. Get latest `previousHash` from DB
        NuxtServer->>NuxtServer: 10. Verify client's `previousHash` matches server's
        NuxtServer->>Prisma: 11. `prisma.ledgerEntry.create(...)`
        Prisma->>Ledger: 12. INSERT INTO "LedgerEntry"
        Ledger-->>Prisma: 13. Return new entry
        Prisma-->>NuxtServer: 14. Return new entry
        NuxtServer-->>Browser: 15. Return new LedgerEntry (success)
    end

6. Unified Project Structure

This is the final monorepo structure that integrates the frontend (app/), backend (server/), and database (prisma/) components.

system/
├── app/                      # Nuxt frontend application
│   ├── assets/
│   │   └── scss/
│   ├── components/
│   ├── composables/
│   ├── layouts/
│   ├── pages/
│   └── ...
├── prisma/                   # Prisma ORM configuration
│   ├── migrations/
│   │   └── .../migration.sql
│   └── schema.prisma         # The single source of truth for the database
├── server/                   # Nuxt server-side logic (API)
│   ├── api/
│   │   └── ...
│   ├── middleware/
│   └── utils/
├── types/                    # Shared TypeScript types
│   ├── citizen.ts
│   ├── ledger.ts
│   └── index.ts
├── docker-compose.yml        # Defines and runs the multi-container stack
├── Dockerfile                # For building the Nuxt application image
├── nuxt.config.ts            # Nuxt configuration
├── package.json
└── tsconfig.json

7. Development Workflow

  1. Initial Setup:
    • Clone the repository.
    • Create a .env file with the DATABASE_URL.
    • Run docker-compose up -d --build to start the PostgreSQL container and the Nuxt development server.
  2. Schema Changes:
    • Modify prisma/schema.prisma.
    • Run npx prisma migrate dev --name <migration-name> to apply changes to the running database. Prisma Client will be auto-generated.
  3. Development:
    • Develop Vue components in the app/ directory.
    • Develop API endpoints in the server/api/ directory.
    • The Nuxt dev server will provide hot-reloading for both frontend and backend changes.

8. Deployment Architecture

  • Strategy: The application will be deployed as a set of Docker containers orchestrated by docker-compose. This simplifies deployment on the target Raspberry Pi.
  • Process:
    1. A Dockerfile will build the Nuxt application into a production-ready Node.js server.
    2. docker-compose.yml will define two services:
      • app: The Nuxt application built from the Dockerfile.
      • db: The official PostgreSQL image, with a persistent volume mounted to store the database data.
    3. Deployment consists of pulling the latest code, running docker-compose up -d --build on the Raspberry Pi.
  • Migrations: Production migrations will be applied by running npx prisma migrate deploy inside the running app container or as part of the container's startup command.

9. Security & Performance

  • Security:
    • Authentication: Managed via cryptographic signatures. The server is stateless regarding authentication.
    • Authorization: All API endpoints inside server/middleware/auth.ts will verify the incoming signature against the publicKey to ensure the action is performed by the legitimate owner of the key.
    • Secrets: The DATABASE_URL will be managed via the .env file, which is excluded from version control.
  • Performance:
    • The primary performance consideration is the Raspberry Pi environment. The monolithic Nuxt/PostgreSQL stack is chosen for its low resource overhead compared to more complex microservice architectures.
    • Database queries will be optimized using indices as defined in the Prisma schema.
    • Frontend assets will be optimized by Nuxt's build process.