2024-11-15
Metadata-First Manifesto: Zašto Kod Nije Više Source of Truth
Problem: Proveli ste 6 meseci razvijajući ERP modul za nabavku. Klijent sada traži da dodamo dodatno approval step-a. Procena? 2 nedelje development + 1 nedelja testiranja. Zašto?
Zato što ste hardkodovali workflow logiku u TypeScript/Java kodu. Svaka izmena zahteva:
- Pronalaženje svih relevantnih fajlova (controllers, services, database migrations)
- Izmena logike na više mesta
- Dodavanje novih database kolona
- Ponovno testiranje celog modula
- Deployment sa downtime
Šta Je Metadata-First Arhitektura?
Umesto da pišete kod koji implementira business logic, vi pišete YAML deklaracije koje opisuju business logic. Platforma onda automatski generiše:
- Database schema (PostgreSQL tabele, indeksi, foreign keys)
- TypeScript tipove (interfejsi, validacije, enumi)
- REST API endpoints (CRUD operacije, filteri, sortiranje)
- Workflow state machine (approval steps, notifikacije, transitions)
- UI forme (input fields, validacije, layout)
Primer: Tradicionalni Pristup
TypeScript kod za Purchase Order workflow (200+ linija):
// models/purchase-order.ts
export interface PurchaseOrder {
id: string;
status: 'draft' | 'pending_approval' | 'approved' | 'rejected';
requestedBy: string;
approvedBy?: string;
items: PurchaseOrderItem[];
totalAmount: number;
createdAt: Date;
updatedAt: Date;
}
// services/purchase-order.service.ts
export class PurchaseOrderService {
async create(data: CreatePurchaseOrderDto) {
// Validate items
if (!data.items || data.items.length === 0) {
throw new Error('At least one item required');
}
// Calculate total
const total = data.items.reduce((sum, item) =>
sum + (item.quantity * item.unitPrice), 0
);
// Create in database
const po = await this.db.purchaseOrders.create({
...data,
status: 'draft',
totalAmount: total,
});
return po;
}
async submitForApproval(id: string, userId: string) {
const po = await this.findOne(id);
if (po.status !== 'draft') {
throw new Error('Can only submit draft orders');
}
if (po.requestedBy !== userId) {
throw new Error('Only requester can submit');
}
await this.db.purchaseOrders.update(id, {
status: 'pending_approval'
});
// Send notification to approver
await this.notificationService.notify(
po.approvedBy,
'New purchase order awaiting approval'
);
}
async approve(id: string, userId: string) {
// ... još 50 linija logike
}
async reject(id: string, userId: string, reason: string) {
// ... još 30 linija logike
}
}
// controllers/purchase-order.controller.ts
@Controller('purchase-orders')
export class PurchaseOrderController {
@Post()
async create(@Body() data: CreatePurchaseOrderDto) {
return this.service.create(data);
}
@Post(':id/submit')
async submit(@Param('id') id: string, @CurrentUser() user: User) {
return this.service.submitForApproval(id, user.id);
}
// ... još 10+ endpoint-a
}
Database migration (SQL):
CREATE TABLE purchase_orders (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
status VARCHAR(50) NOT NULL CHECK (status IN ('draft', 'pending_approval', 'approved', 'rejected')),
requested_by UUID NOT NULL REFERENCES users(id),
approved_by UUID REFERENCES users(id),
total_amount DECIMAL(15,2) NOT NULL,
created_at TIMESTAMP DEFAULT NOW(),
updated_at TIMESTAMP DEFAULT NOW()
);
CREATE TABLE purchase_order_items (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
purchase_order_id UUID NOT NULL REFERENCES purchase_orders(id) ON DELETE CASCADE,
product_id UUID NOT NULL REFERENCES products(id),
quantity INTEGER NOT NULL CHECK (quantity > 0),
unit_price DECIMAL(10,2) NOT NULL,
created_at TIMESTAMP DEFAULT NOW()
);
CREATE INDEX idx_po_status ON purchase_orders(status);
CREATE INDEX idx_po_requested_by ON purchase_orders(requested_by);
Ukupno: ~400 linija koda rasutog u 5+ fajlova.
Isti Funkcionalnost: PMFA Metadata Pristup
Jedan YAML fajl (60 linija):
# metadata/entities/purchase-order.yml
entity: PurchaseOrder
table: purchase_orders
description: "Nabavne narudžbenice sa approval workflow-om"
fields:
- name: id
type: uuid
primary: true
generated: auto
- name: status
type: enum
values: [draft, pending_approval, approved, rejected]
default: draft
indexed: true
- name: requestedBy
type: relation
target: User
required: true
indexed: true
- name: approvedBy
type: relation
target: User
required: false
- name: items
type: relation
target: PurchaseOrderItem
relation: one-to-many
cascade: delete
- name: totalAmount
type: decimal
precision: 15
scale: 2
computed: "SUM(items.quantity * items.unitPrice)"
- name: createdAt
type: timestamp
generated: auto
- name: updatedAt
type: timestamp
generated: auto-update
workflow:
states:
- name: draft
transitions: [submit]
permissions: [requester]
- name: pending_approval
transitions: [approve, reject]
permissions: [approver]
notifications:
on_enter:
- target: approvedBy
message: "New purchase order awaiting approval"
- name: approved
transitions: []
final: true
- name: rejected
transitions: [resubmit]
final: false
transitions:
- name: submit
from: draft
to: pending_approval
condition: "items.length > 0"
- name: approve
from: pending_approval
to: approved
- name: reject
from: pending_approval
to: rejected
requires_comment: true
validations:
- field: items
rule: min_length
value: 1
message: "Najmanje jedna stavka je obavezna"
- field: totalAmount
rule: min_value
value: 0
message: "Ukupan iznos mora biti pozitivan"
permissions:
create: [authenticated]
read: [requester, approver, admin]
update: [requester:draft, admin]
delete: [admin]
Rezultat?
PMFA automatski generiše:
✅ PostgreSQL tabele sa svim indeksima i foreign key-ovima
✅ TypeScript interfejse i enume
✅ REST API sa /purchase-orders endpoint-ima:
POST /purchase-orders- kreiranjeGET /purchase-orders- listing sa filterimaGET /purchase-orders/:id- detaljiPOST /purchase-orders/:id/submit- submit za approvalPOST /purchase-orders/:id/approve- odobriPOST /purchase-orders/:id/reject- odbij
✅ Workflow state machine sa validacijama
✅ Notifikacioni sistem
✅ Permission checks
✅ Validation rules
Ukupno: 60 linija YAML-a umesto 400+ linija koda.
Šta Kada Treba Izmena?
Scenario: Klijent traži DODATNI approval step
Tradicionalni pristup:
- 2 nedelje development (izmena 10+ fajlova)
- 1 nedelja testiranja
- Rizik od breaking existing funkcionalnosti
PMFA pristup:
Dodamo 5 linija u workflow sekciju:
workflow:
states:
# ... postojeći states ...
- name: pending_manager_approval # NOVO
transitions: [manager_approve, manager_reject]
permissions: [manager]
notifications:
on_enter:
- target: manager
message: "Purchase order requires your approval"
transitions:
- name: submit
from: draft
to: pending_manager_approval # Izmenjeno: bilo 'pending_approval'
- name: manager_approve # NOVO
from: pending_manager_approval
to: pending_approval
Rezultat: Izmena instant deployment (5 minuta). Sve ostalo se automatski regeneriše.
Prednosti Metadata-First Pristupa
1. 10x Brže Izmene
Tradicionalni ERP:
- Dodavanje novog polja: 2-3 dana (migration, backend, frontend, testiranje)
- Izmena workflow-a: 1-2 nedelje
- Novi modul: 3-6 meseci
PMFA:
- Dodavanje novog polja: 5 minuta (jedna linija YAML-a)
- Izmena workflow-a: 10 minuta
- Novi modul: 1-2 nedelje (samo YAML definicije)
2. Zero Tehnički Dug
Kod koji ne postoji ne može biti legacy.
- Nema "tech debt refactoring" backloga
- Nema breaking changes u API-ju
- Nema deprecated funkcija koje treba zamenjivati
3. Perfect Documentation
YAML metadata JE dokumentacija. Nema "docs out of sync" problema.
# Ovo je i kod i dokumentacija istovremeno
field: approvalLimit
type: decimal
description: "Maksimalni iznos koji approver može odobriti bez eskalacije"
default: 10000
validation:
min: 0
max: 1000000
4. Tenant-By-Architecture Multi-Tenancy
Svaki tenant može imati svoje custom polje bez izmene shared database schema:
# Tenant A: Prodavnica elektronike
customFields:
- name: warrantyPeriod
type: integer
unit: months
# Tenant B: Auto servis
customFields:
- name: vehicleVIN
type: string
format: alphanumeric
PMFA dinamički generiše storage strategy (JSONB kolona ili separate schema).
Kada NIJE Metadata-First?
Ovaj pristup NIJE za:
❌ Unique business logic koji se ne može generalizovati (AI algoritmi, complex financial calculations)
❌ Performance-critical inner loops (real-time trading systems)
❌ Custom UI/UX requirements (marketing websites, consumer apps)
Metadata-first JE za:
✅ CRUD operacije (90% enterprise sistema)
✅ Workflow-based procese (approvals, state machines)
✅ Forme, validacije, permissions
✅ Reporting i dashboards
✅ Multi-tenant SaaS platforme
Zaključak
Kod je imperativ. Metadata je deklaracija.
Imperativni kod kaže platformi KAKO da uradi nešto. Deklarativna metadata kaže platformi ŠTA da uradi.
PMFA filozofija: Business logic pripada metadata deklaracijama, ne TypeScript fajlovima.
Rezultat?
- 10x brži development
- Zero tehnički dug
- Instant izmene bez deployment-a
- Perfect documentation
Metadata-first je budućnost enterprise software-a.
Zainteresovani za PMFA? Kontaktirajte nas na office@nonnotech.com za early access program.