- Add NFC ePassport roadmap (ICAO 9303, eIDAS) - Add TensorFlow.js edge face detection (BlazeFace) - Add structured audit logger (GDPR-compliant) - Risk scoring support Part of KYC Apple Native UX v1.1.0
16 KiB
ARC-001 — Canonical Domain Model
Status: Accepted
Datum: 2026-06-01
Beslutsfattare: AAMOS Architecture Team
Kontext
AAMOS är ett verksamhetsoperativsystem vars primära designkrav är att moduler (Ekonomi, CRM, HR, Projekt, Kvalitet, Inköp, Kontrakt) kan samexistera, utbyta data och bygga på varandras entiteter utan att skapa n×m-mappningsproblem.
Utan ett kanoniskt objektsystem uppstår ofrånkomligen:
- Semantisk drift — samma begrepp (t.ex. "Person") modelleras på 4 olika sätt i 4 moduler, vilket omöjliggör korsmodulärsökning och AI-kontext
- Integrationsskuld — varje ny modul måste skriva egna adaptrar mot alla befintliga moduler
- Auditbrist — när ett objekt existerar på flera ställen blir spårbarhet svårt att garantera
Ekonomimodulen är referensimplementationen. Alla val i detta ADR speglas redan (eller ska speglas) i Ekonomimodulens databasschema.
Beslut
AAMOS definierar tolv universella kanoniska objekt. Varje modul MÅSTE använda dessa objekt som primärreferenser och får inte definiera alternativa entiteter för samma semantiska koncept.
Fundamentala fält (alla objekt)
Varje kanoniskt objekt bär alltid dessa fält:
| Fält | Typ | Beskrivning |
|---|---|---|
id |
UUID v7 |
Globalt unikt, tidsordnat |
tenant_id |
UUID |
Isoleringsdomän (se ARC-003) |
created_at |
timestamptz |
ISO 8601, UTC |
updated_at |
timestamptz |
ISO 8601, UTC, auto-update |
created_by |
UUID |
Referens till Person.id eller system-ID |
deleted_at |
timestamptz|null |
Soft-delete, null = aktiv |
metadata |
jsonb |
Fri nyckel-värde-extension (modul-specifika fält) |
version |
integer |
Optimistic locking, inkrementeras vid varje UPDATE |
UUID v7 används för att primary key-index förblir klusterordnade i PostgreSQL och förhindrar page splits vid hög insertfrekvens.
Objekt 1 — Entity
Abstrakt basobjekt. Alla övriga kanoniska objekt ärver Entity-kontraktet (de fundamentala fälten ovan). Entity instansieras aldrig direkt — det är ett kontrakt, inte en tabell.
Syfte: Definiera det minsta kontraktet som gör ett objekt spårbart, isolerat och versionerat.
Relationer:
- Varje konkret objekt implementerar Entity-kontraktet
- Entity-kontraktet gör det möjligt för generiska tjänster (audit, sök, event-routing) att hantera alla objekt uniformt
Objekt 2 — Organization
Bolag, juridisk person, koncern, avdelning, team, kostnadsställe.
| Fält | Typ | Beskrivning |
|---|---|---|
name |
text |
Officiellt namn |
org_number |
text|null |
Organisationsnummer (landsspecifikt) |
org_type |
enum |
company, department, team, group, subsidiary |
parent_id |
UUID|null |
Självrefererande hierarki |
country_code |
char(2) |
ISO 3166-1 alpha-2 |
tax_id |
text|null |
Momsregistreringsnummer |
currency_code |
char(3) |
ISO 4217 (primärvaluta) |
status |
enum |
active, inactive, merged, dissolved |
external_refs |
jsonb |
{erp_id, crm_id, ...} |
Relationer:
- Organization → Organization (parent_id, hierarkisk)
- Organization ↔ Person (many-to-many via
memberships) - Organization → Location (many)
- Organization → Contract (many)
Ekonomimodulen-mappning:
accounts.owner_org_id → Organization.id
journal_entries.entity_id → Organization.id (när motpart är bolag)
Objekt 3 — Person
Anställd, kontaktperson, ägare, kund, leverantörskontakt, systemanvändare.
| Fält | Typ | Beskrivning |
|---|---|---|
first_name |
text |
|
last_name |
text |
|
display_name |
text |
Beräknat eller angivet |
email |
text|null |
Primär e-post |
phone |
text|null |
E.164-format |
national_id |
encrypted_text|null |
Personnummer — krypterat i vila |
person_type |
enum |
employee, contact, customer, owner, system |
primary_org_id |
UUID|null |
→ Organization.id |
user_account_id |
UUID|null |
→ auth-systemets User.id |
gdpr_consent |
jsonb |
Samtyckesregister per ändamål |
Relationer:
- Person → Organization (many-to-many via memberships)
- Person → Location (many, adress-koppling)
- Person → Task (assignee)
- Person → Contract (signatory)
Ekonomimodulen-mappning:
suppliers.contact_person_id → Person.id
expense_reports.submitted_by → Person.id
Objekt 4 — Asset
Anläggningstillgång, resursobjekt, produkt, inventariepost, licenspost.
| Fält | Typ | Beskrivning |
|---|---|---|
name |
text |
|
asset_type |
enum |
fixed_asset, inventory, product, license, resource, intangible |
serial_number |
text|null |
|
acquisition_date |
date|null |
|
acquisition_cost |
numeric(18,4)|null |
|
currency_code |
char(3)|null |
|
owner_org_id |
UUID|null |
→ Organization.id |
location_id |
UUID|null |
→ Location.id |
status |
enum |
active, disposed, under_maintenance, transferred |
depreciation_policy |
jsonb|null |
Avskrivningsregler (Ekonomi) |
Relationer:
- Asset → Organization (owner)
- Asset → Location (plats)
- Asset → Transaction (inköp, avskrivning)
- Asset → Contract (leasing, garanti)
Ekonomimodulen-mappning:
fixed_assets tabell är en direkt implementation av Asset med utökad depreciation_policy.
Objekt 5 — Location
Adress, arbetsplats, lagerplats, land, leveransadress.
| Fält | Typ | Beskrivning |
|---|---|---|
name |
text|null |
T.ex. "Huvudkontor Stockholm" |
location_type |
enum |
address, workplace, warehouse, country, virtual |
street |
text|null |
|
city |
text|null |
|
postal_code |
text|null |
|
country_code |
char(2) |
ISO 3166-1 |
region |
text|null |
Stat/landsdel |
coordinates |
point|null |
PostGIS POINT(lon lat) |
parent_id |
UUID|null |
Hierarki (land → stad → adress) |
Relationer:
- Location → Organization (many)
- Location → Person (hemadress, arbetsplats)
- Location → Asset (plats)
Objekt 6 — Project
Projekt, uppdrag, kostnadsbärare, kampanj, budgetpost.
| Fält | Typ | Beskrivning |
|---|---|---|
name |
text |
|
code |
text|null |
Projektnamn/nummer för referens |
project_type |
enum |
internal, client, rnd, infrastructure, overhead |
status |
enum |
planning, active, on_hold, completed, cancelled |
owner_id |
UUID |
→ Person.id |
org_id |
UUID |
→ Organization.id |
budget |
numeric(18,4)|null |
|
currency_code |
char(3)|null |
|
start_date |
date|null |
|
end_date |
date|null |
|
parent_project_id |
UUID|null |
Delprojekt-hierarki |
Relationer:
- Project → Organization (ägande)
- Project → Person (projektledare, team)
- Project → Task (nedbrytning)
- Project → Transaction (kostnader, intäkter)
- Project → Contract (kontrakt kopplade till projekt)
Ekonomimodulen-mappning:
cost_centers.project_id → Project.id
budget_lines.project_id → Project.id
Objekt 7 — Task
Arbetsuppgift, aktivitet, ärende, batchjobb, processuppgift.
| Fält | Typ | Beskrivning |
|---|---|---|
title |
text |
|
description |
text|null |
|
task_type |
enum |
manual, automated, approval, review, notification |
status |
enum |
pending, in_progress, blocked, completed, cancelled |
priority |
enum |
low, medium, high, critical |
assignee_id |
UUID|null |
→ Person.id |
project_id |
UUID|null |
→ Project.id |
parent_task_id |
UUID|null |
Subtask-hierarki |
due_date |
timestamptz|null |
|
completed_at |
timestamptz|null |
|
workflow_instance_id |
UUID|null |
→ Workflow.id |
Relationer:
- Task → Person (assignee, creator)
- Task → Project (tillhörighet)
- Task → Workflow (processdrivet)
- Task → Document (bilagor)
Objekt 8 — Workflow
Processinstans. Kopplas till Workflow Engine (framtida).
| Fält | Typ | Beskrivning |
|---|---|---|
workflow_definition_id |
text |
Referens till process-definition |
workflow_version |
text |
Versionerad processdefinition |
status |
enum |
pending, running, waiting, completed, failed, cancelled |
context |
jsonb |
Runtime-kontext för processinstansen |
started_at |
timestamptz|null |
|
completed_at |
timestamptz|null |
|
initiator_id |
UUID |
→ Person.id eller system |
current_step |
text|null |
Stegidentifierare |
Relationer:
- Workflow → Task (genererade uppgifter)
- Workflow → Document (processade dokument)
- Workflow → Transaction (utlösta transaktioner)
Ekonomimodulen-mappning:
Fakturaflöde (inkommen faktura → godkännande → betalning) är en Workflow-instans.
Objekt 9 — Document
Faktura, kvitto, kontrakt, rapport, fil, attest.
| Fält | Typ | Beskrivning |
|---|---|---|
title |
text |
|
document_type |
enum |
invoice, receipt, contract, report, attachment, specification, certificate |
mime_type |
text|null |
application/pdf, image/jpeg, etc. |
storage_key |
text|null |
S3/blob-sökväg |
checksum |
text|null |
SHA-256 av filinnehållet |
size_bytes |
bigint|null |
|
source_system |
text|null |
Ursprungssystem |
related_entity_id |
UUID|null |
Polymorf referens |
related_entity_type |
text|null |
T.ex. Transaction, Contract |
classification |
enum |
public, internal, confidential, restricted (se ARC-003) |
extracted_data |
jsonb|null |
AI-extraherade fält (se ARC-002) |
Relationer:
- Document → Transaction (underlag)
- Document → Contract (kontraktsdokument)
- Document → Workflow (processunderlag)
Ekonomimodulen-mappning:
supplier_invoices.document_id → Document.id
expense_reports.receipt_document_ids → Document.id[]
Objekt 10 — Transaction
Ekonomisk händelse.
JournalEntryär en Transaction medtransaction_type = journal_entry.
| Fält | Typ | Beskrivning |
|---|---|---|
transaction_type |
enum |
journal_entry, payment, invoice, credit_note, accrual, depreciation, transfer |
amount |
numeric(18,4) |
Absolutbelopp |
currency_code |
char(3) |
ISO 4217 |
base_currency_amount |
numeric(18,4)|null |
Omräknat till baskvaluta |
exchange_rate |
numeric(12,8)|null |
|
status |
enum |
draft, posted, voided, pending_approval |
transaction_date |
date |
Transaktionsdatum (affärsdatum) |
value_date |
date|null |
Valuteringsdatum |
reference |
text|null |
Externt referensnummer |
counterparty_id |
UUID|null |
→ Organization.id eller Person.id |
project_id |
UUID|null |
→ Project.id |
document_id |
UUID|null |
→ Document.id (underlag) |
lines |
jsonb |
Transaktionsrader med konton och belopp |
Relationer:
- Transaction → Organization (motpart)
- Transaction → Person (utförare)
- Transaction → Document (verifikat)
- Transaction → Project (kostnadsbärare)
- Transaction → Asset (anläggningstillgångsrörelser)
Ekonomimodulen-mappning:
journal_entries är Transactions med transaction_type = journal_entry.
Ledger Engine håller transaktionsrader i lines-jsonb eller separata rader beroende på volym.
Objekt 11 — Event
Systemevent, domänevent. Detta är Hermes Event Fabric-payloaden.
| Fält | Typ | Beskrivning |
|---|---|---|
event_type |
text |
Namnrymd: aamos.ekonomi.transaction.posted |
event_version |
text |
Semver av event-schemat |
source_service |
text |
Originerande tjänst |
correlation_id |
UUID |
Spårnings-ID genom systemet |
causation_id |
UUID|null |
ID på det event som orsakade detta |
subject_type |
text |
Typ av ämnesobjekt |
subject_id |
UUID |
ID på ämnesobjektet |
payload |
jsonb |
Eventnyttolast |
published_at |
timestamptz |
Publiceringstidpunkt |
Namnrymdskonvention:
aamos.<modul>.<objekt>.<verb> — t.ex. aamos.ekonomi.transaction.posted, aamos.crm.contact.created
Relationer:
- Event refererar till vilket som helst kanoniskt objekt via
subject_id - Events persisteras i JSONL-fallback för replay
Objekt 12 — Contract
Avtal, SLA, leverantörsavtal, anställningsavtal, ramavtal, licens.
| Fält | Typ | Beskrivning |
|---|---|---|
title |
text |
|
contract_type |
enum |
supplier, customer, employment, sla, license, framework, nda |
status |
enum |
draft, pending_signature, active, expired, terminated, renewed |
counterparty_org_id |
UUID|null |
→ Organization.id |
counterparty_person_id |
UUID|null |
→ Person.id |
owner_org_id |
UUID |
→ Organization.id (intern part) |
start_date |
date|null |
|
end_date |
date|null |
|
auto_renewal |
boolean |
|
value |
numeric(18,4)|null |
Kontraktsvärde |
currency_code |
char(3)|null |
|
document_id |
UUID|null |
→ Document.id (signerat PDF) |
terms |
jsonb|null |
Strukturerade avtalsvillkor |
Relationer:
- Contract → Organization (parter)
- Contract → Person (signatärer)
- Contract → Document (kontraktsdokument)
- Contract → Transaction (betalningsplan, fakturor)
- Contract → Asset (leasade tillgångar)
Ekonomimodulens fullständiga mappning
Ekonomimodul → Kanonisk typ
──────────────────────────────────────────
JournalEntry → Transaction (type=journal_entry)
Supplier → Organization (org_type=company)
Customer → Organization (org_type=company)
SupplierInvoice → Document (type=invoice) + Transaction
ExpenseReport → Document (type=report) + Transaction[]
FixedAsset → Asset (type=fixed_asset)
CostCenter → Project (type=overhead/internal)
Employee → Person (type=employee)
ApprovalFlow → Workflow
Receipt → Document (type=receipt)
SupplierContract → Contract (type=supplier)
Konsekvenser
Positiva
- Ny modul definierar bara sina egna extensions i
metadata, inte nya bastyper - AI-kontextfönster kan laddas med enhetliga objektrepresentationer
- Korsmodulär sökning och audit fungerar utan adaptrar
- Event-routing är självbeskrivande via
subject_type
Negativa / risker
- Migrationsarbete: Ekonomimodulen måste verifiera att befintliga tabeller uppfyller kontraktet
- UUID v7 kräver att databasen kan generera dem (
pgcryptoeller app-sida) metadata jsonbkan bli en dumpingsplats — kräver disciplin och review
Implementation
Fas 1 (Ekonomimodulen, nu)
- Verifiera att alla tabeller har
tenant_id,version,deleted_at - Lägg till
metadata jsonb DEFAULT '{}'på tabeller som saknar det - Dokumentera mappning i modulens README
Fas 2 (inför CRM)
- Skapa dedikerade tabeller för Organization och Person i
core-schema - Ekonomimodulen FK:ar till
core.organizationsochcore.persons
Fas 3 (plattformsnivå)
- GraphQL/REST-lager för kanoniska objekt
- Unified search-index över alla objekt
- AI-kontext-builder som hämtar kanonisk representation
Öppna frågor
-
Ska kanoniska objekt bo i ett delat
core-schema eller replikeras per modul?
Förslag: Shared schema för Person, Organization, Location. Replika med CDC för moduler som behöver hög läsprestanda. -
UUID v7 vs ULID?
UUID v7 är nu RFC-standard och stöds bättre i PostgreSQL-ekosystemet. ULID är alternativ om befintlig kod är svår att migrera. -
metadata-schemavalidering?
Modul-specifika JSON Schema-fragment bör definieras och valideras vid INSERT/UPDATE. -
Event-schema-registret?
Hermes bör ha ett schema-register som validerar event-payloads motevent_version. Se ARC-002.